summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-03-21 01:00:00 +0300
committerDmitry Stogov <dmitry@zend.com>2015-03-21 01:00:00 +0300
commit430266edfa93e04cf5199d289050ba14866645b3 (patch)
tree8417c4f5c30fb6da767376d3f6aa568cd3d7ad88 /Zend/zend_compile.c
parenta43a9c91374b3cd9864eea5e7c0b0e2e9dafa2e2 (diff)
parent1f408af03cf5dcfbcabebb20f162d2ed787b5579 (diff)
downloadphp-git-430266edfa93e04cf5199d289050ba14866645b3.tar.gz
Merge branch 'scalar_type_hints_v5'
* scalar_type_hints_v5: (65 commits) Fixed in-place modification of IS_CONST operand Changed SKIPIF messages ZPP changed to lazely check for "strict/weak" only if it's really necessary. Cleanup. cleanup Fixed return type hint handling for constants Fixed tests Imroved ZPP rules (condititins reoredered to prevent duplicate checks) Fixed comments Fixed error messages Improved type hinting: Fixed white spaces Add check for maintaining reference all the way through both type and return values Reduce the number of times that the zval needs to be separated in return type checking to those that are necessary Add test to ensure namespaced code can't use scalar types as class names Disallow relative namespace type declarations Add support and tests for null constant default values. Refactor complex conditionals into an extracted function for clarity and code-reuse Refactor as to not use call info, but add the flag to the op_array. Fix severity issues with callbacks, start work porting ZEND_STRLEN opcode to work with strict mode, more refactoring to come Fix C89 compatibility by moving a misplaced if statement Refactor gotos into more elaborate ifs to eliminate goto failure ...
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c200
1 files changed, 159 insertions, 41 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index b7c25eea8d..64d96e452d 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -129,6 +129,72 @@ static zend_bool zend_get_unqualified_name(const zend_string *name, const char *
}
/* }}} */
+typedef struct _scalar_typehint_info {
+ const char* name;
+ const size_t name_len;
+ const zend_uchar type;
+} scalar_typehint_info;
+
+static const scalar_typehint_info scalar_typehints[] = {
+ {"int", sizeof("int") - 1, IS_LONG},
+ {"float", sizeof("float") - 1, IS_DOUBLE},
+ {"string", sizeof("string") - 1, IS_STRING},
+ {"bool", sizeof("bool") - 1, _IS_BOOL},
+ {NULL, 0, IS_UNDEF}
+};
+
+static zend_always_inline const scalar_typehint_info* zend_find_scalar_typehint(const zend_string *const_name) /* {{{ */
+{
+ const scalar_typehint_info *info = &scalar_typehints[0];
+ const char *uqname;
+ size_t uqname_len;
+
+ if (!zend_get_unqualified_name(const_name, &uqname, &uqname_len)) {
+ uqname = const_name->val;
+ uqname_len = const_name->len;
+ }
+
+ while (info->name) {
+ if (uqname_len == info->name_len && zend_binary_strcasecmp(uqname, uqname_len, info->name, info->name_len) == 0) {
+ break;
+ }
+ info++;
+ }
+
+ if (info->name) {
+ return info;
+ } else {
+ return NULL;
+ }
+}
+/* }}} */
+
+ZEND_API void zend_assert_valid_class_name(const zend_string *const_name) /* {{{ */
+{
+ const scalar_typehint_info *info = zend_find_scalar_typehint(const_name);
+
+ if (info) {
+ zend_error_noreturn(E_COMPILE_ERROR, "\"%s\" cannot be used as a class name", info->name);
+ }
+}
+/* }}} */
+
+static zend_always_inline zend_uchar zend_lookup_scalar_typehint_by_name(const zend_string *const_name) /* {{{ */
+{
+ const scalar_typehint_info *info = zend_find_scalar_typehint(const_name);
+
+ if (info) {
+ if (const_name->len != info->name_len) {
+ zend_error_noreturn(E_COMPILE_ERROR, "\"%s\" cannot be used as a type declaration", const_name->val);
+ }
+ return info->type;
+ } else {
+ return 0;
+ }
+}
+/* }}} */
+
+
static void init_compiler_declarables(void) /* {{{ */
{
ZVAL_LONG(&CG(declarables).ticks, 0);
@@ -1864,7 +1930,11 @@ 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);
+ zend_op *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));
+ }
}
}
/* }}} */
@@ -3334,11 +3404,9 @@ 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);
- }
+ /* 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);
}
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL);
@@ -3908,6 +3976,28 @@ void zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
}
/* }}} */
+static int zend_declare_is_first_statement(zend_ast *ast) /* {{{ */
+{
+ uint32_t i = 0;
+ zend_ast_list *file_ast = zend_ast_get_list(CG(ast));
+
+ /* Check to see if this declare is preceeded only by declare statements */
+ while (i < file_ast->children) {
+ if (file_ast->child[i] == ast) {
+ return SUCCESS;
+ } else if (file_ast->child[i] == NULL) {
+ /* Empty statements are not allowed prior to a declare */
+ return FAILURE;
+ } else if (file_ast->child[i]->kind != ZEND_AST_DECLARE) {
+ /* declares can only be preceeded by other declares */
+ return FAILURE;
+ }
+ i++;
+ }
+ return FAILURE;
+}
+/* }}} */
+
void zend_compile_declare(zend_ast *ast) /* {{{ */
{
zend_ast_list *declares = zend_ast_get_list(ast->child[0]);
@@ -3928,29 +4018,34 @@ void zend_compile_declare(zend_ast *ast) /* {{{ */
ZVAL_COPY_VALUE(&CG(declarables).ticks, &value_zv);
zval_dtor(&value_zv);
} else if (zend_string_equals_literal_ci(name, "encoding")) {
- uint32_t i = 0;
- zend_bool valid = 0;
- /* Encoding declaration was already handled during parsing. Here we
- * only check that it is the first statement in the file. */
- zend_ast_list *file_ast = zend_ast_get_list(CG(ast));
-
- /* Check to see if this declare is preceeded only by declare statements */
- while (valid == 0 && i < file_ast->children) {
- if (file_ast->child[i] == ast) {
- valid = 1;
- } else if (file_ast->child[i] == NULL) {
- /* Empty statements are not allowed prior to a declare */
- break;
- } else if (file_ast->child[i]->kind != ZEND_AST_DECLARE) {
- /* declares can only be preceeded by other declares */
- break;
- }
- i++;
- }
- if (valid != 1) {
+
+ if (FAILURE == zend_declare_is_first_statement(ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Encoding declaration pragma must be "
"the very first statement in the script");
}
+ } else if (zend_string_equals_literal_ci(name, "strict_types")) {
+ zval value_zv;
+
+ if (FAILURE == zend_declare_is_first_statement(ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must be "
+ "the very first statement in the script");
+ }
+
+ if (ast->child[1] != NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must not "
+ "use block mode");
+ }
+
+ zend_const_expr_to_zval(&value_zv, value_ast);
+
+ if (Z_TYPE(value_zv) != IS_LONG || (Z_LVAL(value_zv) != 0 && Z_LVAL(value_zv) != 1)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must have 0 or 1 as its value");
+ }
+
+ if (Z_LVAL(value_zv) == 1) {
+ CG(active_op_array)->fn_flags |= ZEND_ACC_STRICT_TYPES;
+ }
+
} else {
zend_error(E_COMPILE_WARNING, "Unsupported declare '%s'", name->val);
}
@@ -3995,19 +4090,24 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
arg_infos->type_hint = return_type_ast->attr;
} else {
zend_string *class_name = zend_ast_get_str(return_type_ast);
+ zend_uchar type = zend_lookup_scalar_typehint_by_name(class_name);
- if (zend_is_const_default_class_ref(return_type_ast)) {
- class_name = zend_resolve_class_name_ast(return_type_ast);
+ if (type != 0) {
+ arg_infos->type_hint = type;
} 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;
+ 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->type_hint = IS_OBJECT;
+ arg_infos->class_name = class_name;
+ }
}
arg_infos++;
@@ -4117,19 +4217,30 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
}
} else {
zend_string *class_name = zend_ast_get_str(type_ast);
+ zend_uchar type;
- if (zend_is_const_default_class_ref(type_ast)) {
- class_name = zend_resolve_class_name_ast(type_ast);
+ type = zend_lookup_scalar_typehint_by_name(class_name);
+ if (type != 0) {
+ arg_info->type_hint = type;
} else {
- zend_string_addref(class_name);
- }
- arg_info->type_hint = IS_OBJECT;
- arg_info->class_name = class_name;
+ if (zend_is_const_default_class_ref(type_ast)) {
+ class_name = zend_resolve_class_name_ast(type_ast);
+ } else {
+ zend_string_addref(class_name);
+ }
+ arg_info->type_hint = IS_OBJECT;
+ arg_info->class_name = class_name;
+ }
if (default_ast && !has_null_default && !Z_CONSTANT(default_node.u.constant)) {
+ if (arg_info->class_name) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with a class type hint can only be NULL");
+ } else if (!ZEND_SAME_FAKE_TYPE(arg_info->type_hint, Z_TYPE(default_node.u.constant))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
+ "with a %s type hint can only be %s or NULL", class_name->val, class_name->val);
+ }
}
}
}
@@ -4409,6 +4520,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE);
+ op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES);
op_array->fn_flags |= decl->flags;
op_array->line_start = decl->start_lineno;
op_array->line_end = decl->end_lineno;
@@ -4743,6 +4855,8 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
import_name = zend_hash_find_ptr(CG(current_import), lcname);
}
+ zend_assert_valid_class_name(name);
+
if (CG(current_namespace)) {
name = zend_prefix_with_ns(name);
@@ -4978,6 +5092,10 @@ void zend_compile_use(zend_ast *ast) /* {{{ */
}
}
+ if (type == T_CLASS) {
+ zend_assert_valid_class_name(new_name);
+ }
+
if (case_sensitive) {
lookup_name = zend_string_copy(new_name);
} else {