diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 2133 |
1 files changed, 278 insertions, 1855 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a5a32298d5..91bfbfeebb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -30,6 +30,7 @@ #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" +#include "zend_inheritance.h" #define CONSTANT_EX(op_array, op) \ (op_array)->literals[op] @@ -76,29 +77,6 @@ ZEND_API zend_compiler_globals compiler_globals; ZEND_API zend_executor_globals executor_globals; #endif -static zend_property_info *zend_duplicate_property_info(zend_property_info *property_info TSRMLS_DC) /* {{{ */ -{ - zend_property_info* new_property_info; - - new_property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info)); - memcpy(new_property_info, property_info, sizeof(zend_property_info)); - zend_string_addref(new_property_info->name); - if (new_property_info->doc_comment) { - zend_string_addref(new_property_info->doc_comment); - } - return new_property_info; -} -/* }}} */ - -static zend_property_info *zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */ -{ - zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1); - memcpy(new_property_info, property_info, sizeof(zend_property_info)); - zend_string_addref(new_property_info->name); - return new_property_info; -} -/* }}} */ - static void zend_destroy_property_info(zval *zv) /* {{{ */ { zend_property_info *property_info = Z_PTR_P(zv); @@ -137,21 +115,33 @@ static zend_string *zend_build_runtime_definition_key(zend_string *name, unsigne { zend_string *result; char char_pos_buf[32]; - size_t char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); + size_t filename_len, char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); const char *filename; if (CG(active_op_array)->filename) { filename = CG(active_op_array)->filename->val; + filename_len = CG(active_op_array)->filename->len; } else { filename = "-"; + filename_len = sizeof("-") - 1; } - /* NULL, name length, filename length, last accepting char position length */ - result = zend_string_alloc(1 + name->len + strlen(filename) + char_pos_len, 0); + result = zend_string_alloc(1 + name->len + filename_len + char_pos_len, 0); + sprintf(result->val, "%c%s%s%s", '\0', name->val, filename, char_pos_buf); + return zend_new_interned_string(result TSRMLS_CC); +} +/* }}} */ - result->val[0] = '\0'; - sprintf(result->val + 1, "%s%s%s", name->val, filename, char_pos_buf); - return result; +static zend_bool zend_get_unqualified_name(const zend_string *name, const char **result, size_t *result_len) /* {{{ */ +{ + const char *ns_separator = zend_memrchr(name->val, '\\', name->len); + if (ns_separator != NULL) { + *result = ns_separator + 1; + *result_len = name->val + name->len - *result; + return 1; + } + + return 0; } /* }}} */ @@ -342,82 +332,63 @@ int zend_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC) /* {{{ */ } /* }}} */ -static int zend_add_func_name_literal(zend_op_array *op_array, zval *zv TSRMLS_DC) /* {{{ */ +static inline int zend_add_literal_string(zend_op_array *op_array, zend_string **str TSRMLS_DC) /* {{{ */ { int ret; - zend_string *lc_name; - zval c; - - if (op_array->last_literal > 0 && - &op_array->literals[op_array->last_literal - 1] == zv && - Z_CACHE_SLOT(op_array->literals[op_array->last_literal - 1]) == -1) { - /* we already have function name as last literal (do nothing) */ - ret = op_array->last_literal - 1; - } else { - ret = zend_add_literal(op_array, zv TSRMLS_CC); - } + zval zv; + ZVAL_STR(&zv, *str); + ret = zend_add_literal(op_array, &zv TSRMLS_CC); + *str = Z_STR(zv); + return ret; +} - lc_name = zend_string_alloc(Z_STRLEN_P(zv), 0); - zend_str_tolower_copy(lc_name->val, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); - ZVAL_NEW_STR(&c, lc_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); +static int zend_add_func_name_literal(zend_op_array *op_array, zend_string *name TSRMLS_DC) /* {{{ */ +{ + /* Original name */ + int ret = zend_add_literal_string(op_array, &name TSRMLS_CC); + + /* Lowercased name */ + zend_string *lc_name = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(lc_name->val, name->val, name->len); + zend_add_literal_string(op_array, &lc_name TSRMLS_CC); return ret; } /* }}} */ -static int zend_add_ns_func_name_literal(zend_op_array *op_array, zval *zv TSRMLS_DC) /* {{{ */ +static int zend_add_ns_func_name_literal(zend_op_array *op_array, zend_string *name TSRMLS_DC) /* {{{ */ { - int ret; - zend_string *lc_name; - const char *ns_separator; - int lc_len; - zval c; - - if (op_array->last_literal > 0 && - &op_array->literals[op_array->last_literal - 1] == zv && - Z_CACHE_SLOT(op_array->literals[op_array->last_literal - 1]) == -1) { - /* we already have function name as last literal (do nothing) */ - ret = op_array->last_literal - 1; - } else { - ret = zend_add_literal(op_array, zv TSRMLS_CC); - } + const char *unqualified_name; + size_t unqualified_name_len; - lc_name = zend_string_alloc(Z_STRLEN_P(zv), 0); - zend_str_tolower_copy(lc_name->val, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); - ZVAL_NEW_STR(&c, lc_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + /* Original name */ + int ret = zend_add_literal_string(op_array, &name TSRMLS_CC); - ns_separator = (const char*)zend_memrchr(Z_STRVAL_P(zv), '\\', Z_STRLEN_P(zv)); + /* Lowercased name */ + zend_string *lc_name = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(lc_name->val, name->val, name->len); + zend_add_literal_string(op_array, &lc_name TSRMLS_CC); - if (ns_separator != NULL) { - ns_separator += 1; - lc_len = Z_STRLEN_P(zv) - (ns_separator - Z_STRVAL_P(zv)); - lc_name = zend_string_alloc(lc_len, 0); - zend_str_tolower_copy(lc_name->val, ns_separator, lc_len); - ZVAL_NEW_STR(&c, lc_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + /* Lowercased unqualfied name */ + if (zend_get_unqualified_name(name, &unqualified_name, &unqualified_name_len)) { + lc_name = zend_string_alloc(unqualified_name_len, 0); + zend_str_tolower_copy(lc_name->val, unqualified_name, unqualified_name_len); + zend_add_literal_string(op_array, &lc_name TSRMLS_CC); } return ret; } /* }}} */ -static int zend_add_class_name_literal(zend_op_array *op_array, zend_string *name TSRMLS_DC) /* {{{ */ { - int ret; - zend_string *lc_name; - - zval zv; - ZVAL_STR(&zv, name); - - ret = zend_add_literal(op_array, &zv TSRMLS_CC); - name = Z_STR(zv); /* Load new name string in case it was interned */ +static int zend_add_class_name_literal(zend_op_array *op_array, zend_string *name TSRMLS_DC) /* {{{ */ +{ + /* Original name */ + int ret = zend_add_literal_string(op_array, &name TSRMLS_CC); - lc_name = zend_string_alloc(name->len, 0); + /* Lowercased name */ + zend_string *lc_name = zend_string_alloc(name->len, 0); zend_str_tolower_copy(lc_name->val, name->val, name->len); - - ZVAL_NEW_STR(&zv, lc_name); - zend_add_literal(CG(active_op_array), &zv TSRMLS_CC); + zend_add_literal_string(op_array, &lc_name TSRMLS_CC); zend_alloc_cache_slot(ret TSRMLS_CC); @@ -425,72 +396,44 @@ static int zend_add_class_name_literal(zend_op_array *op_array, zend_string *nam } /* }}} */ -static int zend_add_const_name_literal(zend_op_array *op_array, zval *zv, int unqualified TSRMLS_DC) /* {{{ */ +static int zend_add_const_name_literal(zend_op_array *op_array, zend_string *name, zend_bool unqualified TSRMLS_DC) /* {{{ */ { - int ret; - char *name; zend_string *tmp_name; - const char *ns_separator; - int name_len, ns_len; - zval c; - - if (op_array->last_literal > 0 && - &op_array->literals[op_array->last_literal - 1] == zv && - Z_CACHE_SLOT(op_array->literals[op_array->last_literal - 1]) == -1) { - /* we already have function name as last literal (do nothing) */ - ret = op_array->last_literal - 1; - } else { - ret = zend_add_literal(op_array, zv TSRMLS_CC); - } - /* skip leading '\\' */ - if (Z_STRVAL_P(zv)[0] == '\\') { - name_len = Z_STRLEN_P(zv) - 1; - name = Z_STRVAL_P(zv) + 1; - } else { - name_len = Z_STRLEN_P(zv); - name = Z_STRVAL_P(zv); - } - ns_separator = zend_memrchr(name, '\\', name_len); - if (ns_separator) { - ns_len = ns_separator - name; - } else { - ns_len = 0; - } + int ret = zend_add_literal_string(op_array, &name TSRMLS_CC); + + size_t ns_len = 0, after_ns_len = name->len; + const char *after_ns = zend_memrchr(name->val, '\\', name->len); + if (after_ns) { + after_ns += 1; + ns_len = after_ns - name->val - 1; + after_ns_len = name->len - ns_len - 1; - if (ns_len) { /* lowercased namespace name & original constant name */ - tmp_name = zend_string_init(name, name_len, 0); + tmp_name = zend_string_init(name->val, name->len, 0); zend_str_tolower(tmp_name->val, ns_len); - ZVAL_NEW_STR(&c, tmp_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + zend_add_literal_string(op_array, &tmp_name TSRMLS_CC); /* lowercased namespace name & lowercased constant name */ - tmp_name = zend_string_alloc(name_len, 0); - zend_str_tolower_copy(tmp_name->val, name, name_len); - ZVAL_NEW_STR(&c, tmp_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); - } + tmp_name = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(tmp_name->val, name->val, name->len); + zend_add_literal_string(op_array, &tmp_name TSRMLS_CC); - if (ns_len) { if (!unqualified) { return ret; } - ns_len++; - name += ns_len; - name_len -= ns_len; + } else { + after_ns = name->val; } - /* original constant name */ - tmp_name = zend_string_init(name, name_len, 0); - ZVAL_NEW_STR(&c, tmp_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + /* original unqualified constant name */ + tmp_name = zend_string_init(after_ns, after_ns_len, 0); + zend_add_literal_string(op_array, &tmp_name TSRMLS_CC); - /* lowercased constant name */ - tmp_name = zend_string_alloc(name_len, 0); - zend_str_tolower_copy(tmp_name->val, name, name_len); - ZVAL_NEW_STR(&c, tmp_name); - zend_add_literal(CG(active_op_array), &c TSRMLS_CC); + /* lowercased unqualified constant name */ + tmp_name = zend_string_alloc(after_ns_len, 0); + zend_str_tolower_copy(tmp_name->val, after_ns, after_ns_len); + zend_add_literal_string(op_array, &tmp_name TSRMLS_CC); return ret; } @@ -559,8 +502,7 @@ void zend_do_free(znode *op1 TSRMLS_DC) /* {{{ */ && opline->result.var == op1->u.op.var) { if (opline->opcode == ZEND_FETCH_R || opline->opcode == ZEND_FETCH_DIM_R || - opline->opcode == ZEND_FETCH_OBJ_R || - opline->opcode == ZEND_QM_ASSIGN_VAR) { + opline->opcode == ZEND_FETCH_OBJ_R) { /* It's very rare and useless case. It's better to use additional FREE opcode and simplify the FETCH handlers their selves */ @@ -653,12 +595,16 @@ zend_string *zend_prefix_with_ns(zend_string *name TSRMLS_DC) { } } -void *zend_hash_find_ptr_lc(HashTable *ht, char *str, size_t len) { +void *zend_hash_find_ptr_lc(HashTable *ht, const char *str, size_t len) { void *result; - zend_string *lcname = zend_string_alloc(len, 0); + zend_string *lcname; + ALLOCA_FLAG(use_heap); + + STR_ALLOCA_ALLOC(lcname, len, use_heap); zend_str_tolower_copy(lcname->val, str, len); result = zend_hash_find_ptr(ht, lcname); - zend_string_free(lcname); + STR_ALLOCA_FREE(lcname, use_heap); + return result; } @@ -882,7 +828,7 @@ static int generate_free_loop_var(znode *var TSRMLS_DC) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); - opline->opcode = var->op_type == IS_TMP_VAR ? ZEND_FREE : ZEND_SWITCH_FREE; + opline->opcode = ZEND_FREE; SET_NODE(opline->op1, var); SET_UNUSED(opline->op2); } @@ -932,1527 +878,6 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ } /* }}} */ -static void do_inherit_parent_constructor(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - zend_function *function, *new_function; - - if (!ce->parent) { - return; - } - - /* You cannot change create_object */ - ce->create_object = ce->parent->create_object; - - /* Inherit special functions if needed */ - if (!ce->get_iterator) { - ce->get_iterator = ce->parent->get_iterator; - } - if (!ce->iterator_funcs.funcs) { - ce->iterator_funcs.funcs = ce->parent->iterator_funcs.funcs; - } - if (!ce->__get) { - ce->__get = ce->parent->__get; - } - if (!ce->__set) { - ce->__set = ce->parent->__set; - } - if (!ce->__unset) { - ce->__unset = ce->parent->__unset; - } - if (!ce->__isset) { - ce->__isset = ce->parent->__isset; - } - if (!ce->__call) { - ce->__call = ce->parent->__call; - } - if (!ce->__callstatic) { - ce->__callstatic = ce->parent->__callstatic; - } - if (!ce->__tostring) { - ce->__tostring = ce->parent->__tostring; - } - if (!ce->clone) { - ce->clone = ce->parent->clone; - } - if(!ce->serialize) { - ce->serialize = ce->parent->serialize; - } - if(!ce->unserialize) { - ce->unserialize = ce->parent->unserialize; - } - if (!ce->destructor) { - ce->destructor = ce->parent->destructor; - } - if (!ce->__debugInfo) { - ce->__debugInfo = ce->parent->__debugInfo; - } - if (ce->constructor) { - if (ce->parent->constructor && ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL) { - zend_error(E_ERROR, "Cannot override final %s::%s() with %s::%s()", - ce->parent->name->val, ce->parent->constructor->common.function_name->val, - ce->name->val, ce->constructor->common.function_name->val - ); - } - return; - } - - if ((function = zend_hash_str_find_ptr(&ce->parent->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1)) != NULL) { - /* inherit parent's constructor */ - if (function->type == ZEND_INTERNAL_FUNCTION) { - new_function = pemalloc(sizeof(zend_internal_function), 1); - memcpy(new_function, function, sizeof(zend_internal_function)); - } else { - new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - memcpy(new_function, function, sizeof(zend_op_array)); - } - zend_hash_str_update_ptr(&ce->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1, new_function); - function_add_ref(new_function); - } else { - /* Don't inherit the old style constructor if we already have the new style constructor */ - zend_string *lc_class_name; - zend_string *lc_parent_class_name; - - lc_class_name = zend_string_alloc(ce->name->len, 0); - zend_str_tolower_copy(lc_class_name->val, ce->name->val, ce->name->len); - if (!zend_hash_exists(&ce->function_table, lc_class_name)) { - lc_parent_class_name = zend_string_alloc(ce->parent->name->len, 0); - zend_str_tolower_copy(lc_parent_class_name->val, ce->parent->name->val, ce->parent->name->len); - if (!zend_hash_exists(&ce->function_table, lc_parent_class_name) && - (function = zend_hash_find_ptr(&ce->parent->function_table, lc_parent_class_name)) != NULL) { - if (function->common.fn_flags & ZEND_ACC_CTOR) { - /* inherit parent's constructor */ - new_function = pemalloc(sizeof(zend_function), function->type == ZEND_INTERNAL_FUNCTION); - memcpy(new_function, function, sizeof(zend_function)); - zend_hash_update_ptr(&ce->function_table, lc_parent_class_name, new_function); - function_add_ref(new_function); - } - } - zend_string_release(lc_parent_class_name); - } - zend_string_free(lc_class_name); - } - ce->constructor = ce->parent->constructor; -} -/* }}} */ - -char *zend_visibility_string(uint32_t fn_flags) /* {{{ */ -{ - if (fn_flags & ZEND_ACC_PRIVATE) { - return "private"; - } - if (fn_flags & ZEND_ACC_PROTECTED) { - return "protected"; - } - if (fn_flags & ZEND_ACC_PUBLIC) { - return "public"; - } - return ""; -} -/* }}} */ - -static zend_function *do_inherit_method(zend_function *old_function TSRMLS_DC) /* {{{ */ -{ - zend_function *new_function; - - if (old_function->type == ZEND_INTERNAL_FUNCTION) { - new_function = pemalloc(sizeof(zend_internal_function), 1); - memcpy(new_function, old_function, sizeof(zend_internal_function)); - } else { - new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - memcpy(new_function, old_function, sizeof(zend_op_array)); - } - /* The class entry of the derived function intentionally remains the same - * as that of the parent class. That allows us to know in which context - * we're running, and handle private method calls properly. - */ - function_add_ref(new_function); - return new_function; -} -/* }}} */ - -static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */ -{ - uint32_t i, num_args; - - /* If it's a user function then arg_info == NULL means we don't have any parameters but - * we still need to do the arg number checks. We are only willing to ignore this for internal - * functions because extensions don't always define arg_info. - */ - if (!proto || (!proto->common.arg_info && proto->common.type != ZEND_USER_FUNCTION)) { - return 1; - } - - /* Checks for constructors only if they are declared in an interface, - * or explicitly marked as abstract - */ - if ((fe->common.fn_flags & ZEND_ACC_CTOR) - && ((proto->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0 - && (proto->common.fn_flags & ZEND_ACC_ABSTRACT) == 0)) { - return 1; - } - - /* If both methods are private do not enforce a signature */ - if ((fe->common.fn_flags & ZEND_ACC_PRIVATE) && (proto->common.fn_flags & ZEND_ACC_PRIVATE)) { - return 1; - } - - /* check number of arguments */ - if (proto->common.required_num_args < fe->common.required_num_args - || proto->common.num_args > fe->common.num_args) { - return 0; - } - - /* by-ref constraints on return values are covariant */ - if ((proto->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) - && !(fe->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { - return 0; - } - - if ((proto->common.fn_flags & ZEND_ACC_VARIADIC) - && !(fe->common.fn_flags & ZEND_ACC_VARIADIC)) { - return 0; - } - - /* For variadic functions any additional (optional) arguments that were added must be - * checked against the signature of the variadic argument, so in this case we have to - * go through all the parameters of the function and not just those present in the - * prototype. */ - num_args = proto->common.num_args; - if ((proto->common.fn_flags & ZEND_ACC_VARIADIC) - && fe->common.num_args > proto->common.num_args) { - num_args = fe->common.num_args; - } - - for (i = 0; i < num_args; i++) { - zend_arg_info *fe_arg_info = &fe->common.arg_info[i]; - - zend_arg_info *proto_arg_info; - if (i < proto->common.num_args) { - proto_arg_info = &proto->common.arg_info[i]; - } else { - proto_arg_info = &proto->common.arg_info[proto->common.num_args-1]; - } - - if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) { - /* Only one has a type hint and the other one doesn't */ - return 0; - } - - if (fe_arg_info->class_name) { - zend_string *fe_class_name, *proto_class_name; - - if (!strcasecmp(fe_arg_info->class_name, "parent") && proto->common.scope) { - fe_class_name = zend_string_copy(proto->common.scope->name); - } else if (!strcasecmp(fe_arg_info->class_name, "self") && fe->common.scope) { - fe_class_name = zend_string_copy(fe->common.scope->name); - } else { - fe_class_name = zend_string_init( - fe_arg_info->class_name, - fe_arg_info->class_name_len, 0); - } - - if (!strcasecmp(proto_arg_info->class_name, "parent") && proto->common.scope && proto->common.scope->parent) { - proto_class_name = zend_string_copy(proto->common.scope->parent->name); - } else if (!strcasecmp(proto_arg_info->class_name, "self") && proto->common.scope) { - proto_class_name = zend_string_copy(proto->common.scope->name); - } else { - proto_class_name = zend_string_init( - proto_arg_info->class_name, - proto_arg_info->class_name_len, 0); - } - - if (strcasecmp(fe_class_name->val, proto_class_name->val)!=0) { - const char *colon; - - if (fe->common.type != ZEND_USER_FUNCTION) { - zend_string_release(proto_class_name); - zend_string_release(fe_class_name); - return 0; - } else if (strchr(proto_class_name->val, '\\') != NULL || - (colon = zend_memrchr(fe_class_name->val, '\\', fe_class_name->len)) == NULL || - strcasecmp(colon+1, proto_class_name->val) != 0) { - zend_class_entry *fe_ce, *proto_ce; - - fe_ce = zend_lookup_class(fe_class_name TSRMLS_CC); - proto_ce = zend_lookup_class(proto_class_name TSRMLS_CC); - - /* Check for class alias */ - if (!fe_ce || !proto_ce || - fe_ce->type == ZEND_INTERNAL_CLASS || - proto_ce->type == ZEND_INTERNAL_CLASS || - fe_ce != proto_ce) { - zend_string_release(proto_class_name); - zend_string_release(fe_class_name); - return 0; - } - } - } - zend_string_release(proto_class_name); - zend_string_release(fe_class_name); - } - if (fe_arg_info->type_hint != proto_arg_info->type_hint) { - /* Incompatible type hint */ - return 0; - } - - /* by-ref constraints on arguments are invariant */ - if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) { - return 0; - } - } - - return 1; -} -/* }}} */ - -#define REALLOC_BUF_IF_EXCEED(buf, offset, length, size) \ - if (UNEXPECTED(offset - buf + size >= length)) { \ - length += size + 1; \ - buf = erealloc(buf, length); \ - } - -static char *zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */ -{ - char *offset, *buf; - uint32_t length = 1024; - - offset = buf = (char *)emalloc(length * sizeof(char)); - if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { - *(offset++) = '&'; - *(offset++) = ' '; - } - - if (fptr->common.scope) { - memcpy(offset, fptr->common.scope->name->val, fptr->common.scope->name->len); - offset += fptr->common.scope->name->len; - *(offset++) = ':'; - *(offset++) = ':'; - } - - { - size_t name_len = fptr->common.function_name->len; - REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len); - memcpy(offset, fptr->common.function_name->val, name_len); - offset += name_len; - } - - *(offset++) = '('; - if (fptr->common.arg_info) { - uint32_t i, required; - zend_arg_info *arg_info = fptr->common.arg_info; - - required = fptr->common.required_num_args; - for (i = 0; i < fptr->common.num_args;) { - if (arg_info->class_name) { - const char *class_name; - uint32_t class_name_len; - if (!strcasecmp(arg_info->class_name, "self") && fptr->common.scope ) { - class_name = fptr->common.scope->name->val; - class_name_len = fptr->common.scope->name->len; - } else if (!strcasecmp(arg_info->class_name, "parent") && fptr->common.scope->parent) { - class_name = fptr->common.scope->parent->name->val; - class_name_len = fptr->common.scope->parent->name->len; - } else { - class_name = arg_info->class_name; - class_name_len = arg_info->class_name_len; - } - REALLOC_BUF_IF_EXCEED(buf, offset, length, class_name_len); - memcpy(offset, class_name, class_name_len); - offset += class_name_len; - *(offset++) = ' '; - } else if (arg_info->type_hint) { - uint32_t type_name_len; - char *type_name = zend_get_type_by_const(arg_info->type_hint); - type_name_len = strlen(type_name); - REALLOC_BUF_IF_EXCEED(buf, offset, length, type_name_len); - memcpy(offset, type_name, type_name_len); - offset += type_name_len; - *(offset++) = ' '; - } - - if (arg_info->pass_by_reference) { - *(offset++) = '&'; - } - - if (arg_info->is_variadic) { - *(offset++) = '.'; - *(offset++) = '.'; - *(offset++) = '.'; - } - - *(offset++) = '$'; - - if (arg_info->name) { - REALLOC_BUF_IF_EXCEED(buf, offset, length, arg_info->name_len); - memcpy(offset, arg_info->name, arg_info->name_len); - offset += arg_info->name_len; - } else { - uint32_t idx = i; - memcpy(offset, "param", 5); - offset += 5; - do { - *(offset++) = (char) (idx % 10) + '0'; - idx /= 10; - } while (idx > 0); - } - if (i >= required && !arg_info->is_variadic) { - *(offset++) = ' '; - *(offset++) = '='; - *(offset++) = ' '; - if (fptr->type == ZEND_USER_FUNCTION) { - zend_op *precv = NULL; - { - uint32_t idx = i; - zend_op *op = ((zend_op_array *)fptr)->opcodes; - zend_op *end = op + ((zend_op_array *)fptr)->last; - - ++idx; - while (op < end) { - if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT) - && op->op1.num == (zend_ulong)idx) - { - precv = op; - } - ++op; - } - } - if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) { - zval *zv = precv->op2.zv; - - if (Z_TYPE_P(zv) == IS_CONSTANT) { - REALLOC_BUF_IF_EXCEED(buf, offset, length, Z_STRLEN_P(zv)); - memcpy(offset, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); - offset += Z_STRLEN_P(zv); - } else if (Z_TYPE_P(zv) == IS_FALSE) { - memcpy(offset, "false", 5); - offset += 5; - } else if (Z_TYPE_P(zv) == IS_TRUE) { - memcpy(offset, "true", 4); - offset += 4; - } else if (Z_TYPE_P(zv) == IS_NULL) { - memcpy(offset, "NULL", 4); - offset += 4; - } else if (Z_TYPE_P(zv) == IS_STRING) { - *(offset++) = '\''; - REALLOC_BUF_IF_EXCEED(buf, offset, length, MIN(Z_STRLEN_P(zv), 10)); - memcpy(offset, Z_STRVAL_P(zv), MIN(Z_STRLEN_P(zv), 10)); - offset += MIN(Z_STRLEN_P(zv), 10); - if (Z_STRLEN_P(zv) > 10) { - *(offset++) = '.'; - *(offset++) = '.'; - *(offset++) = '.'; - } - *(offset++) = '\''; - } else if (Z_TYPE_P(zv) == IS_ARRAY) { - memcpy(offset, "Array", 5); - offset += 5; - } else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { - memcpy(offset, "<expression>", 12); - offset += 12; - } else { - zend_string *str = zval_get_string(zv); - REALLOC_BUF_IF_EXCEED(buf, offset, length, str->len); - memcpy(offset, str->val, str->len); - offset += str->len; - zend_string_release(str); - } - } - } else { - memcpy(offset, "NULL", 4); - offset += 4; - } - } - - if (++i < fptr->common.num_args) { - *(offset++) = ','; - *(offset++) = ' '; - } - arg_info++; - REALLOC_BUF_IF_EXCEED(buf, offset, length, 32); - } - } - *(offset++) = ')'; - *offset = '\0'; - - return buf; -} -/* }}} */ - -static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */ -{ - uint32_t child_flags; - uint32_t parent_flags = parent->common.fn_flags; - - if ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0 - && parent->common.fn_flags & ZEND_ACC_ABSTRACT - && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope) - && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) { - zend_error_noreturn(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", - parent->common.scope->name->val, - child->common.function_name->val, - child->common.prototype ? child->common.prototype->common.scope->name->val : child->common.scope->name->val); - } - - if (parent_flags & ZEND_ACC_FINAL) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot override final method %s::%s()", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val); - } - - child_flags = child->common.fn_flags; - /* You cannot change from static to non static and vice versa. - */ - if ((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC)) { - if (child->common.fn_flags & ZEND_ACC_STATIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non static method %s::%s() static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child)); - } else { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot make static method %s::%s() non static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child)); - } - } - - /* Disallow making an inherited method abstract. */ - if ((child_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_ABSTRACT)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name->val, ZEND_FN_SCOPE_NAME(child)); - } - - if (parent_flags & ZEND_ACC_CHANGED) { - child->common.fn_flags |= ZEND_ACC_CHANGED; - } else { - /* Prevent derived classes from restricting access that was available in parent classes - */ - if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) { - zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); - } else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK)) - && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) { - child->common.fn_flags |= ZEND_ACC_CHANGED; - } - } - - if (parent_flags & ZEND_ACC_PRIVATE) { - child->common.prototype = NULL; - } else if (parent_flags & ZEND_ACC_ABSTRACT) { - child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; - child->common.prototype = parent; - } else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE))) { - /* ctors only have a prototype if it comes from an interface */ - child->common.prototype = parent->common.prototype ? parent->common.prototype : parent; - } - - if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { - if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, zend_get_function_declaration(child->common.prototype TSRMLS_CC)); - } - } else if (EG(error_reporting) & E_STRICT || Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */ - if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) { - char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC); - zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name->val, method_prototype); - efree(method_prototype); - } - } -} -/* }}} */ - -static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_function *parent, zend_string *key, zend_class_entry *child_ce) /* {{{ */ -{ - uint32_t parent_flags = parent->common.fn_flags; - zend_function *child; - TSRMLS_FETCH(); - - if ((child = zend_hash_find_ptr(child_function_table, key)) == NULL) { - if (parent_flags & (ZEND_ACC_ABSTRACT)) { - child_ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; - } - return 1; /* method doesn't exist in child, copy from parent */ - } - - do_inheritance_check_on_method(child, parent TSRMLS_CC); - - return 0; -} -/* }}} */ - -static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_property_info *parent_info, zend_string *key, zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - zend_property_info *child_info; - zend_class_entry *parent_ce = ce->parent; - - if (parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_SHADOW)) { - if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) { - child_info->flags |= ZEND_ACC_CHANGED; - } else { - if(ce->type & ZEND_INTERNAL_CLASS) { - child_info = zend_duplicate_property_info_internal(parent_info); - } else { - child_info = zend_duplicate_property_info(parent_info TSRMLS_CC); - } - zend_hash_update_ptr(&ce->properties_info, key, child_info); - child_info->flags &= ~ZEND_ACC_PRIVATE; /* it's not private anymore */ - child_info->flags |= ZEND_ACC_SHADOW; /* but it's a shadow of private */ - } - return 0; /* don't copy access information to child */ - } - - if ((child_info = zend_hash_find_ptr(&ce->properties_info, key)) != NULL) { - if ((parent_info->flags & ZEND_ACC_STATIC) != (child_info->flags & ZEND_ACC_STATIC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s", - (parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name->val, key->val, - (child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name->val, key->val); - - } - - if(parent_info->flags & ZEND_ACC_CHANGED) { - child_info->flags |= ZEND_ACC_CHANGED; - } - - if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) { - zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name->val, key->val, zend_visibility_string(parent_info->flags), parent_ce->name->val, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); - } else if ((child_info->flags & ZEND_ACC_STATIC) == 0) { - zval_ptr_dtor(&(ce->default_properties_table[parent_info->offset])); - ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset]; - ZVAL_UNDEF(&ce->default_properties_table[child_info->offset]); - child_info->offset = parent_info->offset; - } - return 0; /* Don't copy from parent */ - } else { - return 1; /* Copy from parent */ - } -} -/* }}} */ - -static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */ -{ - if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce TSRMLS_CC) == FAILURE) { - zend_error(E_CORE_ERROR, "Class %s could not implement interface %s", ce->name->val, iface->name->val); - } - if (ce == iface) { - zend_error(E_ERROR, "Interface %s cannot implement itself", ce->name->val); - } -} -/* }}} */ - -ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_entry *iface TSRMLS_DC) /* {{{ */ -{ - /* expects interface to be contained in ce's interface list already */ - uint32_t i, ce_num, if_num = iface->num_interfaces; - zend_class_entry *entry; - - if (if_num==0) { - return; - } - ce_num = ce->num_interfaces; - - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } - - /* Inherit the interfaces, only if they're not already inherited by the class */ - while (if_num--) { - entry = iface->interfaces[if_num]; - for (i = 0; i < ce_num; i++) { - if (ce->interfaces[i] == entry) { - break; - } - } - if (i == ce_num) { - ce->interfaces[ce->num_interfaces++] = entry; - } - } - - /* and now call the implementing handlers */ - while (ce_num < ce->num_interfaces) { - do_implement_interface(ce, ce->interfaces[ce_num++] TSRMLS_CC); - } -} -/* }}} */ - -#ifdef ZTS -# define zval_property_ctor(parent_ce, ce) \ - (((parent_ce)->type != (ce)->type) ? ZVAL_COPY_CTOR : zval_add_ref) -#else -# define zval_property_ctor(parent_ce, ce) \ - zval_add_ref -#endif - -static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */ -{ - if (!Z_ISREF_P(zv)) { - if (parent_ce->type == ZEND_INTERNAL_CLASS) { - ZVAL_NEW_PERSISTENT_REF(zv, zv); - } else { - ZVAL_NEW_REF(zv, zv); - } - } - if (Z_CONSTANT_P(Z_REFVAL_P(zv))) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - if (zend_hash_add(&ce->constants_table, name, zv)) { - Z_ADDREF_P(zv); - } -} -/* }}} */ - -ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */ -{ - zend_property_info *property_info; - zend_function *func; - zend_string *key; - zval *zv; - - if ((ce->ce_flags & ZEND_ACC_INTERFACE) - && !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) { - zend_error_noreturn(E_COMPILE_ERROR, "Interface %s may not inherit from class (%s)", ce->name->val, parent_ce->name->val); - } - if (parent_ce->ce_flags & ZEND_ACC_FINAL_CLASS) { - zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ce->name->val, parent_ce->name->val); - } - - ce->parent = parent_ce; - /* Copy serialize/unserialize callbacks */ - if (!ce->serialize) { - ce->serialize = parent_ce->serialize; - } - if (!ce->unserialize) { - ce->unserialize = parent_ce->unserialize; - } - - /* Inherit interfaces */ - zend_do_inherit_interfaces(ce, parent_ce TSRMLS_CC); - - /* Inherit properties */ - if (parent_ce->default_properties_count) { - int i = ce->default_properties_count + parent_ce->default_properties_count; - - ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS); - if (ce->default_properties_count) { - while (i-- > parent_ce->default_properties_count) { - ce->default_properties_table[i] = ce->default_properties_table[i - parent_ce->default_properties_count]; - } - } - for (i = 0; i < parent_ce->default_properties_count; i++) { -#ifdef ZTS - if (parent_ce->type != ce->type) { - ZVAL_DUP(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]); - if (Z_OPT_CONSTANT(ce->default_properties_table[i])) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - continue; - } -#endif - - ZVAL_COPY(&ce->default_properties_table[i], &parent_ce->default_properties_table[i]); - if (Z_OPT_CONSTANT(ce->default_properties_table[i])) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - } - ce->default_properties_count += parent_ce->default_properties_count; - } - - if (parent_ce->type != ce->type) { - /* User class extends internal class */ - zend_update_class_constants(parent_ce TSRMLS_CC); - if (parent_ce->default_static_members_count) { - int i = ce->default_static_members_count + parent_ce->default_static_members_count; - - ce->default_static_members_table = erealloc(ce->default_static_members_table, sizeof(zval) * i); - if (ce->default_static_members_count) { - while (i-- > parent_ce->default_static_members_count) { - ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count]; - } - } - for (i = 0; i < parent_ce->default_static_members_count; i++) { - ZVAL_MAKE_REF(&CE_STATIC_MEMBERS(parent_ce)[i]); - ce->default_static_members_table[i] = CE_STATIC_MEMBERS(parent_ce)[i]; - Z_ADDREF(ce->default_static_members_table[i]); - if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - } - ce->default_static_members_count += parent_ce->default_static_members_count; - ce->static_members_table = ce->default_static_members_table; - } - } else { - if (parent_ce->default_static_members_count) { - int i = ce->default_static_members_count + parent_ce->default_static_members_count; - - ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * i, ce->type == ZEND_INTERNAL_CLASS); - if (ce->default_static_members_count) { - while (i-- > parent_ce->default_static_members_count) { - ce->default_static_members_table[i] = ce->default_static_members_table[i - parent_ce->default_static_members_count]; - } - } - for (i = 0; i < parent_ce->default_static_members_count; i++) { - ZVAL_MAKE_REF(&parent_ce->default_static_members_table[i]); - ce->default_static_members_table[i] = parent_ce->default_static_members_table[i]; - Z_ADDREF(ce->default_static_members_table[i]); - if (Z_CONSTANT_P(Z_REFVAL(ce->default_static_members_table[i]))) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - } - ce->default_static_members_count += parent_ce->default_static_members_count; - if (ce->type == ZEND_USER_CLASS) { - ce->static_members_table = ce->default_static_members_table; - } - } - } - - ZEND_HASH_FOREACH_PTR(&ce->properties_info, property_info) { - if (property_info->ce == ce) { - if (property_info->flags & ZEND_ACC_STATIC) { - property_info->offset += parent_ce->default_static_members_count; - } else { - property_info->offset += parent_ce->default_properties_count; - } - } - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->properties_info, key, property_info) { - if (do_inherit_property_access_check(&ce->properties_info, property_info, key, ce TSRMLS_CC)) { - if (ce->type & ZEND_INTERNAL_CLASS) { - property_info = zend_duplicate_property_info_internal(property_info); - } else { - property_info = zend_duplicate_property_info(property_info TSRMLS_CC); - } - zend_hash_add_new_ptr(&ce->properties_info, key, property_info); - } - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) { - do_inherit_class_constant(key, zv, ce, parent_ce TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { - if (do_inherit_method_check(&ce->function_table, func, key, ce)) { - zend_function *new_func = do_inherit_method(func TSRMLS_CC); - zend_hash_add_new_ptr(&ce->function_table, key, new_func); - } - } ZEND_HASH_FOREACH_END(); - - do_inherit_parent_constructor(ce TSRMLS_CC); - - if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) { - ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; - } else if (!(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) { - /* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */ - zend_verify_abstract_class(ce TSRMLS_CC); - } - ce->ce_flags |= parent_ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS; -} -/* }}} */ - -static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ -{ - zval *old_constant; - - if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) { - if (!Z_ISREF_P(old_constant) || - !Z_ISREF_P(parent_constant) || - Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", name->val, iface->name->val); - } - return 0; - } - return 1; -} -/* }}} */ - -static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */ -{ - if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) { - ZVAL_MAKE_REF(zv); - Z_ADDREF_P(zv); - if (Z_CONSTANT_P(Z_REFVAL_P(zv))) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } - zend_hash_update(&ce->constants_table, name, zv); - } -} -/* }}} */ - -ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */ -{ - uint32_t i, ignore = 0; - uint32_t current_iface_num = ce->num_interfaces; - uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0; - zend_function *func; - zend_string *key; - zval *zv; - - for (i = 0; i < ce->num_interfaces; i++) { - if (ce->interfaces[i] == NULL) { - memmove(ce->interfaces + i, ce->interfaces + i + 1, sizeof(zend_class_entry*) * (--ce->num_interfaces - i)); - i--; - } else if (ce->interfaces[i] == iface) { - if (i < parent_iface_num) { - ignore = 1; - } else { - zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ce->name->val, iface->name->val); - } - } - } - if (ignore) { - /* Check for attempt to redeclare interface constants */ - ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) { - do_inherit_constant_check(&iface->constants_table, zv, key, iface); - } ZEND_HASH_FOREACH_END(); - } else { - if (ce->num_interfaces >= current_iface_num) { - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } - } - ce->interfaces[ce->num_interfaces++] = iface; - - ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) { - do_inherit_iface_constant(key, zv, ce, iface TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) { - if (do_inherit_method_check(&ce->function_table, func, key, ce)) { - zend_function *new_func = do_inherit_method(func TSRMLS_CC); - zend_hash_add_new_ptr(&ce->function_table, key, new_func); - } - } ZEND_HASH_FOREACH_END(); - - do_implement_interface(ce, iface TSRMLS_CC); - zend_do_inherit_interfaces(ce, iface TSRMLS_CC); - } -} -/* }}} */ - -ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */ -{ - uint32_t i, ignore = 0; - uint32_t current_trait_num = ce->num_traits; - uint32_t parent_trait_num = ce->parent ? ce->parent->num_traits : 0; - - for (i = 0; i < ce->num_traits; i++) { - if (ce->traits[i] == NULL) { - memmove(ce->traits + i, ce->traits + i + 1, sizeof(zend_class_entry*) * (--ce->num_traits - i)); - i--; - } else if (ce->traits[i] == trait) { - if (i < parent_trait_num) { - ignore = 1; - } - } - } - if (!ignore) { - if (ce->num_traits >= current_trait_num) { - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->traits = (zend_class_entry **) realloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num)); - } else { - ce->traits = (zend_class_entry **) erealloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num)); - } - } - ce->traits[ce->num_traits++] = trait; - } -} -/* }}} */ - -static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_function *other_fn TSRMLS_DC) /* {{{ */ -{ - uint32_t fn_flags = fn->common.scope->ce_flags; - uint32_t other_flags = other_fn->common.scope->ce_flags; - - return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) - && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC)) - && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == - (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ -} -/* }}} */ - -static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zend_function* fe TSRMLS_DC) /* {{{ */ -{ - if (!strncmp(mname->val, ZEND_CLONE_FUNC_NAME, mname->len)) { - ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE; - } else if (!strncmp(mname->val, ZEND_CONSTRUCTOR_FUNC_NAME, mname->len)) { - if (ce->constructor) { - zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val); - } - ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR; - } else if (!strncmp(mname->val, ZEND_DESTRUCTOR_FUNC_NAME, mname->len)) { - ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR; - } else if (!strncmp(mname->val, ZEND_GET_FUNC_NAME, mname->len)) { - ce->__get = fe; - } else if (!strncmp(mname->val, ZEND_SET_FUNC_NAME, mname->len)) { - ce->__set = fe; - } else if (!strncmp(mname->val, ZEND_CALL_FUNC_NAME, mname->len)) { - ce->__call = fe; - } else if (!strncmp(mname->val, ZEND_UNSET_FUNC_NAME, mname->len)) { - ce->__unset = fe; - } else if (!strncmp(mname->val, ZEND_ISSET_FUNC_NAME, mname->len)) { - ce->__isset = fe; - } else if (!strncmp(mname->val, ZEND_CALLSTATIC_FUNC_NAME, mname->len)) { - ce->__callstatic = fe; - } else if (!strncmp(mname->val, ZEND_TOSTRING_FUNC_NAME, mname->len)) { - ce->__tostring = fe; - } else if (!strncmp(mname->val, ZEND_DEBUGINFO_FUNC_NAME, mname->len)) { - ce->__debugInfo = fe; - } else if (ce->name->len == mname->len) { - zend_string *lowercase_name = zend_string_alloc(ce->name->len, 0); - zend_str_tolower_copy(lowercase_name->val, ce->name->val, ce->name->len); - lowercase_name = zend_new_interned_string(lowercase_name TSRMLS_CC); - if (!memcmp(mname->val, lowercase_name->val, mname->len)) { - if (ce->constructor) { - zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name->val); - } - ce->constructor = fe; - fe->common.fn_flags |= ZEND_ACC_CTOR; - } - zend_string_release(lowercase_name); - } -} -/* }}} */ - -static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_string *key, zend_function *fn, HashTable **overriden TSRMLS_DC) /* {{{ */ -{ - zend_function *existing_fn = NULL; - zend_function *new_fn; - - if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) { - if (existing_fn->common.scope == ce) { - /* members from the current class override trait methods */ - /* use temporary *overriden HashTable to detect hidden conflict */ - if (*overriden) { - if ((existing_fn = zend_hash_find_ptr(*overriden, key)) != NULL) { - if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - /* Make sure the trait method is compatible with previosly declared abstract method */ - if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - zend_get_function_declaration(fn TSRMLS_CC), - zend_get_function_declaration(existing_fn TSRMLS_CC)); - } - } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - /* Make sure the abstract declaration is compatible with previous declaration */ - if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - zend_get_function_declaration(fn TSRMLS_CC), - zend_get_function_declaration(existing_fn TSRMLS_CC)); - } - return; - } - } - } else { - ALLOC_HASHTABLE(*overriden); - zend_hash_init_ex(*overriden, 8, NULL, ptr_dtor, 0, 0); - } - fn = zend_hash_update_mem(*overriden, key, fn, sizeof(zend_function)); - return; - } else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - /* Make sure the trait method is compatible with previosly declared abstract method */ - if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - zend_get_function_declaration(fn TSRMLS_CC), - zend_get_function_declaration(existing_fn TSRMLS_CC)); - } - } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - /* Make sure the abstract declaration is compatible with previous declaration */ - if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) { - zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - zend_get_function_declaration(fn TSRMLS_CC), - zend_get_function_declaration(existing_fn TSRMLS_CC)); - } - return; - } else if ((existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { - /* two traits can't define the same non-abstract method */ -#if 1 - zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s", - name, ce->name->val); -#else /* TODO: better error message */ - zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s", - fn->common.scope->name->val, fn->common.function_name->val, - ce->name->val, name, - existing_fn->common.scope->name->val, existing_fn->common.function_name->val); -#endif - } else { - /* inherited members are overridden by members inserted by traits */ - /* check whether the trait method fulfills the inheritance requirements */ - do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC); - } - } - - function_add_ref(fn); - new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - memcpy(new_fn, fn, sizeof(zend_op_array)); - fn = zend_hash_update_ptr(&ce->function_table, key, new_fn); - zend_add_magic_methods(ce, key, fn TSRMLS_CC); -} -/* }}} */ - -static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* {{{ */ -{ - if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { - - fn->common.scope = ce; - - if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { - ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; - } - if (fn->op_array.static_variables) { - ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS; - } - } -} -/* }}} */ - -static int zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable **overriden, HashTable *exclude_table TSRMLS_DC) /* {{{ */ -{ - zend_trait_alias *alias, **alias_ptr; - zend_string *lcname; - zend_function fn_copy; - - /* apply aliases which are qualified with a class name, there should not be any ambiguity */ - if (ce->trait_aliases) { - alias_ptr = ce->trait_aliases; - alias = *alias_ptr; - while (alias) { - /* Scope unset or equal to the function we compare to, and the alias applies to fn */ - if (alias->alias != NULL - && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce) - && alias->trait_method->method_name->len == fnname->len - && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) { - fn_copy = *fn; - - /* if it is 0, no modifieres has been changed */ - if (alias->modifiers) { - fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); - } - - lcname = zend_string_alloc(alias->alias->len, 0); - zend_str_tolower_copy(lcname->val, alias->alias->val, alias->alias->len); - zend_add_trait_method(ce, alias->alias->val, lcname, &fn_copy, overriden TSRMLS_CC); - zend_string_release(lcname); - - /* Record the trait from which this alias was resolved. */ - if (!alias->trait_method->ce) { - alias->trait_method->ce = fn->common.scope; - } - } - alias_ptr++; - alias = *alias_ptr; - } - } - - if (exclude_table == NULL || zend_hash_find(exclude_table, fnname) == NULL) { - /* is not in hashtable, thus, function is not to be excluded */ - fn_copy = *fn; - - /* apply aliases which have not alias name, just setting visibility */ - if (ce->trait_aliases) { - alias_ptr = ce->trait_aliases; - alias = *alias_ptr; - while (alias) { - /* Scope unset or equal to the function we compare to, and the alias applies to fn */ - if (alias->alias == NULL && alias->modifiers != 0 - && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce) - && (alias->trait_method->method_name->len == fnname->len) - && (zend_binary_strcasecmp(alias->trait_method->method_name->val, alias->trait_method->method_name->len, fnname->val, fnname->len) == 0)) { - - fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); - - /** Record the trait from which this alias was resolved. */ - if (!alias->trait_method->ce) { - alias->trait_method->ce = fn->common.scope; - } - } - alias_ptr++; - alias = *alias_ptr; - } - } - - zend_add_trait_method(ce, fn->common.function_name->val, fnname, &fn_copy, overriden TSRMLS_CC); - } - - return ZEND_HASH_APPLY_KEEP; -} -/* }}} */ - -static void zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */ -{ - uint32_t i; - - if ((trait->ce_flags & ZEND_ACC_TRAIT) != ZEND_ACC_TRAIT) { - zend_error_noreturn(E_COMPILE_ERROR, "Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements", trait->name->val); - } - - for (i = 0; i < ce->num_traits; i++) { - if (ce->traits[i] == trait) { - return; - } - } - zend_error_noreturn(E_COMPILE_ERROR, "Required Trait %s wasn't added to %s", trait->name->val, ce->name->val); -} -/* }}} */ - -static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - size_t i, j = 0; - zend_trait_precedence *cur_precedence; - zend_trait_method_reference *cur_method_ref; - zend_string *lcname; - zend_bool method_exists; - - /* resolve class references */ - if (ce->trait_precedences) { - i = 0; - while ((cur_precedence = ce->trait_precedences[i])) { - /** Resolve classes for all precedence operations. */ - if (cur_precedence->exclude_from_classes) { - cur_method_ref = cur_precedence->trait_method; - if (!(cur_precedence->trait_method->ce = zend_fetch_class(cur_method_ref->class_name, - ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { - zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val); - } - zend_check_trait_usage(ce, cur_precedence->trait_method->ce TSRMLS_CC); - - /** Ensure that the prefered method is actually available. */ - lcname = zend_string_alloc(cur_method_ref->method_name->len, 0); - zend_str_tolower_copy(lcname->val, - cur_method_ref->method_name->val, - cur_method_ref->method_name->len); - method_exists = zend_hash_exists(&cur_method_ref->ce->function_table, - lcname); - zend_string_free(lcname); - if (!method_exists) { - zend_error_noreturn(E_COMPILE_ERROR, - "A precedence rule was defined for %s::%s but this method does not exist", - cur_method_ref->ce->name->val, - cur_method_ref->method_name->val); - } - - /** With the other traits, we are more permissive. - We do not give errors for those. This allows to be more - defensive in such definitions. - However, we want to make sure that the insteadof declaration - is consistent in itself. - */ - j = 0; - while (cur_precedence->exclude_from_classes[j].class_name) { - zend_string* class_name = cur_precedence->exclude_from_classes[j].class_name; - - if (!(cur_precedence->exclude_from_classes[j].ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { - zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", class_name->val); - } - zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j].ce TSRMLS_CC); - - /* make sure that the trait method is not from a class mentioned in - exclude_from_classes, for consistency */ - if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i].ce) { - zend_error_noreturn(E_COMPILE_ERROR, - "Inconsistent insteadof definition. " - "The method %s is to be used from %s, but %s is also on the exclude list", - cur_method_ref->method_name->val, - cur_precedence->trait_method->ce->name->val, - cur_precedence->trait_method->ce->name->val); - } - - zend_string_release(class_name); - j++; - } - } - i++; - } - } - - if (ce->trait_aliases) { - i = 0; - while (ce->trait_aliases[i]) { - /** For all aliases with an explicit class name, resolve the class now. */ - if (ce->trait_aliases[i]->trait_method->class_name) { - cur_method_ref = ce->trait_aliases[i]->trait_method; - if (!(cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { - zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name->val); - } - zend_check_trait_usage(ce, cur_method_ref->ce TSRMLS_CC); - - /** And, ensure that the referenced method is resolvable, too. */ - lcname = zend_string_alloc(cur_method_ref->method_name->len, 0); - zend_str_tolower_copy(lcname->val, - cur_method_ref->method_name->val, - cur_method_ref->method_name->len); - method_exists = zend_hash_exists(&cur_method_ref->ce->function_table, - lcname); - zend_string_free(lcname); - - if (!method_exists) { - zend_error_noreturn(E_COMPILE_ERROR, "An alias was defined for %s::%s but this method does not exist", cur_method_ref->ce->name->val, cur_method_ref->method_name->val); - } - } - i++; - } - } -} -/* }}} */ - -static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) /* {{{ */ -{ - size_t i = 0, j; - - if (!precedences) { - return; - } - while (precedences[i]) { - if (precedences[i]->exclude_from_classes) { - j = 0; - while (precedences[i]->exclude_from_classes[j].ce) { - if (precedences[i]->exclude_from_classes[j].ce == trait) { - zend_string *lcname = zend_string_alloc(precedences[i]->trait_method->method_name->len, 0); - - zend_str_tolower_copy(lcname->val, - precedences[i]->trait_method->method_name->val, - precedences[i]->trait_method->method_name->len); - if (zend_hash_add_empty_element(exclude_table, lcname) == NULL) { - zend_string_release(lcname); - zend_error_noreturn(E_COMPILE_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times", precedences[i]->trait_method->method_name->val, trait->name->val); - } - zend_string_release(lcname); - } - ++j; - } - } - ++i; - } -} -/* }}} */ - -static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - uint32_t i; - HashTable *overriden = NULL; - zend_string *key; - zend_function *fn; - - for (i = 0; i < ce->num_traits; i++) { - if (ce->trait_precedences) { - HashTable exclude_table; - - /* TODO: revisit this start size, may be its not optimal */ - zend_hash_init_ex(&exclude_table, 8, NULL, NULL, 0, 0); - - zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]); - - /* copies functions, applies defined aliasing, and excludes unused trait methods */ - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) { - zend_traits_copy_functions(key, fn, ce, &overriden, &exclude_table TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); - - zend_hash_destroy(&exclude_table); - } else { - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->traits[i]->function_table, key, fn) { - zend_traits_copy_functions(key, fn, ce, &overriden, NULL TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); - } - } - - ZEND_HASH_FOREACH_PTR(&ce->function_table, fn) { - zend_fixup_trait_method(fn, ce); - } ZEND_HASH_FOREACH_END(); - - if (overriden) { - zend_hash_destroy(overriden); - FREE_HASHTABLE(overriden); - } -} -/* }}} */ - -static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, zend_string *prop_name, zend_class_entry *coliding_ce) /* {{{ */ -{ - size_t i; - - if (coliding_ce == ce) { - for (i = 0; i < current_trait; i++) { - if (zend_hash_exists(&ce->traits[i]->properties_info, prop_name)) { - return ce->traits[i]; - } - } - } - - return coliding_ce; -} -/* }}} */ - -static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - size_t i; - zend_property_info *property_info; - zend_property_info *coliding_prop; - zval compare_result; - zend_string* prop_name; - const char* class_name_unused; - zend_bool not_compatible; - zval* prop_value; - uint32_t flags; - zend_string *doc_comment; - - /* In the following steps the properties are inserted into the property table - * for that, a very strict approach is applied: - * - check for compatibility, if not compatible with any property in class -> fatal - * - if compatible, then strict notice - */ - for (i = 0; i < ce->num_traits; i++) { - ZEND_HASH_FOREACH_PTR(&ce->traits[i]->properties_info, property_info) { - /* first get the unmangeld name if necessary, - * then check whether the property is already there - */ - flags = property_info->flags; - if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { - prop_name = zend_string_copy(property_info->name); - } else { - const char *pname; - int pname_len; - - /* for private and protected we need to unmangle the names */ - zend_unmangle_property_name_ex(property_info->name->val, property_info->name->len, - &class_name_unused, &pname, &pname_len); - prop_name = zend_string_init(pname, pname_len, 0); - } - - /* next: check for conflicts with current class */ - if ((coliding_prop = zend_hash_find_ptr(&ce->properties_info, prop_name)) != NULL) { - if (coliding_prop->flags & ZEND_ACC_SHADOW) { - zend_hash_del(&ce->properties_info, prop_name); - flags |= ZEND_ACC_CHANGED; - } else { - if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC)) - == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { - /* flags are identical, now the value needs to be checked */ - if (flags & ZEND_ACC_STATIC) { - not_compatible = (FAILURE == compare_function(&compare_result, - &ce->default_static_members_table[coliding_prop->offset], - &ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC)) - || (Z_LVAL(compare_result) != 0); - } else { - not_compatible = (FAILURE == compare_function(&compare_result, - &ce->default_properties_table[coliding_prop->offset], - &ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC)) - || (Z_LVAL(compare_result) != 0); - } - } else { - /* the flags are not identical, thus, we assume properties are not compatible */ - not_compatible = 1; - } - - if (not_compatible) { - zend_error_noreturn(E_COMPILE_ERROR, - "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", - find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val, - property_info->ce->name->val, - prop_name->val, - ce->name->val); - } else { - zend_error(E_STRICT, - "%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed", - find_first_definition(ce, i, prop_name, coliding_prop->ce)->name->val, - property_info->ce->name->val, - prop_name->val, - ce->name->val); - zend_string_release(prop_name); - continue; - } - } - } - - /* property not found, so lets add it */ - if (flags & ZEND_ACC_STATIC) { - prop_value = &ce->traits[i]->default_static_members_table[property_info->offset]; - } else { - prop_value = &ce->traits[i]->default_properties_table[property_info->offset]; - } - if (Z_REFCOUNTED_P(prop_value)) Z_ADDREF_P(prop_value); - - doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; - zend_declare_property_ex(ce, prop_name, - prop_value, flags, - doc_comment TSRMLS_CC); - zend_string_release(prop_name); - } ZEND_HASH_FOREACH_END(); - } -} -/* }}} */ - -static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - int i = 0; - zend_trait_alias* cur_alias; - zend_string* lc_method_name; - - if (ce->trait_aliases) { - while (ce->trait_aliases[i]) { - cur_alias = ce->trait_aliases[i]; - /** The trait for this alias has not been resolved, this means, this - alias was not applied. Abort with an error. */ - if (!cur_alias->trait_method->ce) { - if (cur_alias->alias) { - /** Plain old inconsistency/typo/bug */ - zend_error_noreturn(E_COMPILE_ERROR, - "An alias (%s) was defined for method %s(), but this method does not exist", - cur_alias->alias->val, - cur_alias->trait_method->method_name->val); - } else { - /** Here are two possible cases: - 1) this is an attempt to modifiy the visibility - of a method introduce as part of another alias. - Since that seems to violate the DRY principle, - we check against it and abort. - 2) it is just a plain old inconsitency/typo/bug - as in the case where alias is set. */ - - lc_method_name = zend_string_alloc(cur_alias->trait_method->method_name->len, 0); - zend_str_tolower_copy( - lc_method_name->val, - cur_alias->trait_method->method_name->val, - cur_alias->trait_method->method_name->len); - if (zend_hash_exists(&ce->function_table, - lc_method_name)) { - zend_string_free(lc_method_name); - zend_error_noreturn(E_COMPILE_ERROR, - "The modifiers for the trait alias %s() need to be changed in the same statment in which the alias is defined. Error", - cur_alias->trait_method->method_name->val); - } else { - zend_string_free(lc_method_name); - zend_error_noreturn(E_COMPILE_ERROR, - "The modifiers of the trait method %s() are changed, but this method does not exist. Error", - cur_alias->trait_method->method_name->val); - - } - } - } - i++; - } - } -} -/* }}} */ - -ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ -{ - - if (ce->num_traits <= 0) { - return; - } - - /* complete initialization of trait strutures in ce */ - zend_traits_init_trait_structures(ce TSRMLS_CC); - - /* first care about all methods to be flattened into the class */ - zend_do_traits_method_binding(ce TSRMLS_CC); - - /* Aliases which have not been applied indicate typos/bugs. */ - zend_do_check_for_inconsistent_traits_aliasing(ce TSRMLS_CC); - - /* then flatten the properties into it, to, mostly to notfiy developer about problems */ - zend_do_traits_property_binding(ce TSRMLS_CC); - - /* verify that all abstract methods from traits have been implemented */ - zend_verify_abstract_class(ce TSRMLS_CC); - - /* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */ - if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { - ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; - } -} -/* }}} */ - ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time TSRMLS_DC) /* {{{ */ { zend_function *function, *new_function; @@ -2668,13 +1093,11 @@ ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array TSRMLS } /* }}} */ -ZEND_API zend_string *zend_mangle_property_name(const char *src1, int src1_length, const char *src2, int src2_length, int internal) /* {{{ */ +ZEND_API zend_string *zend_mangle_property_name(const char *src1, size_t src1_length, const char *src2, size_t src2_length, int internal) /* {{{ */ { - zend_string *prop_name; - int prop_name_length; + size_t prop_name_length = 1 + src1_length + 1 + src2_length; + zend_string *prop_name = zend_string_alloc(prop_name_length, internal); - prop_name_length = 1 + src1_length + 1 + src2_length; - prop_name = zend_string_alloc(prop_name_length, internal); prop_name->val[0] = '\0'; memcpy(prop_name->val + 1, src1, src1_length+1); memcpy(prop_name->val + 1 + src1_length + 1, src2, src2_length+1); @@ -2682,105 +1105,94 @@ ZEND_API zend_string *zend_mangle_property_name(const char *src1, int src1_lengt } /* }}} */ -static int zend_strnlen(const char* s, int maxlen) /* {{{ */ +static int zend_strnlen(const char* s, size_t maxlen) /* {{{ */ { - int len = 0; + size_t len = 0; while (*s++ && maxlen--) len++; return len; } /* }}} */ -ZEND_API int zend_unmangle_property_name_ex(const char *mangled_property, int len, const char **class_name, const char **prop_name, int *prop_len) /* {{{ */ +ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char **class_name, const char **prop_name, size_t *prop_len) /* {{{ */ { - int class_name_len; + size_t class_name_len; *class_name = NULL; - if (mangled_property[0]!=0) { - *prop_name = mangled_property; + if (name->val[0] != '\0') { + *prop_name = name->val; if (prop_len) { - *prop_len = len; + *prop_len = name->len; } return SUCCESS; } - if (len < 3 || mangled_property[1]==0) { + if (name->len < 3 || name->val[1] == '\0') { zend_error(E_NOTICE, "Illegal member variable name"); - *prop_name = mangled_property; + *prop_name = name->val; if (prop_len) { - *prop_len = len; + *prop_len = name->len; } return FAILURE; } - class_name_len = zend_strnlen(mangled_property + 1, --len - 1) + 1; - if (class_name_len >= len || mangled_property[class_name_len]!=0) { + class_name_len = zend_strnlen(name->val + 1, name->len - 2); + if (class_name_len >= name->len - 2 || name->val[class_name_len + 1] != '\0') { zend_error(E_NOTICE, "Corrupt member variable name"); - *prop_name = mangled_property; + *prop_name = name->val; if (prop_len) { - *prop_len = len + 1; + *prop_len = name->len; } return FAILURE; } - *class_name = mangled_property + 1; - *prop_name = (*class_name) + class_name_len; + + *class_name = name->val + 1; + *prop_name = name->val + class_name_len + 2; if (prop_len) { - *prop_len = len - class_name_len; + *prop_len = name->len - class_name_len - 2; } return SUCCESS; } /* }}} */ -static zend_constant *zend_get_ct_const(zend_string *name, int all_internal_constants_substitution TSRMLS_DC) /* {{{ */ +static zend_constant *zend_lookup_reserved_const(const char *name, size_t len TSRMLS_DC) /* {{{ */ { - zend_constant *c = NULL; - char *lookup_name; - - if (name->val[0] == '\\') { - c = zend_hash_str_find_ptr(EG(zend_constants), name->val + 1, name->len - 1); - if (!c) { - lookup_name = zend_str_tolower_dup(name->val + 1, name->len - 1); - c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, name->len - 1); - efree(lookup_name); - - if (c && (c->flags & CONST_CT_SUBST) && !(c->flags & CONST_CS)) { - return c; - } - return NULL; - } - } else if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) { - lookup_name = zend_str_tolower_dup(name->val, name->len); - c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, name->len); - efree(lookup_name); - - if (c && (c->flags & CONST_CT_SUBST) && !(c->flags & CONST_CS)) { - return c; - } - return NULL; - } - - if (c->flags & CONST_CT_SUBST) { - return c; - } - if (all_internal_constants_substitution && - (c->flags & CONST_PERSISTENT) && - !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION) && - !Z_CONSTANT(c->value)) { + zend_constant *c = zend_hash_find_ptr_lc(EG(zend_constants), name, len); + if (c && !(c->flags & CONST_CS) && (c->flags & CONST_CT_SUBST)) { return c; } return NULL; } /* }}} */ -static int zend_constant_ct_subst(znode *result, zval *const_name, int all_internal_constants_substitution TSRMLS_DC) /* {{{ */ +static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool is_fully_qualified TSRMLS_DC) /* {{{ */ { - zend_constant *c = zend_get_ct_const(Z_STR_P(const_name), - all_internal_constants_substitution TSRMLS_CC); + zend_constant *c; - if (c) { - result->op_type = IS_CONST; - ZVAL_DUP(&result->u.constant, &c->value); - return 1; + if (!(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { + /* Substitute case-sensitive (or lowercase) persistent constants */ + c = zend_hash_find_ptr(EG(zend_constants), name); + if (c && (c->flags & CONST_PERSISTENT)) { + ZVAL_DUP(zv, &c->value); + return 1; + } } + + { + /* Substitute true, false and null (including unqualified usage in namespaces) */ + const char *lookup_name = name->val; + size_t lookup_len = name->len; + + if (!is_fully_qualified) { + zend_get_unqualified_name(name, &lookup_name, &lookup_len); + } + + c = zend_lookup_reserved_const(lookup_name, lookup_len TSRMLS_CC); + if (c) { + ZVAL_DUP(zv, &c->value); + return 1; + } + } + return 0; } /* }}} */ @@ -2934,9 +1346,6 @@ again: if (LANG_SCNG(yy_text)[LANG_SCNG(yy_leng)-1] != '>') { CG(increment_lineno) = 1; } - if (CG(has_bracketed_namespaces) && !CG(in_namespace)) { - goto again; - } retval = ';'; /* implicit ; */ break; case T_OPEN_TAG_WITH_ECHO: @@ -3311,7 +1720,9 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z SET_NODE(opline->op2, op2); } - zend_make_tmp_result(result, opline TSRMLS_CC); + if (result) { + zend_make_tmp_result(result, opline TSRMLS_CC); + } return opline; } @@ -3575,7 +1986,6 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint zend_compile_expr(&name_node, name_ast TSRMLS_CC); - opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL TSRMLS_CC); opline->extended_value = ZEND_FETCH_LOCAL; @@ -3613,6 +2023,22 @@ 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 TSRMLS_DC); +void zend_compile_assign(znode *result, zend_ast *ast TSRMLS_DC); +static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node TSRMLS_DC); + +static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node TSRMLS_DC) /* {{{ */ +{ + znode dummy_node; + if (var_ast->kind == ZEND_AST_LIST) { + zend_compile_list_assign(&dummy_node, var_ast, value_node TSRMLS_CC); + } else { + zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast, + zend_ast_create_znode(value_node)); + zend_compile_assign(&dummy_node, assign_ast TSRMLS_CC); + } + zend_do_free(&dummy_node TSRMLS_CC); +} +/* }}} */ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type TSRMLS_DC) /* {{{ */ { @@ -3773,7 +2199,7 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n for (i = 0; i < list->children; ++i) { zend_ast *var_ast = list->child[i]; - znode fetch_result, dim_node, var_node, assign_result; + znode fetch_result, dim_node; zend_op *opline; if (var_ast == NULL) { @@ -3791,17 +2217,7 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n get_list_fetch_opcode(expr_node->op_type), expr_node, &dim_node TSRMLS_CC); opline->extended_value |= ZEND_FETCH_ADD_LOCK; - if (var_ast->kind != ZEND_AST_LIST) { - if (is_this_fetch(var_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); - } - zend_compile_var(&var_node, var_ast, BP_VAR_W TSRMLS_CC); - zend_emit_op(&assign_result, ZEND_ASSIGN, &var_node, &fetch_result TSRMLS_CC); - zend_do_free(&assign_result TSRMLS_CC); - } else { - zend_compile_list_assign(&assign_result, var_ast, &fetch_result TSRMLS_CC); - zend_do_free(&assign_result TSRMLS_CC); - } + zend_emit_assign_znode(var_ast, &fetch_result TSRMLS_CC); } *result = *expr_node; } @@ -3903,16 +2319,6 @@ void zend_compile_assign(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ } /* }}} */ -static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node TSRMLS_DC) /* {{{ */ -{ - znode dummy_node; - zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast, - zend_ast_create_znode(value_node)); - zend_compile_assign(&dummy_node, assign_ast TSRMLS_CC); - zend_do_free(&dummy_node TSRMLS_CC); -} -/* }}} */ - void zend_compile_assign_ref(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { zend_ast *target_ast = ast->child[0]; @@ -3929,6 +2335,10 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_var(&target_node, target_ast, BP_VAR_W TSRMLS_CC); zend_compile_var(&source_node, source_ast, BP_VAR_REF TSRMLS_CC); + if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use result of built-in function in write context"); + } + opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node TSRMLS_CC); if (!result) { opline->result_type |= EXT_TYPE_UNUSED; @@ -4142,7 +2552,7 @@ void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast TS SET_UNUSED(opline->op1); opline->op2_type = IS_CONST; opline->op2.constant = zend_add_ns_func_name_literal( - CG(active_op_array), &name_node->u.constant TSRMLS_CC); + CG(active_op_array), Z_STR(name_node->u.constant) TSRMLS_CC); zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); zend_compile_call_common(result, args_ast, NULL TSRMLS_CC); @@ -4156,8 +2566,8 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a SET_UNUSED(opline->op1); if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) { opline->op2_type = IS_CONST; - opline->op2.constant - = zend_add_func_name_literal(CG(active_op_array), &name_node->u.constant TSRMLS_CC); + opline->op2.constant = zend_add_func_name_literal(CG(active_op_array), + Z_STR(name_node->u.constant) TSRMLS_CC); zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); } else { SET_NODE(opline->op2, name_node); @@ -4243,13 +2653,13 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args TSRMLS_DC) /* { } /* }}} */ -static int zend_try_compile_ct_bound_init_user_func(znode *result, zend_ast *name_ast, uint32_t num_args TSRMLS_DC) /* {{{ */ +static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t num_args TSRMLS_DC) /* {{{ */ { zend_string *name, *lcname; zend_function *fbc; zend_op *opline; - if (name_ast->kind != ZEND_AST_CONST || Z_TYPE_P(zend_ast_get_zval(name_ast)) != IS_STRING) { + if (name_ast->kind != ZEND_AST_ZVAL || Z_TYPE_P(zend_ast_get_zval(name_ast)) != IS_STRING) { return FAILURE; } @@ -4266,20 +2676,22 @@ static int zend_try_compile_ct_bound_init_user_func(znode *result, zend_ast *nam } opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, NULL TSRMLS_CC); + opline->extended_value = num_args; + opline->op2_type = IS_CONST; LITERAL_STR(opline->op2, lcname); - opline->extended_value = num_args; + zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); return SUCCESS; } /* }}} */ -static void zend_compile_init_user_func(znode *result, zend_ast *name_ast, uint32_t num_args, zend_string *orig_func_name TSRMLS_DC) /* {{{ */ +static void zend_compile_init_user_func(zend_ast *name_ast, uint32_t num_args, zend_string *orig_func_name TSRMLS_DC) /* {{{ */ { zend_op *opline; znode name_node; - if (zend_try_compile_ct_bound_init_user_func(result, name_ast, num_args TSRMLS_CC) == SUCCESS) { + if (zend_try_compile_ct_bound_init_user_func(name_ast, num_args TSRMLS_CC) == SUCCESS) { return; } @@ -4301,7 +2713,7 @@ int zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcna return FAILURE; } - zend_compile_init_user_func(NULL, args->child[0], 1, lcname TSRMLS_CC); + zend_compile_init_user_func(args->child[0], 1, lcname TSRMLS_CC); zend_compile_expr(&arg_node, args->child[1] TSRMLS_CC); zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL TSRMLS_CC); zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL TSRMLS_CC); @@ -4319,7 +2731,7 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam return FAILURE; } - zend_compile_init_user_func(NULL, args->child[0], args->children - 1, lcname TSRMLS_CC); + zend_compile_init_user_func(args->child[0], args->children - 1, lcname TSRMLS_CC); for (i = 1; i < args->children; ++i) { zend_ast *arg_ast = args->child[i]; znode arg_node; @@ -4469,8 +2881,8 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type TSRMLS } opline->op2_type = IS_CONST; - opline->op2.constant = - zend_add_func_name_literal(CG(active_op_array), &method_node.u.constant TSRMLS_CC); + opline->op2.constant = zend_add_func_name_literal(CG(active_op_array), + Z_STR(method_node.u.constant) TSRMLS_CC); zend_alloc_polymorphic_cache_slot(opline->op2.constant TSRMLS_CC); } else { SET_NODE(opline->op2, &method_node); @@ -4524,8 +2936,8 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type TSRMLS if (method_node.op_type == IS_CONST) { opline->op2_type = IS_CONST; - opline->op2.constant = - zend_add_func_name_literal(CG(active_op_array), &method_node.u.constant TSRMLS_CC); + opline->op2.constant = zend_add_func_name_literal(CG(active_op_array), + Z_STR(method_node.u.constant) TSRMLS_CC); if (opline->op1_type == IS_CONST) { zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); } else { @@ -4551,7 +2963,7 @@ void zend_compile_new(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_class_ref(&class_node, class_ast TSRMLS_CC); opnum = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(result, ZEND_NEW, &class_node, NULL TSRMLS_CC); + zend_emit_op(result, ZEND_NEW, &class_node, NULL TSRMLS_CC); zend_compile_call_common(&ctor_result, args_ast, NULL TSRMLS_CC); zend_do_free(&ctor_result TSRMLS_CC); @@ -4588,7 +3000,8 @@ void zend_compile_global_var(zend_ast *ast TSRMLS_DC) /* {{{ */ } if (zend_try_compile_cv(&result, var_ast TSRMLS_CC) == SUCCESS) { - zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node TSRMLS_CC); + zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node TSRMLS_CC); + zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); } else { zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL TSRMLS_CC); @@ -4804,11 +3217,9 @@ void zend_compile_goto(zend_ast *ast TSRMLS_DC) /* {{{ */ void zend_compile_label(zend_ast *ast TSRMLS_DC) /* {{{ */ { - zval *label = zend_ast_get_zval(ast->child[0]); + zend_string *label = zend_ast_get_str(ast->child[0]); zend_label dest; - ZEND_ASSERT(Z_TYPE_P(label) == IS_STRING); - if (!CG(context).labels) { ALLOC_HASHTABLE(CG(context).labels); zend_hash_init(CG(context).labels, 8, NULL, ptr_dtor, 0); @@ -4817,8 +3228,8 @@ void zend_compile_label(zend_ast *ast TSRMLS_DC) /* {{{ */ dest.brk_cont = CG(context).current_brk_cont; dest.opline_num = get_next_op_number(CG(active_op_array)); - if (!zend_hash_add_mem(CG(context).labels, Z_STR_P(label), &dest, sizeof(zend_label))) { - zend_error_noreturn(E_COMPILE_ERROR, "Label '%s' already defined", Z_STRVAL_P(label)); + if (!zend_hash_add_mem(CG(context).labels, label, &dest, sizeof(zend_label))) { + zend_error_noreturn(E_COMPILE_ERROR, "Label '%s' already defined", label->val); } } /* }}} */ @@ -4935,7 +3346,7 @@ void zend_compile_foreach(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_bool is_variable = zend_is_variable(expr_ast) && !zend_is_call(expr_ast) && zend_can_write_to_variable(expr_ast); - znode expr_node, reset_node, value_node, key_node, dummy_node; + znode expr_node, reset_node, value_node, key_node; zend_op *opline; uint32_t opnum_reset, opnum_fetch; @@ -4958,10 +3369,14 @@ void zend_compile_foreach(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_expr(&expr_node, expr_ast TSRMLS_CC); } + if (by_ref) { + zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W TSRMLS_CC); + } + opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL TSRMLS_CC); if (by_ref && is_variable) { - opline->extended_value = ZEND_FE_RESET_VARIABLE | ZEND_FE_RESET_REFERENCE; // ??? + opline->extended_value = ZEND_FE_FETCH_BYREF; } zend_stack_push(&CG(loop_var_stack), &reset_node); @@ -4989,10 +3404,7 @@ void zend_compile_foreach(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_make_tmp_result(&key_node, opline TSRMLS_CC); } - if (value_ast->attr == ZEND_AST_LIST) { - zend_compile_list_assign(&dummy_node, value_ast, &value_node TSRMLS_CC); - zend_do_free(&dummy_node TSRMLS_CC); - } else if (by_ref) { + if (by_ref) { zend_emit_assign_ref_znode(value_ast, &value_node TSRMLS_CC); } else { zend_emit_assign_znode(value_ast, &value_node TSRMLS_CC); @@ -5091,6 +3503,11 @@ void zend_compile_switch(zend_ast *ast TSRMLS_DC) /* {{{ */ znode cond_node; if (!cond_ast) { + if (has_default_case) { + CG(zend_lineno) = case_ast->lineno; + zend_error_noreturn(E_COMPILE_ERROR, + "Switch statements may only contain one default clause"); + } has_default_case = 1; continue; } @@ -5129,7 +3546,7 @@ void zend_compile_switch(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_end_loop(get_next_op_number(CG(active_op_array)), 1 TSRMLS_CC); if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) { - zend_emit_op(NULL, expr_node.op_type == IS_TMP_VAR ? ZEND_FREE : ZEND_SWITCH_FREE, + zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL TSRMLS_CC); } else if (expr_node.op_type == IS_CONST) { zval_dtor(&expr_node.u.constant); @@ -5518,8 +3935,6 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo } if (op_array->fn_flags & ZEND_ACC_ABSTRACT) { - //zend_op *opline; - if (op_array->fn_flags & ZEND_ACC_PRIVATE) { zend_error_noreturn(E_COMPILE_ERROR, "%s function %s::%s() cannot be declared private", in_interface ? "Interface" : "Abstract", ce->name->val, name->val); @@ -5531,11 +3946,6 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo } ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; - - /*opline = get_next_op(op_array TSRMLS_CC); - opline->opcode = ZEND_RAISE_ABSTRACT_ERROR; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2);*/ } else if (!has_body) { zend_error_noreturn(E_COMPILE_ERROR, "Non-abstract method %s::%s() must contain body", ce->name->val, name->val); @@ -6288,10 +4698,11 @@ void zend_compile_use(zend_ast *ast TSRMLS_DC) /* {{{ */ if (new_name_ast) { new_name = zend_string_copy(zend_ast_get_str(new_name_ast)); } else { - /* The form "use A\B" is eqivalent to "use A\B as B" */ - const char *p = zend_memrchr(old_name->val, '\\', old_name->len); - if (p) { - new_name = zend_string_init(p + 1, old_name->len - (p - old_name->val + 1), 0); + const char *unqualified_name; + size_t unqualified_name_len; + if (zend_get_unqualified_name(old_name, &unqualified_name, &unqualified_name_len)) { + /* The form "use A\B" is eqivalent to "use A\B as B" */ + new_name = zend_string_init(unqualified_name, unqualified_name_len, 0); } else { new_name = zend_string_copy(old_name); @@ -6395,11 +4806,12 @@ void zend_compile_const_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ value_node.op_type = IS_CONST; zend_const_expr_to_zval(value_zv, value_ast TSRMLS_CC); - if (zend_get_ct_const(name, 0 TSRMLS_CC)) { + if (zend_lookup_reserved_const(name->val, name->len TSRMLS_CC)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare constant '%s'", name->val); } name = zend_prefix_with_ns(name TSRMLS_CC); + name = zend_new_interned_string(name TSRMLS_CC); if (CG(current_import_const) && (import_name = zend_hash_find_ptr(CG(current_import_const), name)) @@ -6524,7 +4936,7 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast TSRMLS_DC) ZVAL_LONG(zv, CG(zend_lineno)); break; case T_FILE: - ZVAL_STR(zv, zend_string_copy(CG(compiled_filename))); + ZVAL_STR_COPY(zv, CG(compiled_filename)); break; case T_DIR: { @@ -6547,7 +4959,7 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast TSRMLS_DC) } case T_FUNC_C: if (op_array && op_array->function_name) { - ZVAL_STR(zv, zend_string_copy(op_array->function_name)); + ZVAL_STR_COPY(zv, op_array->function_name); } else { ZVAL_EMPTY_STRING(zv); } @@ -6555,13 +4967,13 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast TSRMLS_DC) case T_METHOD_C: if (ce) { if (op_array && op_array->function_name) { - ZVAL_STR(zv, zend_concat3(ce->name->val, ce->name->len, "::", 2, + ZVAL_NEW_STR(zv, zend_concat3(ce->name->val, ce->name->len, "::", 2, op_array->function_name->val, op_array->function_name->len)); } else { - ZVAL_STR(zv, zend_string_copy(ce->name)); + ZVAL_STR_COPY(zv, ce->name); } } else if (op_array && op_array->function_name) { - ZVAL_STR(zv, zend_string_copy(op_array->function_name)); + ZVAL_STR_COPY(zv, op_array->function_name); } else { ZVAL_EMPTY_STRING(zv); } @@ -6571,7 +4983,7 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast TSRMLS_DC) if (ZEND_CE_IS_TRAIT(ce)) { return 0; } else { - ZVAL_STR(zv, zend_string_copy(ce->name)); + ZVAL_STR_COPY(zv, ce->name); } } else { ZVAL_EMPTY_STRING(zv); @@ -6579,14 +4991,14 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast TSRMLS_DC) break; case T_TRAIT_C: if (ce && ZEND_CE_IS_TRAIT(ce)) { - ZVAL_STR(zv, zend_string_copy(ce->name)); + ZVAL_STR_COPY(zv, ce->name); } else { ZVAL_EMPTY_STRING(zv); } break; case T_NS_C: if (CG(current_namespace)) { - ZVAL_STR(zv, zend_string_copy(CG(current_namespace))); + ZVAL_STR_COPY(zv, CG(current_namespace)); } else { ZVAL_EMPTY_STRING(zv); } @@ -6851,7 +5263,7 @@ void zend_compile_cast(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_expr(&expr_node, expr_ast TSRMLS_CC); - opline = zend_emit_op(result, ZEND_CAST, &expr_node, NULL TSRMLS_CC); + opline = zend_emit_op_tmp(result, ZEND_CAST, &expr_node, NULL TSRMLS_CC); opline->extended_value = ast->attr; } /* }}} */ @@ -6876,17 +5288,7 @@ static void zend_compile_shorthand_conditional(znode *result, zend_ast *ast TSRM opline_jmp_set = &CG(active_op_array)->opcodes[opnum_jmp_set]; opline_jmp_set->op2.opline_num = get_next_op_number(CG(active_op_array)) + 1; - if (cond_node.op_type == IS_VAR || cond_node.op_type == IS_CV - || false_node.op_type == IS_VAR || false_node.op_type == IS_CV - ) { - opline_jmp_set->opcode = ZEND_JMP_SET_VAR; - opline_jmp_set->result_type = IS_VAR; - GET_NODE(result, opline_jmp_set->result); - - opline_qm_assign = zend_emit_op(NULL, ZEND_QM_ASSIGN_VAR, &false_node, NULL TSRMLS_CC); - } else { - opline_qm_assign = zend_emit_op(NULL, ZEND_QM_ASSIGN, &false_node, NULL TSRMLS_CC); - } + opline_qm_assign = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &false_node, NULL TSRMLS_CC); SET_NODE(opline_qm_assign->result, result); } /* }}} */ @@ -6913,7 +5315,7 @@ void zend_compile_conditional(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_expr(&true_node, true_ast TSRMLS_CC); opnum_qm_assign1 = get_next_op_number(CG(active_op_array)); - zend_emit_op(result, ZEND_QM_ASSIGN, &true_node, NULL TSRMLS_CC); + zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &true_node, NULL TSRMLS_CC); opnum_jmp = zend_emit_jump(0 TSRMLS_CC); @@ -6922,14 +5324,6 @@ void zend_compile_conditional(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ zend_compile_expr(&false_node, false_ast TSRMLS_CC); opline_qm_assign1 = &CG(active_op_array)->opcodes[opnum_qm_assign1]; - if (true_node.op_type == IS_VAR || true_node.op_type == IS_CV - || false_node.op_type == IS_VAR || false_node.op_type == IS_CV - ) { - opline_qm_assign1->opcode = ZEND_QM_ASSIGN_VAR; - opline_qm_assign1->result_type = IS_VAR; - GET_NODE(result, opline_qm_assign1->result); - } - opline_qm_assign2 = zend_emit_op(NULL, opline_qm_assign1->opcode, &false_node, NULL TSRMLS_CC); SET_NODE(opline_qm_assign2->result, result); @@ -6937,6 +5331,30 @@ void zend_compile_conditional(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ } /* }}} */ +void zend_compile_coalesce(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ +{ + zend_ast *expr_ast = ast->child[0]; + zend_ast *default_ast = ast->child[1]; + + znode expr_node, default_node; + zend_op *opline; + uint32_t opnum; + + zend_compile_var(&expr_node, expr_ast, BP_VAR_IS TSRMLS_CC); + + opnum = get_next_op_number(CG(active_op_array)); + zend_emit_op_tmp(result, ZEND_COALESCE, &expr_node, NULL TSRMLS_CC); + + zend_compile_expr(&default_node, default_ast TSRMLS_CC); + + opline = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &default_node, NULL TSRMLS_CC); + SET_NODE(opline->result, result); + + opline = &CG(active_op_array)->opcodes[opnum]; + opline->op2.opline_num = get_next_op_number(CG(active_op_array)); +} +/* }}} */ + void zend_compile_print(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; @@ -7141,7 +5559,7 @@ void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_op *opline; - uint32_t i, opnum_init; + uint32_t i, opnum_init = -1; zend_bool packed = 1; if (zend_try_ct_eval_array(&result->u.constant, ast TSRMLS_CC)) { @@ -7149,8 +5567,6 @@ void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ return; } - opnum_init = get_next_op_number(CG(active_op_array)); - for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; zend_ast *value_ast = elem_ast->child[0]; @@ -7173,6 +5589,7 @@ void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ } if (i == 0) { + opnum_init = get_next_op_number(CG(active_op_array)); opline = zend_emit_op_tmp(result, ZEND_INIT_ARRAY, &value_node, key_node_ptr TSRMLS_CC); opline->extended_value = list->children << ZEND_ARRAY_SIZE_SHIFT; } else { @@ -7194,6 +5611,7 @@ void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ /* Add a flag to INIT_ARRAY if we know this array cannot be packed */ if (!packed) { + ZEND_ASSERT(opnum_init != -1); opline = &CG(active_op_array)->opcodes[opnum_init]; opline->extended_value |= ZEND_ARRAY_NOT_PACKED; } @@ -7203,17 +5621,17 @@ void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ void zend_compile_const(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { zend_ast *name_ast = ast->child[0]; - zend_string *orig_name = zend_ast_get_str(name_ast); - zend_bool is_fully_qualified; - zval resolved_name; zend_op *opline; - ZVAL_STR(&resolved_name, zend_resolve_const_name( - orig_name, name_ast->attr, &is_fully_qualified TSRMLS_CC)); + zend_bool is_fully_qualified; + zend_string *orig_name = zend_ast_get_str(name_ast); + zend_string *resolved_name = zend_resolve_const_name( + orig_name, name_ast->attr, &is_fully_qualified TSRMLS_CC); - if (zend_constant_ct_subst(result, &resolved_name, 1 TSRMLS_CC)) { - zval_dtor(&resolved_name); + if (zend_try_ct_eval_const(&result->u.constant, resolved_name, is_fully_qualified TSRMLS_CC)) { + result->op_type = IS_CONST; + zend_string_release(resolved_name); return; } @@ -7222,16 +5640,16 @@ void zend_compile_const(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ if (is_fully_qualified) { opline->op2.constant = zend_add_const_name_literal( - CG(active_op_array), &resolved_name, 0 TSRMLS_CC); + CG(active_op_array), resolved_name, 0 TSRMLS_CC); } else { opline->extended_value = IS_CONSTANT_UNQUALIFIED; if (CG(current_namespace)) { opline->extended_value |= IS_CONSTANT_IN_NAMESPACE; opline->op2.constant = zend_add_const_name_literal( - CG(active_op_array), &resolved_name, 1 TSRMLS_CC); + CG(active_op_array), resolved_name, 1 TSRMLS_CC); } else { opline->op2.constant = zend_add_const_name_literal( - CG(active_op_array), &resolved_name, 0 TSRMLS_CC); + CG(active_op_array), resolved_name, 0 TSRMLS_CC); } } zend_alloc_cache_slot(opline->op2.constant TSRMLS_CC); @@ -7279,7 +5697,7 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast TSRMLS_DC) /* "Cannot access self::class when no class scope is active"); } result->op_type = IS_CONST; - ZVAL_STR(&result->u.constant, zend_string_copy(CG(active_class_entry)->name)); + ZVAL_STR_COPY(&result->u.constant, CG(active_class_entry)->name); break; case ZEND_FETCH_CLASS_STATIC: case ZEND_FETCH_CLASS_PARENT: @@ -7402,7 +5820,7 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr TSRMLS_DC) /* {{{ */ zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; zend_ast *const_ast = ast->child[1]; - zend_string *class_name = zend_ast_get_str(class_ast); + zend_string *class_name; zend_string *const_name = zend_ast_get_str(const_ast); zval result; int fetch_type; @@ -7412,6 +5830,7 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr TSRMLS_DC) /* {{{ */ "Dynamic class names are not allowed in compile-time class constant references"); } + class_name = zend_ast_get_str(class_ast); fetch_type = zend_get_class_fetch_type(class_name); if (ZEND_FETCH_CLASS_STATIC == fetch_type) { @@ -7429,9 +5848,6 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr TSRMLS_DC) /* {{{ */ class_name->val, class_name->len, "::", 2, const_name->val, const_name->len); Z_TYPE_INFO(result) = IS_CONSTANT_EX; - if (IS_INTERNED(Z_STR(result))) { - Z_TYPE_FLAGS(result) &= ~ (IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE); - } Z_CONST_FLAGS(result) = fetch_type; zend_ast_destroy(ast); @@ -7445,21 +5861,20 @@ void zend_compile_const_expr_const(zend_ast **ast_ptr TSRMLS_DC) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *name_ast = ast->child[0]; - zval *orig_name = zend_ast_get_zval(name_ast); + zend_string *orig_name = zend_ast_get_str(name_ast); zend_bool is_fully_qualified; - znode result; - zval resolved_name; + zval result, resolved_name; + ZVAL_STR(&resolved_name, zend_resolve_const_name( + orig_name, name_ast->attr, &is_fully_qualified TSRMLS_CC)); - if (zend_constant_ct_subst(&result, orig_name, 0 TSRMLS_CC)) { + if (zend_try_ct_eval_const(&result, Z_STR(resolved_name), is_fully_qualified TSRMLS_CC)) { + zend_string_release(Z_STR(resolved_name)); zend_ast_destroy(ast); - *ast_ptr = zend_ast_create_zval(&result.u.constant); + *ast_ptr = zend_ast_create_zval(&result); return; } - ZVAL_STR(&resolved_name, zend_resolve_const_name( - Z_STR_P(orig_name), name_ast->attr, &is_fully_qualified TSRMLS_CC)); - Z_TYPE_INFO(resolved_name) = IS_CONSTANT_EX; if (!is_fully_qualified) { Z_CONST_FLAGS(resolved_name) = IS_CONSTANT_UNQUALIFIED; @@ -7483,7 +5898,7 @@ void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr TSRMLS_DC) /* zend_error_noreturn(E_COMPILE_ERROR, "Cannot access self::class when no class scope is active"); } - ZVAL_STR(&result, zend_string_copy(CG(active_class_entry)->name)); + ZVAL_STR_COPY(&result, CG(active_class_entry)->name); break; case ZEND_FETCH_CLASS_STATIC: case ZEND_FETCH_CLASS_PARENT: @@ -7774,6 +6189,9 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ case ZEND_AST_CONDITIONAL: zend_compile_conditional(result, ast TSRMLS_CC); return; + case ZEND_AST_COALESCE: + zend_compile_coalesce(result, ast TSRMLS_CC); + return; case ZEND_AST_PRINT: zend_compile_print(result, ast TSRMLS_CC); return; @@ -7940,15 +6358,20 @@ void zend_eval_const_expr(zend_ast **ast_ptr TSRMLS_DC) /* {{{ */ return; } break; - case ZEND_AST_CONST: { - znode result_node; + case ZEND_AST_CONST: + { + zend_ast *name_ast = ast->child[0]; + zend_bool is_fully_qualified; + zend_string *resolved_name = zend_resolve_const_name( + zend_ast_get_str(name_ast), name_ast->attr, &is_fully_qualified TSRMLS_CC); - if (!zend_constant_ct_subst(&result_node, zend_ast_get_zval(ast->child[0]), 0 TSRMLS_CC)) { + if (!zend_try_ct_eval_const(&result, resolved_name, is_fully_qualified TSRMLS_CC)) { + zend_string_release(resolved_name); return; } - zend_ast_destroy(ast); - *ast_ptr = zend_ast_create_zval(&result_node.u.constant); - return; + + zend_string_release(resolved_name); + break; } default: return; |