diff --git a/.gdbinit b/.gdbinit index d3b456239e375..150f7aa3575d6 100644 --- a/.gdbinit +++ b/.gdbinit @@ -208,7 +208,7 @@ define ____printzv_contents set $handle = $zvalue->value.obj.handle set $handlers = $zvalue->value.obj.handlers set $zobj = $zvalue->value.obj - set $cname = $zobj->ce->name->val + set $cname = $zobj->cr->ce->name->val printf "(%s) #%d", $cname, $handle if ! $arg1 if $handlers->get_properties == &zend_std_get_properties @@ -225,7 +225,7 @@ define ____printzv_contents end else printf " {\n" - set $ht = &$zobj->ce->properties_info + set $ht = &$zobj->cr->ce->properties_info set $k = 0 set $num = $ht->nNumUsed while $k < $num @@ -653,3 +653,8 @@ document lookup_root lookup a refcounted in root usage: lookup_root [ptr]. end + +define print_opcode + set $opcode = $arg0 + print zend_get_opcode_name($opcode) +end diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index 9248f0b822441..c984407d470e1 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -24,8 +24,11 @@ #include "Optimizer/zend_optimizer.h" #include "Optimizer/zend_optimizer_internal.h" #include "zend_API.h" +#include "zend_compile.h" #include "zend_constants.h" #include "zend_execute.h" +#include "zend_string.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_extensions.h" @@ -35,6 +38,9 @@ #define LITERAL_STATIC_METHOD 2 #define LITERAL_STATIC_PROPERTY 3 +// TODO: temp hack +#define LITERAL_PNR_AND_KEY (1<<7) + typedef struct _literal_info { uint8_t num_related; } literal_info; @@ -245,7 +251,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx break; case ZEND_NEW: if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, 2); + LITERAL_INFO(opline->op1.constant, 2 | LITERAL_PNR_AND_KEY); } break; case ZEND_DECLARE_CLASS: @@ -312,6 +318,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx memset(map, 0, op_array->last_literal * sizeof(int)); for (i = 0; i < op_array->last_literal; i++) { if (!info[i].num_related) { + // TODO: temp hack + if (Z_TYPE(op_array->literals[i]) == IS_PNR) { + zend_pnr_destroy(Z_PNR(op_array->literals[i])); + } /* unset literal */ zval_ptr_dtor_nogc(&op_array->literals[i]); continue; @@ -443,6 +453,67 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; } + case IS_PNR: { + ZEND_ASSERT(info[i].num_related & LITERAL_PNR_AND_KEY); + ZEND_ASSERT(Z_TYPE(op_array->literals[i+1]) == IS_STRING); + + uint8_t num_related = info[i].num_related & ~LITERAL_PNR_AND_KEY; + ZEND_ASSERT(num_related == 2); + + zend_packed_name_reference pnr = Z_PNR(op_array->literals[i]); + zend_string *base_key = Z_STR(op_array->literals[i+1]); + zend_string *key; + if (ZEND_PNR_IS_COMPLEX(pnr)) { + key = ZEND_PNR_COMPLEX_GET_KEY(pnr); + } else { + key = ZEND_PNR_SIMPLE_GET_NAME(pnr); + } + key = zend_string_concat2( + ZSTR_VAL(key), ZSTR_LEN(key)+1, + ZSTR_VAL(base_key), ZSTR_LEN(base_key)); + bias_key(key, 300 + num_related - 1); + + if ((pos = zend_hash_find(&hash, key)) != NULL) { + ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_PNR && + Z_TYPE(op_array->literals[Z_LVAL_P(pos)+1]) == IS_STRING && + info[i].num_related == info[Z_LVAL_P(pos)].num_related); + zend_string_release_ex(key, 0); + map[i] = Z_LVAL_P(pos); + zend_packed_name_reference pnr2 = Z_PNR(op_array->literals[i]); + if (ZEND_PNR_IS_SIMPLE(pnr2)) { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr2)); + } else { + zend_name_reference *class_ref = ZEND_PNR_COMPLEX_GET_REF(pnr2); + zend_string_release(class_ref->name); + zend_string_release(class_ref->key); + } + n = num_related; + while (n > 1) { + i++; + zval_ptr_dtor_nogc(&op_array->literals[i]); + n--; + } + } else { + map[i] = j; + ZVAL_LONG(&zv, j); + zend_hash_add_new(&hash, key, &zv); + zend_string_release_ex(key, 0); + if (i != j) { + op_array->literals[j] = op_array->literals[i]; + info[j] = info[i]; + } + j++; + n = num_related; + while (n > 1) { + i++; + if (i != j) op_array->literals[j] = op_array->literals[i]; + j++; + n--; + } + } + + break; + } case IS_ARRAY: ZEND_ASSERT(info[i].num_related == 1); if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) { diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index b1f568da5d920..93a7247dd5d7a 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -29,6 +29,7 @@ #include "zend_call_graph.h" #include "zend_inference.h" #include "zend_dump.h" +#include "zend_scc.h" #ifndef ZEND_DEBUG_DFA # define ZEND_DEBUG_DFA ZEND_DEBUG @@ -278,8 +279,8 @@ static inline bool can_elide_list_type( ZEND_ASSERT(!is_intersection); return can_elide_list_type(script, op_array, use_info, *single_type); } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(*single_type)); zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); zend_string_release(lcname); bool result = ce && safe_instanceof(use_info->ce, ce); diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index b7c0a5ec4466a..3aa83a9468d6d 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -164,7 +164,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i /* These flags will always cause an exception */ ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT; - if (ce && !ce->parent && !ce->create_object && !ce->constructor && + if (ce && !ce->num_parents && !ce->create_object && !ce->constructor && !ce->destructor && !ce->__get && !ce->__set && !(ce->ce_flags & forbidden_flags) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -228,7 +228,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1( script, op_array, opline); if (ce && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && !ce->parent) { + !ce->destructor && !ce->__get && !ce->__set && !ce->num_parents) { return 1; } break; diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c index b525f8d5ee226..8ffc805eeb207 100644 --- a/Zend/Optimizer/ssa_integrity.c +++ b/Zend/Optimizer/ssa_integrity.c @@ -123,7 +123,6 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex for (i = 0; i < ssa->vars_count; i++) { zend_ssa_var *var = &ssa->vars[i]; int use; - uint32_t type = ssa->var_info[i].type; if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) { if (var->use_chain >= 0) { @@ -192,14 +191,17 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex } } FOREACH_PHI_USE_END(); - if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { - FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); - } - if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { - FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); - } - if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { - FAIL("var " VARFMT " may be ref but has ce\n", VAR(i)); + if (ssa->var_info) { + uint32_t type = ssa->var_info[i].type; + if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { + FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); + } + if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { + FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); + } + if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { + FAIL("var " VARFMT " may be ref but has ce\n", VAR(i)); + } } } diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h index 93d455060686e..75e46bc32bbe9 100644 --- a/Zend/Optimizer/zend_cfg.h +++ b/Zend/Optimizer/zend_cfg.h @@ -99,6 +99,11 @@ typedef struct _zend_cfg { #define ZEND_CFG_RECV_ENTRY (1<<24) #define ZEND_CALL_TREE (1<<23) #define ZEND_SSA_USE_CV_RESULTS (1<<22) +/* Chain function call opcodes by adding fake defs and uses. E.g. in a sequence + * INIT_FCALL->SEND->SEND->DO_FCALL, each element defines an SSA var that is + * used by the next one. This can be used to ensure that a worklist algorithm + * would visit DO_FCALL if any of the previous op codes were visited. */ +#define ZEND_SSA_CALL_CHAINS (1<<21) #define CRT_CONSTANT_EX(op_array, opline, node) \ (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \ diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 1001a935d80a4..12945a3ae76df 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -23,7 +23,9 @@ #include "zend_func_info.h" #include "zend_call_graph.h" #include "zend_dump.h" +#include "zend_smart_str.h" #include "ext/standard/php_string.h" +#include "zend_types.h" void zend_dump_ht(HashTable *ht) { @@ -76,6 +78,39 @@ void zend_dump_const(const zval *zv) case IS_ARRAY: fprintf(stderr, " array(...)"); break; + case IS_PNR: { + zend_packed_name_reference pnr = Z_PNR_P(zv); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(pnr, name, type_args); + + smart_str str = {0}; + smart_str_append(&str, name); + + if (type_args != &zend_empty_type_list) { + smart_str_appendc(&str, '<'); + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH((zend_type_list*)type_args, single_type) { + if (single_type != type_args->types) { + smart_str_appendc(&str, ','); + } + zend_string *type_str = zend_type_to_string(*single_type, NULL); + smart_str_append(&str, type_str); + zend_string_release(type_str); + } ZEND_TYPE_LIST_FOREACH_END(); + smart_str_appendc(&str, '>'); + } + + name = smart_str_extract(&str); + zend_string *escaped_string = php_addcslashes(name, "\0..\37\"\\\177..\377", 2); + zend_string_release(name); + + fprintf(stderr, " pnr(\"%s\")", ZSTR_VAL(escaped_string)); + + zend_string_release(escaped_string); + + break; + } default: fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv)); break; @@ -373,7 +408,11 @@ ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *s } else { fprintf(stderr, "#?."); } - zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num); + if (var_num >= 0) { + zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num); + } else { + fprintf(stderr, "?"); + } if (ssa_var_num >= 0 && ssa->vars) { if (ssa->vars[ssa_var_num].no_val) { @@ -446,13 +485,14 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block uint32_t n = 0; if (!ssa_op || ssa_op->result_use < 0) { - if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op && ssa_op->result_def >= 0) { - int ssa_var_num = ssa_op->result_def; - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags); - } else { - zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); - } + if (ssa_op && ssa_op->result_def >= 0) { + int ssa_var_num = ssa_op->result_def; + zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, + (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) ? EX_VAR_TO_NUM(opline->result.var) : -1, + dump_flags); + fprintf(stderr, " = "); + } else if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { + zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); fprintf(stderr, " = "); } } diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index f60f1f09a4b49..47be51031026d 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -60,40 +60,6 @@ #define LOG_NEG_RANGE(...) #endif -/* Pop elements in unspecified order from worklist until it is empty */ -#define WHILE_WORKLIST(worklist, len, i) do { \ - bool _done = 0; \ - while (!_done) { \ - _done = 1; \ - ZEND_BITSET_FOREACH(worklist, len, i) { \ - zend_bitset_excl(worklist, i); \ - _done = 0; - -#define WHILE_WORKLIST_END() \ - } ZEND_BITSET_FOREACH_END(); \ - } \ -} while (0) - -#define CHECK_SCC_VAR(var2) \ - do { \ - if (!ssa->vars[var2].no_val) { \ - if (ssa->vars[var2].scc < 0) { \ - zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ - } \ - if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ - ssa->vars[var].scc = ssa->vars[var2].scc; \ - is_root = 0; \ - } \ - } \ - } while (0) - -#define CHECK_SCC_ENTRY(var2) \ - do { \ - if (ssa->vars[var2].scc != ssa->vars[var].scc) { \ - ssa->vars[var2].scc_entry = 1; \ - } \ - } while (0) - #define ADD_SCC_VAR(_var) \ do { \ if (ssa->vars[_var].scc == scc && \ @@ -111,56 +77,19 @@ } \ } while (0) -#define FOR_EACH_DEFINED_VAR(line, MACRO) \ - do { \ - if (ssa->ops[line].op1_def >= 0) { \ - MACRO(ssa->ops[line].op1_def); \ - } \ - if (ssa->ops[line].op2_def >= 0) { \ - MACRO(ssa->ops[line].op2_def); \ - } \ - if (ssa->ops[line].result_def >= 0) { \ - MACRO(ssa->ops[line].result_def); \ - } \ - if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line-1].op1_def >= 0) { \ - MACRO(ssa->ops[line-1].op1_def); \ - } \ - if (ssa->ops[line-1].op2_def >= 0) { \ - MACRO(ssa->ops[line-1].op2_def); \ - } \ - if (ssa->ops[line-1].result_def >= 0) { \ - MACRO(ssa->ops[line-1].result_def); \ - } \ - } else if ((uint32_t)line+1 < op_array->last && \ - op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line+1].op1_def >= 0) { \ - MACRO(ssa->ops[line+1].op1_def); \ - } \ - if (ssa->ops[line+1].op2_def >= 0) { \ - MACRO(ssa->ops[line+1].op2_def); \ - } \ - if (ssa->ops[line+1].result_def >= 0) { \ - MACRO(ssa->ops[line+1].result_def); \ - } \ - } \ - } while (0) - +/* Pop elements in unspecified order from worklist until it is empty */ +#define WHILE_WORKLIST(worklist, len, i) do { \ + bool _done = 0; \ + while (!_done) { \ + _done = 1; \ + ZEND_BITSET_FOREACH(worklist, len, i) { \ + zend_bitset_excl(worklist, i); \ + _done = 0; -#define FOR_EACH_VAR_USAGE(_var, MACRO) \ - do { \ - zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ - int use = ssa->vars[_var].use_chain; \ - while (use >= 0) { \ - FOR_EACH_DEFINED_VAR(use, MACRO); \ - use = zend_ssa_next_use(ssa->ops, _var, use); \ - } \ - p = ssa->vars[_var].phi_use_chain; \ - while (p) { \ - MACRO(p->ssa_var); \ - p = zend_ssa_next_use_phi(ssa, _var, p); \ - } \ - } while (0) +#define WHILE_WORKLIST_END() \ + } ZEND_BITSET_FOREACH_END(); \ + } \ +} while (0) static inline bool add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b) @@ -173,320 +102,6 @@ static inline bool sub_will_overflow(zend_long a, zend_long b) { } #endif -#if 0 -/* Recursive Pearce's SCC algorithm implementation */ -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ -{ - int is_root = 1; -#ifdef SYM_RANGE - zend_ssa_phi *p; -#endif - - ssa->vars[var].scc = *index; - (*index)++; - - FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); - -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - p = ssa->vars[var].sym_use_chain; - while (p) { - CHECK_SCC_VAR(p->ssa_var); - p = p->sym_use_chain; - } -#endif - - if (is_root) { - ssa->sccs--; - while (stack->len > 0) { - int var2 = zend_worklist_stack_peek(stack); - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - break; - } - zend_worklist_stack_pop(stack); - ssa->vars[var2].scc = ssa->sccs; - (*index)--; - } - ssa->vars[var].scc = ssa->sccs; - ssa->vars[var].scc_entry = 1; - (*index)--; - } else { - zend_worklist_stack_push(stack, var); - } -} -/* }}} */ - -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int index = 0; - zend_worklist_stack stack; - int j; - ALLOCA_FLAG(stack_use_heap) - - ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - - /* Find SCCs using Pearce's algorithm. */ - ssa->sccs = ssa->vars_count; - for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); - } - } - - if (ssa->sccs) { - /* Shift SCC indexes. */ - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc -= ssa->sccs; - } - } - } - ssa->sccs = ssa->vars_count - ssa->sccs; - - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - int var = j; - FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); - } - } - - ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); -} -/* }}} */ - -#else -/* Iterative Pearce's SCC algorithm implementation */ - -typedef struct _zend_scc_iterator { - int state; - int last; - union { - int use; - zend_ssa_phi *phi; - }; -} zend_scc_iterator; - -static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ -{ - zend_ssa_phi *phi; - int use, var2; - - switch (iterator->state) { - case 0: goto state_0; - case 1: use = iterator->use; goto state_1; - case 2: use = iterator->use; goto state_2; - case 3: use = iterator->use; goto state_3; - case 4: use = iterator->use; goto state_4; - case 5: use = iterator->use; goto state_5; - case 6: use = iterator->use; goto state_6; - case 7: use = iterator->use; goto state_7; - case 8: use = iterator->use; goto state_8; - case 9: phi = iterator->phi; goto state_9; -#ifdef SYM_RANGE - case 10: phi = iterator->phi; goto state_10; -#endif - case 11: goto state_11; - } - -state_0: - use = ssa->vars[var].use_chain; - while (use >= 0) { - iterator->use = use; - var2 = ssa->ops[use].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 1; - return var2; - } -state_1: - var2 = ssa->ops[use].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 2; - return var2; - } -state_2: - var2 = ssa->ops[use].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 3; - return var2; - } -state_3: - if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { - var2 = ssa->ops[use-1].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 4; - return var2; - } -state_4: - var2 = ssa->ops[use-1].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 5; - return var2; - } -state_5: - var2 = ssa->ops[use-1].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 8; - return var2; - } - } else if ((uint32_t)use+1 < op_array->last && - op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { - var2 = ssa->ops[use+1].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 6; - return var2; - } -state_6: - var2 = ssa->ops[use+1].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 7; - return var2; - } -state_7: - var2 = ssa->ops[use+1].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 8; - return var2; - } - } -state_8: - use = zend_ssa_next_use(ssa->ops, var, use); - } - - phi = ssa->vars[var].phi_use_chain; - while (phi) { - var2 = phi->ssa_var; - if (!ssa->vars[var2].no_val) { - iterator->state = 9; - iterator->phi = phi; - return var2; - } -state_9: - phi = zend_ssa_next_use_phi(ssa, var, phi); - } - -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - phi = ssa->vars[var].sym_use_chain; - while (phi) { - var2 = phi->ssa_var; - if (!ssa->vars[var2].no_val) { - iterator->state = 10; - iterator->phi = phi; - return var2; - } -state_10: - phi = phi->sym_use_chain; - } -#endif - - iterator->state = 11; -state_11: - return -1; -} -/* }}} */ - -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */ -{ -restart: - zend_worklist_stack_push(vstack, var); - iterators[var].state = 0; - iterators[var].last = -1; - ssa->vars[var].scc_entry = 1; - ssa->vars[var].scc = *index; - (*index)++; - - while (vstack->len > 0) { - var = zend_worklist_stack_peek(vstack); - while (1) { - int var2; - - if (iterators[var].last >= 0) { - /* finish edge */ - var2 = iterators[var].last; - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - ssa->vars[var].scc = ssa->vars[var2].scc; - ssa->vars[var].scc_entry = 0; - } - } - var2 = zend_scc_next(op_array, ssa, var, iterators + var); - iterators[var].last = var2; - if (var2 < 0) break; - /* begin edge */ - if (ssa->vars[var2].scc < 0) { - var = var2; - goto restart; - } - } - - /* finish visiting */ - zend_worklist_stack_pop(vstack); - if (ssa->vars[var].scc_entry) { - ssa->sccs--; - while (stack->len > 0) { - int var2 = zend_worklist_stack_peek(stack); - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - break; - } - zend_worklist_stack_pop(stack); - ssa->vars[var2].scc = ssa->sccs; - (*index)--; - } - ssa->vars[var].scc = ssa->sccs; - (*index)--; - } else { - zend_worklist_stack_push(stack, var); - } - } -} -/* }}} */ - -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int index = 0; - zend_worklist_stack stack, vstack; - zend_scc_iterator *iterators; - int j; - ALLOCA_FLAG(stack_use_heap) - ALLOCA_FLAG(vstack_use_heap) - ALLOCA_FLAG(iterators_use_heap) - - iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); - ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); - ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - - /* Find SCCs using Pearce's algorithm. */ - ssa->sccs = ssa->vars_count; - for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); - } - } - - if (ssa->sccs) { - /* Shift SCC indexes. */ - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc -= ssa->sccs; - } - } - } - ssa->sccs = ssa->vars_count - ssa->sccs; - - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - int var = j; - FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); - } - } - - ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); - ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); - free_alloca(iterators, iterators_use_heap); -} -/* }}} */ - -#endif - ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { zend_ssa_var *ssa_vars = ssa->vars; @@ -2385,8 +2000,8 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen if (pce) { /* As we only have space to store one CE, * we use a plain object type for class unions. */ - if (ZEND_TYPE_HAS_NAME(type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(type)); + if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(type)); // TODO: Pass through op_array. *pce = zend_optimizer_get_class_entry(script, NULL, lcname); zend_string_release_ex(lcname, 0); @@ -2470,7 +2085,7 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script * break; case ZEND_FETCH_CLASS_PARENT: if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - ce = op_array->scope->parent; + ce = op_array->scope->parents[0]->ce; } break; } @@ -3341,8 +2956,8 @@ static zend_always_inline zend_result _zend_update_type_info( } break; case ZEND_FETCH_CLASS_PARENT: - if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def); + if (op_array->scope && op_array->scope->parents[0]->ce && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { + UPDATE_SSA_OBJ_TYPE(op_array->scope->parents[0]->ce, 0, ssa_op->result_def); } else { UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); } @@ -3992,17 +3607,6 @@ ZEND_API zend_result zend_update_type_info( return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0); } -static uint32_t get_class_entry_rank(zend_class_entry *ce) { - uint32_t rank = 0; - if (ce->ce_flags & ZEND_ACC_LINKED) { - while (ce->parent) { - rank++; - ce = ce->parent; - } - } - return rank; -} - /* Compute least common ancestor on class inheritance tree only */ static zend_class_entry *join_class_entries( zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) { @@ -4014,22 +3618,20 @@ static zend_class_entry *join_class_entries( return NULL; } - rank1 = get_class_entry_rank(ce1); - rank2 = get_class_entry_rank(ce2); - - while (rank1 != rank2) { - if (rank1 > rank2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - rank1--; - } else { - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; - rank2--; - } + if (ce1->num_parents > ce2->num_parents) { + rank1 = ce1->num_parents - ce2->num_parents; + rank2 = 0; + } else { + rank1 = 0; + rank2 = ce2->num_parents - ce1->num_parents; } + ce1 = rank1 > 0 ? ce1->parents[rank1-1]->ce : ce1; + ce2 = rank2 > 0 ? ce2->parents[rank2-1]->ce : ce2; + while (ce1 != ce2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; + ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) || !ce1->num_parents ? NULL : ce1->parents[0]->ce; + ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) || !ce2->num_parents ? NULL : ce2->parents[0]->ce; } if (ce1) { @@ -5070,7 +4672,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op const zend_class_entry *ce = var_info->ce; if (var_info->is_instanceof || - !ce || ce->create_object || ce->__get || ce->__set || ce->parent) { + !ce || ce->create_object || ce->__get || ce->__set || ce->num_parents) { return 1; } diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index ffcf4f0ae1379..8279a7292be6b 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -148,6 +148,8 @@ END_EXTERN_C() static zend_always_inline uint32_t _const_op_type(const zval *zv) { if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY; + } else if (Z_TYPE_P(zv) == IS_PNR) { + return MAY_BE_STRING; } else if (Z_TYPE_P(zv) == IS_ARRAY) { return zend_array_type_info(zv); } else { @@ -218,7 +220,6 @@ static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) BEGIN_EXTERN_C() ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa); -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); ZEND_API zend_result zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level); ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, int write, int insert); diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index cf1c98c64bb59..61b69d949a888 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -24,6 +24,7 @@ #include "zend_API.h" #include "zend_constants.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_cfg.h" #include "zend_func_info.h" @@ -323,11 +324,13 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array, zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); break; case ZEND_NEW: - REQUIRES_STRING(val); - drop_leading_backslash(val); + if (Z_TYPE_P(val) != IS_PNR) { + return 0; + } + // TODO drop_leading_backslash(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); opline->op2.num = alloc_cache_slots(op_array, 1); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); + zend_optimizer_add_literal_string(op_array, zend_string_tolower(ZEND_PNR_GET_NAME(Z_PNR_P(val)))); break; case ZEND_INIT_STATIC_METHOD_CALL: REQUIRES_STRING(val); @@ -816,9 +819,9 @@ zend_class_entry *zend_optimizer_get_class_entry_from_op1( const zend_script *script, const zend_op_array *op_array, const zend_op *opline) { if (opline->op1_type == IS_CONST) { zval *op1 = CRT_CONSTANT(opline->op1); - if (Z_TYPE_P(op1) == IS_STRING) { - return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); - } + ZEND_ASSERT(Z_TYPE_P(op1) == IS_PNR || Z_TYPE_P(op1) == IS_STRING); + ZEND_ASSERT(Z_TYPE_P(op1 + 1) == IS_STRING); + return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); } else if (opline->op1_type == IS_UNUSED && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { @@ -1075,7 +1078,7 @@ static void zend_optimize(zend_op_array *op_array, } } -static void zend_revert_pass_two(zend_op_array *op_array) +void zend_revert_pass_two(zend_op_array *op_array) { zend_op *opline, *end; @@ -1107,7 +1110,7 @@ static void zend_revert_pass_two(zend_op_array *op_array) op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO; } -static void zend_redo_pass_two(zend_op_array *op_array) +void zend_redo_pass_two(zend_op_array *op_array) { zend_op *opline, *end; #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR diff --git a/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h index 16bfd75520d89..9a5accb6b42cd 100644 --- a/Zend/Optimizer/zend_optimizer.h +++ b/Zend/Optimizer/zend_optimizer.h @@ -98,6 +98,8 @@ ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass); ZEND_API void zend_optimizer_unregister_pass(int idx); zend_result zend_optimizer_startup(void); zend_result zend_optimizer_shutdown(void); +void zend_revert_pass_two(zend_op_array *op_array); +void zend_redo_pass_two(zend_op_array *op_array); END_EXTERN_C() #endif diff --git a/Zend/Optimizer/zend_scc.c b/Zend/Optimizer/zend_scc.c new file mode 100644 index 0000000000000..683bdccbdf6ce --- /dev/null +++ b/Zend/Optimizer/zend_scc.c @@ -0,0 +1,374 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_compile.h" +#include "zend_worklist.h" +#include "zend_optimizer_internal.h" + +#define CHECK_SCC_VAR(var2) \ + do { \ + if (!ssa->vars[var2].no_val) { \ + if (ssa->vars[var2].scc < 0) { \ + zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ + } \ + if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ + ssa->vars[var].scc = ssa->vars[var2].scc; \ + is_root = 0; \ + } \ + } \ + } while (0) + +#define CHECK_SCC_ENTRY(var2) \ + do { \ + if (ssa->vars[var2].scc != ssa->vars[var].scc) { \ + ssa->vars[var2].scc_entry = 1; \ + } \ + } while (0) + +#define ADD_SCC_VAR(_var) \ + do { \ + if (ssa->vars[_var].scc == scc && \ + !(ssa->var_info[_var].type & MAY_BE_REF)) { \ + zend_bitset_incl(worklist, _var); \ + } \ + } while (0) + +#define ADD_SCC_VAR_1(_var) \ + do { \ + if (ssa->vars[_var].scc == scc && \ + !(ssa->var_info[_var].type & MAY_BE_REF) && \ + !zend_bitset_in(visited, _var)) { \ + zend_bitset_incl(worklist, _var); \ + } \ + } while (0) + +#if 0 +/* Recursive Pearce's SCC algorithm implementation */ +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ +{ + int is_root = 1; +#ifdef SYM_RANGE + zend_ssa_phi *p; +#endif + + ssa->vars[var].scc = *index; + (*index)++; + + FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + p = ssa->vars[var].sym_use_chain; + while (p) { + CHECK_SCC_VAR(p->ssa_var); + p = p->sym_use_chain; + } +#endif + + if (is_root) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + ssa->vars[var].scc_entry = 1; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } +} +/* }}} */ + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack; + int j; + ALLOCA_FLAG(stack_use_heap) + + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); +} +/* }}} */ + +#else +/* Iterative Pearce's SCC algorithm implementation */ + +typedef struct _zend_scc_iterator { + int state; + int last; + union { + int use; + zend_ssa_phi *phi; + }; +} zend_scc_iterator; + +static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ +{ + zend_ssa_phi *phi; + int use, var2; + + switch (iterator->state) { + case 0: goto state_0; + case 1: use = iterator->use; goto state_1; + case 2: use = iterator->use; goto state_2; + case 3: use = iterator->use; goto state_3; + case 4: use = iterator->use; goto state_4; + case 5: use = iterator->use; goto state_5; + case 6: use = iterator->use; goto state_6; + case 7: use = iterator->use; goto state_7; + case 8: use = iterator->use; goto state_8; + case 9: phi = iterator->phi; goto state_9; +#ifdef SYM_RANGE + case 10: phi = iterator->phi; goto state_10; +#endif + case 11: goto state_11; + } + +state_0: + use = ssa->vars[var].use_chain; + while (use >= 0) { + iterator->use = use; + var2 = ssa->ops[use].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 1; + return var2; + } +state_1: + var2 = ssa->ops[use].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 2; + return var2; + } +state_2: + var2 = ssa->ops[use].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 3; + return var2; + } +state_3: + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use-1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 4; + return var2; + } +state_4: + var2 = ssa->ops[use-1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 5; + return var2; + } +state_5: + var2 = ssa->ops[use-1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } else if ((uint32_t)use+1 < op_array->last && + op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use+1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 6; + return var2; + } +state_6: + var2 = ssa->ops[use+1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 7; + return var2; + } +state_7: + var2 = ssa->ops[use+1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } +state_8: + use = zend_ssa_next_use(ssa->ops, var, use); + } + + phi = ssa->vars[var].phi_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 9; + iterator->phi = phi; + return var2; + } +state_9: + phi = zend_ssa_next_use_phi(ssa, var, phi); + } + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + phi = ssa->vars[var].sym_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 10; + iterator->phi = phi; + return var2; + } +state_10: + phi = phi->sym_use_chain; + } +#endif + + iterator->state = 11; +state_11: + return -1; +} +/* }}} */ + +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */ +{ +restart: + zend_worklist_stack_push(vstack, var); + iterators[var].state = 0; + iterators[var].last = -1; + ssa->vars[var].scc_entry = 1; + ssa->vars[var].scc = *index; + (*index)++; + + while (vstack->len > 0) { + var = zend_worklist_stack_peek(vstack); + while (1) { + int var2; + + if (iterators[var].last >= 0) { + /* finish edge */ + var2 = iterators[var].last; + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + ssa->vars[var].scc = ssa->vars[var2].scc; + ssa->vars[var].scc_entry = 0; + } + } + var2 = zend_scc_next(op_array, ssa, var, iterators + var); + iterators[var].last = var2; + if (var2 < 0) break; + /* begin edge */ + if (ssa->vars[var2].scc < 0) { + var = var2; + goto restart; + } + } + + /* finish visiting */ + zend_worklist_stack_pop(vstack); + if (ssa->vars[var].scc_entry) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } + } +} +/* }}} */ + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack, vstack; + zend_scc_iterator *iterators; + int j; + ALLOCA_FLAG(stack_use_heap) + ALLOCA_FLAG(vstack_use_heap) + ALLOCA_FLAG(iterators_use_heap) + + iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); + ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); + free_alloca(iterators, iterators_use_heap); +} +/* }}} */ + +#endif + + diff --git a/Zend/Optimizer/zend_scc.h b/Zend/Optimizer/zend_scc.h new file mode 100644 index 0000000000000..254234175da4a --- /dev/null +++ b/Zend/Optimizer/zend_scc.h @@ -0,0 +1,27 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SCC_H +#define ZEND_SCC_H + +#include "zend_compile.h" +#include "zend_ssa.h" + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); + +#endif /* ZEND_SCC_H */ diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 590df8155e395..633b6628c32c4 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -17,12 +17,14 @@ +----------------------------------------------------------------------+ */ +#include "zend_arena.h" #include "zend_compile.h" #include "zend_dfg.h" #include "zend_ssa.h" #include "zend_dump.h" #include "zend_inference.h" #include "Optimizer/zend_optimizer_internal.h" +#include "zend_vm_opcodes.h" static bool dominates(const zend_basic_block *blocks, int a, int b) { while (blocks[b].level > blocks[a].level) { @@ -540,6 +542,81 @@ static void place_essa_pis( } /* }}} */ +static const zend_op *zend_ssa_find_prev_send_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return NULL; + } + break; + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + if (depth == 1) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + +static const zend_op *zend_ssa_find_fcall_init_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */ { const zend_op *next; @@ -680,12 +757,6 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_BIND_INIT_STATIC_OR_JMP: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_REF: - case ZEND_SEND_UNPACK: case ZEND_FE_RESET_RW: case ZEND_MAKE_REF: case ZEND_PRE_INC_OBJ: @@ -703,7 +774,67 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, goto add_op1_def; } break; + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: case ZEND_SEND_VAR: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + // TODO: this is inefficient + const zend_op *init_op = zend_ssa_find_fcall_init_op(op_array, opline); + ssa_ops[k].result_def = ssa_vars_count; + ssa_vars_count++; + if (opline->extended_value == 0) { + if (init_op->opcode != ZEND_NEW) { + const zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + ssa_ops[k].result_use = init_ssa_op->result_def; + } + } else { + // TODO: this is inefficient + const zend_op *prev_send_op = zend_ssa_find_prev_send_op(op_array, opline); + ssa_ops[k].result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } + } else if (opline->op1_type == IS_CV) { + if (opline->opcode != ZEND_SEND_VAR || (build_flags & ZEND_SSA_RC_INFERENCE)) { + goto add_op1_def; + } + } + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + ssa_ops[k].result_def = ssa_vars_count; + ssa_vars_count++; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_CALLABLE_CONVERT: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + // TODO: this is inefficient + const zend_op *init_op = zend_ssa_find_fcall_init_op(op_array, opline); + const zend_op *prev_send_op = zend_ssa_find_prev_send_op(op_array, opline); + if (init_op->opcode == ZEND_NEW) { + if (prev_send_op) { + zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + init_ssa_op->result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } + } else { + if (prev_send_op) { + ssa_ops[k].result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } else { + const zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + ssa_ops[k].result_use = init_ssa_op->result_def; + } + } + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: @@ -787,7 +918,7 @@ ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *op } /* }}} */ -static zend_result zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ +static zend_result zend_ssa_rename(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_block *ssa_blocks = ssa->blocks; @@ -891,7 +1022,7 @@ static zend_result zend_ssa_rename(const zend_op_array *op_array, uint32_t build j = blocks[n].children; while (j >= 0) { // FIXME: Tail call optimization? - if (zend_ssa_rename(op_array, build_flags, ssa, var, j) == FAILURE) + if (zend_ssa_rename(arena, op_array, build_flags, ssa, var, j) == FAILURE) return FAILURE; j = blocks[j].next_child; } @@ -1039,7 +1170,7 @@ ZEND_API zend_result zend_build_ssa(zend_arena **arena, const zend_script *scrip var[j] = j; } ssa->vars_count = op_array->last_var; - if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) == FAILURE) { + if (zend_ssa_rename(arena, op_array, build_flags, ssa, var, 0) == FAILURE) { free_alloca(var, var_use_heap); free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; @@ -1091,15 +1222,21 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_ ssa_vars[op->result_use].use_chain = i; } if (op->op1_def >= 0) { - ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var); + if (op_array->opcodes[i].op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var); + } ssa_vars[op->op1_def].definition = i; } if (op->op2_def >= 0) { - ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var); + if (op_array->opcodes[i].op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var); + } ssa_vars[op->op2_def].definition = i; } if (op->result_def >= 0) { - ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var); + if (op_array->opcodes[i].result_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var); + } ssa_vars[op->result_def].definition = i; } } @@ -1164,7 +1301,10 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_ } for (i = op_array->last_var; i < ssa->vars_count; i++) { if (ssa_vars[i].var < op_array->last_var) { - ssa_vars[i].alias = ssa_vars[ssa_vars[i].var].alias; + int var = ssa_vars[i].var; + if (var >= 0) { + ssa_vars[i].alias = ssa_vars[var].alias; + } } } } diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h index b59c59a1b8673..88a66d0d6df98 100644 --- a/Zend/Optimizer/zend_ssa.h +++ b/Zend/Optimizer/zend_ssa.h @@ -325,4 +325,54 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend } FOREACH_BLOCK_END(); \ } while (0) +#define FOR_EACH_DEFINED_VAR(line, MACRO) \ + do { \ + if (ssa->ops[line].op1_def >= 0) { \ + MACRO(ssa->ops[line].op1_def); \ + } \ + if (ssa->ops[line].op2_def >= 0) { \ + MACRO(ssa->ops[line].op2_def); \ + } \ + if (ssa->ops[line].result_def >= 0) { \ + MACRO(ssa->ops[line].result_def); \ + } \ + if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \ + if (ssa->ops[line-1].op1_def >= 0) { \ + MACRO(ssa->ops[line-1].op1_def); \ + } \ + if (ssa->ops[line-1].op2_def >= 0) { \ + MACRO(ssa->ops[line-1].op2_def); \ + } \ + if (ssa->ops[line-1].result_def >= 0) { \ + MACRO(ssa->ops[line-1].result_def); \ + } \ + } else if ((uint32_t)line+1 < op_array->last && \ + op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \ + if (ssa->ops[line+1].op1_def >= 0) { \ + MACRO(ssa->ops[line+1].op1_def); \ + } \ + if (ssa->ops[line+1].op2_def >= 0) { \ + MACRO(ssa->ops[line+1].op2_def); \ + } \ + if (ssa->ops[line+1].result_def >= 0) { \ + MACRO(ssa->ops[line+1].result_def); \ + } \ + } \ + } while (0) + +#define FOR_EACH_VAR_USAGE(_var, MACRO) \ + do { \ + zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ + int use = ssa->vars[_var].use_chain; \ + while (use >= 0) { \ + FOR_EACH_DEFINED_VAR(use, MACRO); \ + use = zend_ssa_next_use(ssa->ops, _var, use); \ + } \ + p = ssa->vars[_var].phi_use_chain; \ + while (p) { \ + MACRO(p->ssa_var); \ + p = zend_ssa_next_use_phi(ssa, _var, p); \ + } \ + } while (0) + #endif /* ZEND_SSA_H */ diff --git a/Zend/tests/bug77530.phpt b/Zend/tests/bug77530.phpt index fdb2bac78b179..2cff416fda751 100644 --- a/Zend/tests/bug77530.phpt +++ b/Zend/tests/bug77530.phpt @@ -7,4 +7,4 @@ echo (2)::class; ?> --EXPECTF-- -Fatal error: Illegal class name in %s on line %d +Fatal error: Cannot use "::class" on int in %s on line %d diff --git a/Zend/tests/generics/basic.phpt b/Zend/tests/generics/basic.phpt new file mode 100644 index 0000000000000..cbcd897ea7008 --- /dev/null +++ b/Zend/tests/generics/basic.phpt @@ -0,0 +1,20 @@ +--TEST-- +Basic generic class declaration +--FILE-- + { +} + +class C { +} + +final class F { +} + +trait T

{ +} + +?> +--EXPECT-- + diff --git a/Zend/tests/generics/bounds.phpt b/Zend/tests/generics/bounds.phpt new file mode 100644 index 0000000000000..af8f2390d35f7 --- /dev/null +++ b/Zend/tests/generics/bounds.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bounds +--FILE-- + {} + +var_dump(new C()); +var_dump(new C()); +try { + new C(); +} catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); +} + +?> +--EXPECT-- +object(C)#1 (0) { +} +object(C)#1 (0) { +} +TypeError: Class C: Generic type argument #0 (T) must be of type A, stdClass given diff --git a/Zend/tests/generics/duplicate_generic_param.phpt b/Zend/tests/generics/duplicate_generic_param.phpt new file mode 100644 index 0000000000000..2b27daa8913bd --- /dev/null +++ b/Zend/tests/generics/duplicate_generic_param.phpt @@ -0,0 +1,11 @@ +--TEST-- +Duplicate generic parameter name +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Duplicate generic parameter T in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_001.phpt b/Zend/tests/generics/generic_param_in_static_context_001.phpt new file mode 100644 index 0000000000000..6e02d4d991159 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_001.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 001 +--FILE-- + { + public static T $a; +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_002.phpt b/Zend/tests/generics/generic_param_in_static_context_002.phpt new file mode 100644 index 0000000000000..001dda76f988d --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_002.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 002 +--FILE-- + { + public static function f(T $a) {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_003.phpt b/Zend/tests/generics/generic_param_in_static_context_003.phpt new file mode 100644 index 0000000000000..652aa76b83b63 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_003.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 003 +--FILE-- + { + public static function f($a): T {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_004.phpt b/Zend/tests/generics/generic_param_in_static_context_004.phpt new file mode 100644 index 0000000000000..d72b163b5d37d --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_004.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 004 +--FILE-- + { + public static function f($a): T {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_005.phpt b/Zend/tests/generics/generic_param_in_static_context_005.phpt new file mode 100644 index 0000000000000..40fd91ffc5b60 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_005.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 004 +--FILE-- + { + const T A = 1; +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_with_over_types.phpt b/Zend/tests/generics/generic_param_with_over_types.phpt new file mode 100644 index 0000000000000..32eb3772da6ab --- /dev/null +++ b/Zend/tests/generics/generic_param_with_over_types.phpt @@ -0,0 +1,139 @@ +--TEST-- +Combining a generic parameter with other types +--FILE-- + { + public ?T $prop; + public function method(?T $param) { + var_dump($param); + } +} +class ConcreteTest1 extends AbstractTest1 {} + +$obj = new ConcreteTest1; +$obj->method([]); +$obj->method(null); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +abstract class AbstractTest2 { + public T|int $prop; + public function method(T|int $param) { + var_dump($param); + } +} +class ConcreteTest2 extends AbstractTest2 {} + +$obj = new ConcreteTest2; +$obj->method([]); +$obj->method(42); +$obj->method("42"); + +echo "\n"; + +abstract class AbstractTest3 { + public T|stdClass $prop; + public function method(T|stdClass $param) { + var_dump($param); + } +} +class ConcreteTest3 extends AbstractTest3 {} + +$obj = new ConcreteTest3; +$obj->method([]); +$obj->method(new stdClass); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +class ConcreteTest4 { + public ?T $prop; + public function method(?T $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest4; +$obj->method([]); +$obj->method(null); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +class ConcreteTest5 { + public T|int $prop; + public function method(T|int $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest5; +$obj->method([]); +$obj->method(42); +$obj->method("42"); + +echo "\n"; + +class ConcreteTest6 { + public T|stdClass $prop; + public function method(T|stdClass $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest6; +$obj->method([]); +$obj->method(new stdClass); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +array(0) { +} +NULL +AbstractTest1::method(): Argument #1 ($param) must be of type ?T (where T = array), string given, called in %s on line %d + +array(0) { +} +int(42) +int(42) + +array(0) { +} +object(stdClass)#3 (0) { +} +AbstractTest3::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d + +array(0) { +} +NULL +ConcreteTest4::method(): Argument #1 ($param) must be of type ?T (where T = array), string given, called in %s on line %d + +array(0) { +} +int(42) +int(42) + +array(0) { +} +object(stdClass)#3 (0) { +} +ConcreteTest6::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d diff --git a/Zend/tests/generics/inference.phpt b/Zend/tests/generics/inference.phpt new file mode 100644 index 0000000000000..c354aef9211fa --- /dev/null +++ b/Zend/tests/generics/inference.phpt @@ -0,0 +1,155 @@ +--TEST-- +Generic type inference +--FILE-- + {} +class GenB extends GenA {} +class GenC extends GenA {} +class GenD extends GenA {} + +class NoConstructor {} + +class TypeParamNotInConstructor { + public function __construct() {} +} + +class RequiredTypeParam { + public function __construct(T $t) {} +} + +class OptionalTypeParam { + public function __construct(T $t, U $u = null) {} +} + +class ParamUsedTwice { + public function __construct(T $t, T $u) {} +} + +class ParamInUnion { + public function __construct(T|A $t) {} +} + +class ParamInNullable { + public function __construct(?T $t) {} +} + +class ParamInGenericClass { + public function __construct(GenA $t) {} +} + +function test(A&B $ab, B&C $bc, A $a, B $b) { + var_dump(new RequiredTypeParam("string")); + var_dump(new RequiredTypeParam(1)); + var_dump(new RequiredTypeParam(new stdClass)); + + var_dump(new OptionalTypeParam(1, "string")); + var_dump(new OptionalTypeParam(1, 1)); + var_dump(new OptionalTypeParam(1, new stdClass)); + var_dump(new OptionalTypeParam(1)); + + var_dump(new ParamUsedTwice(1, "string")); + var_dump(new ParamUsedTwice(1, new stdClass)); + var_dump(new ParamUsedTwice(new DateTime, new stdClass)); + var_dump(new ParamUsedTwice($ab, new stdClass)); + var_dump(new ParamUsedTwice($ab, $ab)); + var_dump(new ParamUsedTwice($ab, $bc)); + var_dump(new ParamUsedTwice($ab, $a)); + var_dump(new ParamUsedTwice($a, $ab)); + + var_dump(new ParamInUnion(new stdClass)); + var_dump(new ParamInUnion($a)); + var_dump(new ParamInUnion($ab)); + var_dump(new ParamInUnion(rand() ? $a : $b)); + + var_dump(new ParamInNullable(new stdClass)); + var_dump(new ParamInNullable(rand() ? $a : null)); + var_dump(new ParamInNullable(rand() ? $b : null)); + + var_dump(new ParamInGenericClass(new GenA)); + var_dump(new ParamInGenericClass(new GenB)); + var_dump(new ParamInGenericClass(new GenC)); + var_dump(new ParamInGenericClass(new GenD)); + + try { + var_dump(new NoConstructor()); + } catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); + } + + try { + var_dump(new TypeParamNotInConstructor()); + } catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); + } + + +} + +test( + new class implements A, B {}, + new class implements B, C {}, + new class implements A {}, + new class implements B {}, +); + +?> +--EXPECT-- +object(RequiredTypeParam)#5 (0) { +} +object(RequiredTypeParam)#5 (0) { +} +object(RequiredTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice<(A&B)|stdClass>)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice<(A&B)|(B&C)>)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +Error: Generic type T can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter +Error: Generic type T can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter diff --git a/Zend/tests/generics/inheritance_bind_parent_param.phpt b/Zend/tests/generics/inheritance_bind_parent_param.phpt new file mode 100644 index 0000000000000..36a5d3dd208b6 --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_parent_param.phpt @@ -0,0 +1,66 @@ +--TEST-- +Bind direct parent parameter during inheritance +--FILE-- + { + public T $prop; + + public function method(T $param) { + var_dump($param); + } +} + +class ConcreteInt extends WithParam { +} + +class ConcreteStdClass extends WithParam { +} + +class ConcreteSelf extends WithParam { +} + +$obj = new ConcreteInt; +$obj->method(42); +$obj->prop = 42; +var_dump($obj->prop); + +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->prop = "string"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +$obj = new ConcreteStdClass; +$obj->method(new stdClass); +//$obj->prop = new stdClass; +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +/* TODO: This broke -- "self" resolves to WithParam here. +$obj = new ConcreteSelf; +$obj->method($obj); +try { + $obj->method(new stdClass); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +*/ + +?> +--EXPECTF-- +int(42) +int(42) +WithParam::method(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d +Cannot assign string to property WithParam::$prop of type T +object(stdClass)#1 (0) { +} +WithParam::method(): Argument #1 ($param) must be of type T (where T = stdClass), string given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_bind_parent_param_2.phpt b/Zend/tests/generics/inheritance_bind_parent_param_2.phpt new file mode 100644 index 0000000000000..48b24844b9d3e --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_parent_param_2.phpt @@ -0,0 +1,87 @@ +--TEST-- +Bind multiple parent parameters during inheritance +--FILE-- + { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +abstract class WithSameParam extends WithParams { + public function method2(T $param) { + var_dump($param); + } +} + +abstract class WithOneFixedParam extends WithParams { + public function method2(T $param) { + var_dump($param); + } +} + +abstract class WithFirstNullableParam extends WithParams { +} + +class ConcreteIntInt extends WithSameParam { +} + +class ConcreteIntString extends WithOneFixedParam { +} + +class ConcreteNullableIntInt extends WithFirstNullableParam { +} + +$obj = new ConcreteIntInt; +$obj->method(42, 42); +$obj->method2(42); + +try { + $obj->method("string", "string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->method2("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; +$obj = new ConcreteIntString; +$obj->method(42, "string"); +$obj->method2("string"); + +try { + $obj->method("string", 42); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->method2(42); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; +$obj = new ConcreteNullableIntInt; +$obj->method(null, 42); + +?> +--EXPECTF-- +int(42) +int(42) +int(42) +WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d +WithSameParam::method2(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d + +int(42) +string(6) "string" +string(6) "string" +WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d +string(2) "42" + +NULL +int(42) diff --git a/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt b/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt new file mode 100644 index 0000000000000..eeb85db4d8dab --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bind parent parameter passed through to grandparent during inheritance +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +abstract class WithParam2 extends WithParam { +} + +class Concrete extends WithParam2 { +} + +$obj = new Concrete; +$obj->method(42); + +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +int(42) +WithParam::method(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_default_param.phpt b/Zend/tests/generics/inheritance_default_param.phpt new file mode 100644 index 0000000000000..9f36247750e92 --- /dev/null +++ b/Zend/tests/generics/inheritance_default_param.phpt @@ -0,0 +1,49 @@ +--TEST-- +Defaulted type parameters during inheritance +--FILE-- + { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +class Concrete extends WithSimpleDefault { +} + +abstract class WithPrevParamDefault { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +class Concrete2 extends WithPrevParamDefault { +} + +$obj = new Concrete; +$obj->method("string", 42); +try { + $obj->method(42, "string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +$obj = new Concrete2; +$obj->method("string", "string"); +try { + $obj->method([], []); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +string(6) "string" +int(42) +WithSimpleDefault::method(): Argument #2 ($param2) must be of type T2 (where T2 = int), string given, called in %s on line %d +string(6) "string" +string(6) "string" +WithPrevParamDefault::method(): Argument #1 ($param1) must be of type T1 (where T1 = string), array given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_signature_check.phpt b/Zend/tests/generics/inheritance_signature_check.phpt new file mode 100644 index 0000000000000..2af4fa7770f50 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check.phpt @@ -0,0 +1,16 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param) {} +} + +class Test2 extends Test { + public function method(string $param) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Test2::method(string $param) must be compatible with Test::method(T $param) in %s on line %d diff --git a/Zend/tests/generics/inheritance_signature_check_2.phpt b/Zend/tests/generics/inheritance_signature_check_2.phpt new file mode 100644 index 0000000000000..753688fe86bc8 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check_2.phpt @@ -0,0 +1,25 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param): T {} +} + +class Test2 extends Test { + public function method(int $param): int {} +} + +class Test3 extends Test { + public function method(T $param): T {} +} + +class Test4 extends Test { + public function method(?T $param): T {} +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/generics/inheritance_signature_check_3.phpt b/Zend/tests/generics/inheritance_signature_check_3.phpt new file mode 100644 index 0000000000000..ba17746039ce0 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check_3.phpt @@ -0,0 +1,16 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param): T {} +} + +class Test2 extends Test { + public function method(T2 $param): T2 {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Test2::method(T2 $param): T2 must be compatible with Test::method(T $param): T in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args.phpt b/Zend/tests/generics/inheritance_too_few_args.phpt new file mode 100644 index 0000000000000..b045793b05ec9 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 2 generic arguments, but 1 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args_2.phpt b/Zend/tests/generics/inheritance_too_few_args_2.phpt new file mode 100644 index 0000000000000..4b8ae56e4adc8 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent (2) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects at least 2 generic arguments, but 1 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args_3.phpt b/Zend/tests/generics/inheritance_too_few_args_3.phpt new file mode 100644 index 0000000000000..77f98873a5da9 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args_3.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent (3) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 1 generic argument, but 0 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args.phpt b/Zend/tests/generics/inheritance_too_many_args.phpt new file mode 100644 index 0000000000000..00f1ef24c3d91 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 2 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args_2.phpt b/Zend/tests/generics/inheritance_too_many_args_2.phpt new file mode 100644 index 0000000000000..262c21178541a --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent (2) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects at most 2 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args_3.phpt b/Zend/tests/generics/inheritance_too_many_args_3.phpt new file mode 100644 index 0000000000000..91f8bb3e966ba --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args_3.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent (3) +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 0 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/param_same_as_class.phpt b/Zend/tests/generics/param_same_as_class.phpt new file mode 100644 index 0000000000000..35f7249d3a90a --- /dev/null +++ b/Zend/tests/generics/param_same_as_class.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic parameter can't have same name as the class +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Generic parameter Foo has same name as class in %s on line %d diff --git a/Zend/tests/generics/required_after_optional.phpt b/Zend/tests/generics/required_after_optional.phpt new file mode 100644 index 0000000000000..8a0d696b9d9af --- /dev/null +++ b/Zend/tests/generics/required_after_optional.phpt @@ -0,0 +1,10 @@ +--TEST-- +Required generic parameter after optional +--FILE-- + {} + +?> +--EXPECTF-- +Fatal error: Required generic parameter T2 follows optional in %s on line %d diff --git a/Zend/tests/generics/symbolic_inference.inc b/Zend/tests/generics/symbolic_inference.inc new file mode 100644 index 0000000000000..6d5d5c124c3f9 --- /dev/null +++ b/Zend/tests/generics/symbolic_inference.inc @@ -0,0 +1,95 @@ + { + public function __construct(T $t) {} + } + + class D { + const A = 1; + const ?int B = 2; + + public $a; + public int $b; + public stdClass $c; + public C $d; + public ?int $e; + + public function __construct() { + $this->a = null; + $this->b = 1; + $this->c = new stdClass; + $this->d = new C(1); + $this->e = null; + } + + public static function staticMethod(): int { + return 1; + } + + public function method(): string { + return "string"; + } + + public function d(): D { + return $this; + } + } + + function _f($a): int { + return (int) $a; + } + + function _g($a): int { + return (int) $a; + } + + function _int(): int { + return 1; + } + + function _nullable_int(): ?int { + return 1; + } + + function _float(): float { + return 1.5; + } + + function _gmp(): GMP { + return gmp_init(0); + } + + function _array(): array { + return []; + } + + function _string(): string { + return "string"; + } + + function _mixed(): mixed { + return null; + } + + function _mixed_long(): mixed { + return 1; + } + + function _extended_d(): D { + return new class extends D { + function extended(): D { + return new D(); + } + }; + } + +} + +namespace Ns { + const RootAndNsConstant = 1; +} diff --git a/Zend/tests/generics/symbolic_inference.phpt b/Zend/tests/generics/symbolic_inference.phpt new file mode 100644 index 0000000000000..1286874a78520 --- /dev/null +++ b/Zend/tests/generics/symbolic_inference.phpt @@ -0,0 +1,642 @@ +--TEST-- +Symbolic inference +--FILE-- + $d, ?int $e = null) { + var_dump(new C($a)); + var_dump(new C($b)); + var_dump(new C($c)); + var_dump(new C($d)); + var_dump(new C($e)); + }; + $f(1, 1, new stdClass, new C(1), null); + } + + function recv_variadic1() { + $f = function(...$a) { + var_dump(new C($a)); + }; + $f(1,2,3); + } + + function recv_variadic2() { + $f = function(int ...$a) { + var_dump(new C($a)); + }; + $f(1,2,3); + } + + function assign() { + $a = 1; + var_dump(new C($a)); + var_dump(new C($a = "string")); + } + + function assign_dim($i=1) { + $a[0] = 1; + var_dump(new C($a[0])); + var_dump(new C($a[1] = "string")); + @var_dump(new C($a[2]|=1)); + @var_dump(new C($a[3].="string")); + } + + function fetch_dim() { + $a = [1]; + var_dump(new C($a[0])); + } + + class J { + public T $t; + } + class KextendsJ extends J { + } + + function fetch_obj() { + $props = new D(); + var_dump(new C($props->a)); + var_dump(new C($props->b)); + var_dump(new C($props->c)); + var_dump(new C($props->d)); + var_dump(new C($props->e)); + + $props = new J(); + var_dump(new C($props->t ?? null)); + + //$props = new KextendsJ(); + //var_dump(new C($props->t ?? null)); + } + + class E { + const DefinedInE = 'string'; + + function useSelfDefinedInE() { + var_dump(new C(self::DefinedInE)); + } + + function useStaticDefinedInE() { + var_dump(new C(static::DefinedInE)); + } + + function useThisDefinedInE() { + var_dump(new C($this::DefinedInE)); + } + + function useStaticDefinedInFFromE() { + var_dump(new C(static::DefinedInF)); + } + + function useThisDefinedInFFromE() { + var_dump(new C($this::DefinedInF)); + } + + } + + class FextendsE extends E { + const DefinedInF = 1; + + function useSelfDefinedInEFromF() { + var_dump(new C(self::DefinedInE)); + } + + function useParentDefinedInEFromF() { + var_dump(new C(parent::DefinedInE)); + } + + function useStaticDefinedInEFromF() { + var_dump(new C(parent::DefinedInE)); + } + + function useThisDefinedInEFromF() { + var_dump(new C($this::DefinedInE)); + } + + function useSelfDefinedInF() { + var_dump(new C(self::DefinedInF)); + } + + function useStaticDefinedInF() { + var_dump(new C(static::DefinedInF)); + } + + function useThisDefinedInF() { + var_dump(new C($this::DefinedInF)); + } + } + + function fetch_class_constant() { + var_dump(new C(D::A)); + var_dump(new C(D::B)); + + $e = new E(); + foreach (get_class_methods($e) as $method) { + printf("\$e->%s:\n", $method); + try { + [$e, $method](); + } catch (Error $error) { + printf("%s: %s\n", $error::class, $error->getMessage()); + } + } + + $f = new FextendsE(); + foreach (get_class_methods($f) as $method) { + printf("\$f->%s:\n", $method); + try { + [$f, $method](); + } catch (Error $error) { + printf("%s: %s\n", $error::class, $error->getMessage()); + } + } + } + + function fetch_constant1() { + var_dump(new C(RootOnlyConstant)); + var_dump(new C(RootAndNsConstant)); + } + +} + +namespace Ns { + + function fetch_constant() { + var_dump(new \C(RootOnlyConstant)); + var_dump(new \C(RootAndNsConstant)); + } + +} + +namespace { + + function bools(mixed $a = null, mixed $b = null) { + var_dump(new C(!$a)); + var_dump(new C($a && $b)); + var_dump(new C($a || $b)); + var_dump(new C($a xor $b)); + var_dump(new C($a and $b)); + var_dump(new C($a or $b)); + } + + function arithmetic() { + var_dump(new C(_int() + _int())); + var_dump(new C(_array() + _array())); + var_dump(new C(_mixed_long() + _mixed_long())); + + var_dump(new C(_int() * _int())); + + var_dump(new C(_int() ^ _int())); + var_dump(new C(_string() ^ _string())); + + var_dump(new C(_int() % _int())); + } + + function concat() { + var_dump(new C(_string() . _string())); + var_dump(new C(_int() . _int())); + + $a = _string(); + var_dump(new C("string $a")); + } + + function fcall() { + var_dump(new C(_int())); + } + + class G { + public static function f(): string { + return 'string'; + } + public function callSelfF() { + var_dump(new C(self::f())); + } + public function callStaticF() { + var_dump(new C(static::f())); + } + public function callThisF() { + var_dump(new C($this::f())); + } + } + + class H { + public function __construct(public mixed $p) {} + public function f(): T { + return $this->p; + } + public function callSelfF() { + var_dump(new C(self::f())); + } + public function callStaticF() { + var_dump(new C(static::f())); + } + public function callThisF() { + var_dump(new C($this::f())); + } + } + + class IextendsH extends H { + public function callParentF() { + var_dump(new C(parent::f())); + } + } + + function mcall() { + $o = new D(); + var_dump(new C($o->staticMethod())); + var_dump(new C(D::staticMethod())); + var_dump(new C($o->method())); + + $o = _extended_d(); + var_dump(new C($o->extended())); + + $g = new G(); + printf("\$g->callSelfF\n"); + $g->callSelfF(); + printf("\$g->callStaticF\n"); + $g->callStaticF(); + printf("\$g->callThisF\n"); + $g->callThisF(); + + $h = new H('string'); + printf("\$h->callSelfF\n"); + $h->callSelfF(); + printf("\$h->callStaticF\n"); + $h->callStaticF(); + printf("\$h->callThisF\n"); + $h->callThisF(); + + printf("\$h->f\n"); + var_dump(new C($h->f())); + + $i = new IextendsH('string'); + printf("\$i->callParentF\n"); + $i->callParentF(); + } + + function new_() { + var_dump(new C(new D())); + var_dump(new C(new C(1))); + var_dump(new C(new C(1))); + } + + function coalesce() { + var_dump(new C(_nullable_int() ?? _string())); + var_dump(new C(_nullable_int() ?? null)); + } + + function isset_() { + var_dump(new C(isset($a))); + var_dump(new C(empty($a))); + } + + function lambda() { + var_dump(new C(function () {})); + } + + function assign_obj() { + $d = new D(); + $c = new C(1); + var_dump(new C($d->a = 1)); + var_dump(new C($d->b = 1)); + var_dump(new C($d->d = &$c)); + var_dump(new C($d->b += 1)); + } + + function init_array(): void { + $a = [rand(), rand()]; + var_dump(new C($a)); + $a = [...$a]; + var_dump(new C($a)); + } + + function phi(): void { + $a = 1; + if (rand()) { + $a = 'string'; + } + var_dump(new C($a)); + } + + function recursive_fcall($i = 1): void { + $a = "string"; + do { + // _f(...(_f(_f(string|_f(string)))) -> _f(string|_f(string)) + $a = _f($a); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_fcall2($i = 1): void { + $a = "string"; + do { + $a = _f($a); + $a = _g($a); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_arith1($i = 1): void { + $a = "0"; + do { + $a = $a + 1; + } while ($i--); + var_dump(new C($a)); + } + + function recursive_arith2($i = 1): void { + $a = "0"; + do { + $a = _f($a + 1); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_mcall($i = 1): void { + $d = new D(); + do { + $d = $d->d(); + } while ($i--); + var_dump(new C($d)); + } + + // TODO: + // - @ + // - assign_op + + foreach (get_defined_functions()['user'] as $function) { + if (str_starts_with($function, '_')) { + continue; + } + + printf("\n# %s\n", $function); + + $function(); + } + +} + +?> +--EXPECTF-- +# %s +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C>)#4 (0) { +} +object(C)#4 (0) { +} + +# recv_variadic1 +object(C)#3 (0) { +} + +# recv_variadic2 +object(C)#3 (0) { +} + +# assign +object(C)#1 (0) { +} +object(C)#1 (0) { +} + +# assign_dim +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} + +# fetch_dim +object(C)#1 (0) { +} + +# fetch_obj +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C>)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#1 (0) { +} + +# fetch_class_constant +object(C)#4 (0) { +} +object(C)#4 (0) { +} +$e->useSelfDefinedInE: +object(C)#1 (0) { +} +$e->useStaticDefinedInE: +object(C)#1 (0) { +} +$e->useThisDefinedInE: +object(C)#1 (0) { +} +$e->useStaticDefinedInFFromE: +Error: Undefined constant E::DefinedInF +$e->useThisDefinedInFFromE: +Error: Undefined constant E::DefinedInF +$f->useSelfDefinedInEFromF: +object(C)#1 (0) { +} +$f->useParentDefinedInEFromF: +object(C)#1 (0) { +} +$f->useStaticDefinedInEFromF: +object(C)#1 (0) { +} +$f->useThisDefinedInEFromF: +object(C)#1 (0) { +} +$f->useSelfDefinedInF: +object(C)#1 (0) { +} +$f->useStaticDefinedInF: +object(C)#1 (0) { +} +$f->useThisDefinedInF: +object(C)#1 (0) { +} +$f->useSelfDefinedInE: +object(C)#1 (0) { +} +$f->useStaticDefinedInE: +object(C)#1 (0) { +} +$f->useThisDefinedInE: +object(C)#1 (0) { +} +$f->useStaticDefinedInFFromE: +object(C)#1 (0) { +} +$f->useThisDefinedInFFromE: +object(C)#1 (0) { +} + +# fetch_constant1 +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# ns\fetch_constant +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# bools +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# arithmetic +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# concat +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# fcall +object(C)#2 (0) { +} + +# mcall +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#2 (0) { +} +$g->callSelfF +object(C)#4 (0) { +} +$g->callStaticF +object(C)#4 (0) { +} +$g->callThisF +object(C)#4 (0) { +} +$h->callSelfF +object(C)#7 (0) { +} +$h->callStaticF +object(C)#7 (0) { +} +$h->callThisF +object(C)#7 (0) { +} +$h->f +object(C)#7 (0) { +} +$i->callParentF +object(C)#3 (0) { +} + +# new_ +object(C)#7 (0) { +} +object(C>)#7 (0) { +} +object(C)#7 (0) { +} + +# coalesce +object(C)#7 (0) { +} +object(C)#7 (0) { +} + +# isset_ +object(C)#7 (0) { +} +object(C)#7 (0) { +} + +# lambda +object(C)#7 (0) { +} + +# assign_obj +object(C)#6 (0) { +} +object(C)#6 (0) { +} +object(C>)#6 (0) { +} +object(C)#6 (0) { +} + +# init_array +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# phi +object(C)#2 (0) { +} + +# recursive_fcall +object(C)#2 (0) { +} + +# recursive_fcall2 +object(C)#2 (0) { +} + +# recursive_arith1 +object(C)#2 (0) { +} + +# recursive_arith2 +object(C)#2 (0) { +} + +# recursive_mcall +object(C)#6 (0) { +} diff --git a/Zend/tests/generics/symbolic_inference_overloading.phpt b/Zend/tests/generics/symbolic_inference_overloading.phpt new file mode 100644 index 0000000000000..312194917ce9f --- /dev/null +++ b/Zend/tests/generics/symbolic_inference_overloading.phpt @@ -0,0 +1,37 @@ +--TEST-- +Symbolic inference with overloading +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECT-- +# arithmetic +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} diff --git a/Zend/tests/generics/syntax.phpt b/Zend/tests/generics/syntax.phpt new file mode 100644 index 0000000000000..a7fd2b3be60ab --- /dev/null +++ b/Zend/tests/generics/syntax.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test generic syntax +--FILE-- +::BAR); +var_dump(Foo::BAR); +var_dump(new Foo); +var_dump(new Foo(1)); +var_dump(new Foo); +var_dump(new Foo(1)); + +?> +--EXPECTF-- +Fatal error: Generic type arguments are currently not supported here yet in %s on line %d diff --git a/Zend/tests/generics/type_checking.phpt b/Zend/tests/generics/type_checking.phpt new file mode 100644 index 0000000000000..f887543c60b13 --- /dev/null +++ b/Zend/tests/generics/type_checking.phpt @@ -0,0 +1,1173 @@ +--TEST-- +Type checking +--FILE-- + {} +class Base {} +interface I {} +class D extends Base implements I {} + +function f1(C $a) {} +function f2(C $a) {} +function f3(C $a) {} +function f4(C $a) {} +function f5(C $a) {} +function f6(C $a) {} +function f7(C $a) {} +function f8(C $a) {} +function f9(C<(Base&I)|stdClass> $a) {} +function f10(C > $a) {} +function f11(C|int> $a) {} +function f12(array $a) {} +function f13(C $a) {} +function f14(C $a) {} +function f15(C|stdClass $a) {} +function f16(C|null $a) {} + +class Test { + function m1(C $a) {} + function m2(C $a) {} + function m3(C $a) {} + function m4(C $a) {} + function m5(C $a) {} + function m6(C $a) {} +} + +function test(ReflectionFunctionAbstract $rf, object $object = null, string $objectName = null) { + $functionName = $rf->getName(); + if ($object) { + $functionName = sprintf('%s::%s', $objectName, $functionName); + } + $typeName = $rf->getParameters()[0]->getType()->__toString(); + + printf("function %s(%s):\n", $functionName, $typeName); + + $values = [ + 'mixed' => new C(), + 'int' => new C(), + 'string' => new C(), + 'string|int' => new C(), + 'stdClass' => new C(), + 'stdClass|int' => new C(), + 'stdClass|DateTime|int' => new C(), + 'Base' => new C(), + 'D' => new C(), + 'C' => new C >(), + 'C|int' => new C|int>(), + 'array' => new C(), + 'object' => new C(), + ]; + + foreach ($values as $label => $value) { + try { + if ($object) { + $rf->invoke($object, $value); + } else { + $rf->invoke($value); + } + printf(" - C<%s>: accepted\n", $label); + } catch (Error $e) { + printf(" - C<%s>: %s\n", $label, $e->getMessage()); + } + } +} + +$functions = get_defined_functions()['user']; + +foreach ($functions as $function) { + $rf = new ReflectionFunction($function); + if ($rf->getFilename() !== __FILE__ || !str_starts_with($function, 'f')) { + continue; + } + + test($rf); +} + +$methods = (new ReflectionClass(Test::class))->getMethods(); + +$objects = [ + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), +]; + +foreach ($objects as $objectName => $object) { + foreach ($methods as $method) { + test($method, $object, $objectName); + } +} + +?> +--EXPECT-- +function f1(C): + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C>: f1(): Argument #1 ($a) must be of type C, C given + - C|int>: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given +function f2(C): + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C>: f2(): Argument #1 ($a) must be of type C, C given + - C|int>: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given +function f3(C): + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C>: f3(): Argument #1 ($a) must be of type C, C given + - C|int>: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given +function f4(C): + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C>: f4(): Argument #1 ($a) must be of type C, C given + - C|int>: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given +function f5(C): + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C>: f5(): Argument #1 ($a) must be of type C, C given + - C|int>: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given +function f6(C): + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C>: f6(): Argument #1 ($a) must be of type C, C given + - C|int>: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given +function f7(C): + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C>: f7(): Argument #1 ($a) must be of type C, C given + - C|int>: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given +function f8(C): + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C>: f8(): Argument #1 ($a) must be of type C, C given + - C|int>: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given +function f9(C<(Base&I)|stdClass>): + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: accepted + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: accepted + - C>: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C|int>: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given +function f10(C>): + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C>: accepted + - C|int>: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given +function f11(C|int>): + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: accepted + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C>: f11(): Argument #1 ($a) must be of type C|int>, C given + - C|int>: accepted + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given +function f12(array): + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C>: f12(): Argument #1 ($a) must be of type array, C given + - C|int>: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given +function f13(C): + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C>: f13(): Argument #1 ($a) must be of type C, C given + - C|int>: f13(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f13(): Argument #1 ($a) must be of type C, C given +function f14(C): + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted +function f15(C|stdClass): + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: accepted + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C>: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C|int>: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given +function f16(?C): + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: accepted + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C>: f16(): Argument #1 ($a) must be of type ?C, C given + - C|int>: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: accepted + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: accepted + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: accepted + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: accepted + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: accepted + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: accepted + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given diff --git a/Zend/tests/generics/type_params_in_types.phpt b/Zend/tests/generics/type_params_in_types.phpt new file mode 100644 index 0000000000000..38f40b2af84b3 --- /dev/null +++ b/Zend/tests/generics/type_params_in_types.phpt @@ -0,0 +1,129 @@ +--TEST-- +Concrete parameterized types used in type expressions +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +abstract class AbstractPassthru extends AbstractTest { +} + +abstract class AbstractDefaulted extends AbstractTest { +} + +class ConcreteInt extends AbstractTest { +} + +class ConcreteString extends AbstractTest { +} + +class ConcreteIntPassthru extends AbstractPassthru { +} + +class ConcreteIntDefaulted extends AbstractDefaulted { +} +class ConcreteStringDefaulted extends AbstractDefaulted { +} + +class ConcretePassthru { + public function method(T $param) { + var_dump($param); + } +} + +class ConcreteDefaulted { + public function method(T $param) { + var_dump($param); + } +} + +function test(AbstractTest $test) { + $test->method(42); +} + +test(new ConcreteInt); +test(new ConcreteIntPassthru); +test(new ConcreteIntDefaulted); +try { + test(new ConcreteString); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +function test2(AbstractDefaulted $test) { + $test->method(42); +} + +function test3(AbstractDefaulted $test) { + $test->method(42); +} + +test2(new ConcreteIntDefaulted); +test3(new ConcreteIntDefaulted); +try { + test2(new ConcreteStringDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + test3(new ConcreteStringDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +function test4(ConcretePassthru $test) { + $test->method(42); +} + +test4(new ConcretePassthru); +try { + test4(new ConcretePassthru); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +function test5(ConcreteDefaulted $test) { + $test->method(42); +} + +function test6(ConcreteDefaulted $test) { + $test->method(42); +} + +test5(new ConcreteDefaulted); +test5(new ConcreteDefaulted); +test6(new ConcreteDefaulted); +test6(new ConcreteDefaulted); +try { + test5(new ConcreteDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + test6(new ConcreteDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +int(42) +int(42) +int(42) +test(): Argument #1 ($test) must be of type AbstractTest, ConcreteString given, called in %s on line %d +int(42) +int(42) +test2(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d +test3(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d +int(42) +test4(): Argument #1 ($test) must be of type ConcretePassthru, ConcretePassthru given, called in %s on line %d +int(42) +int(42) +int(42) +int(42) +test5(): Argument #1 ($test) must be of type ConcreteDefaulted, ConcreteDefaulted given, called in %s on line %d +test6(): Argument #1 ($test) must be of type ConcreteDefaulted, ConcreteDefaulted given, called in %s on line %d diff --git a/Zend/tests/generics/type_with_incorrect_number_of_args.phpt b/Zend/tests/generics/type_with_incorrect_number_of_args.phpt new file mode 100644 index 0000000000000..98bd9f2e5313c --- /dev/null +++ b/Zend/tests/generics/type_with_incorrect_number_of_args.phpt @@ -0,0 +1,48 @@ +--TEST-- +Type that uses an incorrect number of generic args +--FILE-- + {} +class C2 {} +class C3 {} + +function test1(C1 $param) {} +function test2(C2 $param) {} +function test3(C2 $param) {} +function test4(C3 $param) {} +function test5(C3 $param) {} + +try { + test1(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test2(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test3(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test4(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test5(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Class C1 expects exactly 1 generic argument, but 2 provided +Class C2 expects exactly 2 generic arguments, but 3 provided +Class C2 expects exactly 2 generic arguments, but 1 provided +Class C3 expects at most 3 generic arguments, but 4 provided +Class C3 expects at least 2 generic arguments, but 1 provided diff --git a/Zend/tests/generics/unbound_params.phpt b/Zend/tests/generics/unbound_params.phpt new file mode 100644 index 0000000000000..680023986c729 --- /dev/null +++ b/Zend/tests/generics/unbound_params.phpt @@ -0,0 +1,119 @@ +--TEST-- +Test instantiation of class with unbound params +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +class Test2 { + public function method(T $t, U $u) { + var_dump($t, $u); + } +} + +class Test3 { + public function method(T $param) { + var_dump($param); + } +} + +class AbstractTest4 { + public function method1(T $t, U $u) { + var_dump($t, $u); + } +} + +class Test4 extends AbstractTest4 { + public function method2(T $param) { + var_dump($param); + } +} + +$test = new Test1(); +$test->method(42); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test2(); +$test->method(42,'hello'); +try { + $test->method([],'hello'); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method(42,[]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test3(); +$test->method(42); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test3(); +$test->method('hello'); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test4(); +$test->method1(42, 'hello'); +$test->method2(42); +try { + $test->method1([], 'hello'); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method1(42,[]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method2([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +--EXPECTF-- +int(42) +Test1::method(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d + +int(42) +string(5) "hello" +Test2::method(): Argument #1 ($t) must be of type T (where T = int), array given, called in %s on line %d +Test2::method(): Argument #2 ($u) must be of type U (where U = string), array given, called in %s on line %d + +int(42) +Test3::method(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d + +string(5) "hello" +Test3::method(): Argument #1 ($param) must be of type T (where T = string), array given, called in %s on line %d + +int(42) +string(5) "hello" +int(42) +AbstractTest4::method1(): Argument #1 ($t) must be of type T (where T = int), array given, called in %s on line %d +AbstractTest4::method1(): Argument #2 ($u) must be of type U (where U = string), array given, called in %s on line %d +Test4::method2(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d diff --git a/Zend/tests/generics/variance.phpt b/Zend/tests/generics/variance.phpt new file mode 100644 index 0000000000000..e0ec1f46302ae --- /dev/null +++ b/Zend/tests/generics/variance.phpt @@ -0,0 +1,70 @@ +--TEST-- +Variance +--FILE-- + {} +class Out {} +class In {} + +$objects = [ + 'Invariant' => new Invariant, + 'Invariant' => new Invariant, + 'Out' => new Out, + 'Out' => new Out, + 'In' => new In, + 'In' => new In, +]; + +$functions = [ + Invariant::class => [ + function (Invariant $a) {}, + function (Invariant $b) {}, + ], + Out::class => [ + function (Out $a) {}, + function (Out $b) {}, + ], + In::class => [ + function (In $a) {}, + function (In $b) {}, + ], +]; + +foreach ($objects as $objectName => $object) { + printf("%s:\n", $objectName); + foreach ($functions[$object::class] as $function) { + $rf = new ReflectionFunction($function); + printf(" - function(%s): ", $rf->getParameters()[0]->getType()->__toString()); + try { + $rf->invoke($object); + printf("accepted\n"); + } catch (Error $e) { + printf("%s\n", $e->getMessage()); + } + } +} + +?> +--EXPECT-- +Invariant: + - function(Invariant): accepted + - function(Invariant): {closure}(): Argument #1 ($b) must be of type Invariant, Invariant given +Invariant: + - function(Invariant): {closure}(): Argument #1 ($a) must be of type Invariant, Invariant given + - function(Invariant): accepted +Out: + - function(Out): accepted + - function(Out): {closure}(): Argument #1 ($b) must be of type Out, Out given +Out: + - function(Out): accepted + - function(Out): accepted +In: + - function(In): accepted + - function(In): accepted +In: + - function(In): {closure}(): Argument #1 ($a) must be of type In, In given + - function(In): accepted diff --git a/Zend/tests/oss_fuzz_59764.phpt b/Zend/tests/oss_fuzz_59764.phpt index c8ba573ff9a7e..2d3125251c22d 100644 --- a/Zend/tests/oss_fuzz_59764.phpt +++ b/Zend/tests/oss_fuzz_59764.phpt @@ -5,4 +5,4 @@ oss-fuzz #59764: Test const B = []::{A}; ?> --EXPECTF-- -Fatal error: Class name must be a valid object or a string in %s on line %d +Fatal error: Dynamic class names are not allowed in compile-time class constant references in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index fb0af12f9e510..7578ce5af5280 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -26,6 +26,7 @@ #include "zend_exceptions.h" #include "zend_builtin_functions.h" #include "zend_ini.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_dtrace.h" #include "zend_virtual_cwd.h" @@ -556,13 +557,13 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* smart_str_appends(buf, ZSTR_VAL(class_name)); zend_string_release_ex(class_name, 0); - if (!(zobj->ce->ce_flags & ZEND_ACC_ENUM)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM)) { smart_str_appends(buf, " Object\n"); } else { smart_str_appends(buf, " Enum"); - if (zobj->ce->enum_backing_type != IS_UNDEF) { + if (OBJ_CE(zobj)->enum_backing_type != IS_UNDEF) { smart_str_appendc(buf, ':'); - smart_str_appends(buf, zend_get_type_by_const(zobj->ce->enum_backing_type)); + smart_str_appends(buf, zend_get_type_by_const(OBJ_CE(zobj)->enum_backing_type)); } smart_str_appendc(buf, '\n'); } @@ -1050,7 +1051,6 @@ void zend_register_standard_ini_entries(void) /* {{{ */ } /* }}} */ - /* Unlink the global (r/o) copies of the class, function and constant tables, * and use a fresh r/w copy for the startup thread */ diff --git a/Zend/zend.h b/Zend/zend.h index d60da5f83ac11..ab00cc743252a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,17 +144,36 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; +typedef enum _zend_generic_param_variance { + ZEND_GENERIC_PARAM_INVARIANT, + ZEND_GENERIC_PARAM_IN, + ZEND_GENERIC_PARAM_OUT, +} zend_generic_param_variance; + +typedef struct _zend_generic_param { + zend_string *name; + zend_type bound_type; + zend_type default_type; + zend_generic_param_variance variance; +} zend_generic_param; + struct _zend_class_entry { char type; zend_string *name; - /* class_entry or string depending on ZEND_ACC_LINKED */ - union { - zend_class_entry *parent; - zend_string *parent_name; - }; int refcount; uint32_t ce_flags; + /* Before inheritance, this is either zero or one. + * After inheritance it includes grandparents as well. */ + uint32_t num_parents; + union { + /* List of parents. The direct parent is parents[0], + * below that are inherited grandparents. */ + zend_class_reference **parents; + /* Before linking, only the name of the direct parent is stored. */ + zend_packed_name_reference parent_name; + }; + int default_properties_count; int default_static_members_count; zval *default_properties_table; @@ -219,6 +238,14 @@ struct _zend_class_entry { uint32_t enum_backing_type; HashTable *backed_enum_table; + /* generic_params are the free generic parameters on this class. + * bound_generic_args are the bound generic parameters of parent classes. */ + uint32_t num_generic_params; + uint32_t num_required_generic_params; + uint32_t num_bound_generic_args; + zend_generic_param *generic_params; + zend_type *bound_generic_args; + union { struct { zend_string *filename; @@ -233,6 +260,10 @@ struct _zend_class_entry { } info; }; +typedef char zend_class_entry_storage[ZEND_CLASS_ENTRY_HEADER_SIZE + sizeof(zend_class_entry)]; + +#define ZEND_CES_TO_CE(ces) ((zend_class_entry*)((char*) (ces) + ZEND_CLASS_ENTRY_HEADER_SIZE)) + typedef struct _zend_utility_functions { void (*error_function)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); size_t (*printf_function)(const char *format, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 1, 2); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index d065c4d61232a..e701b1844f240 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -20,6 +20,7 @@ */ #include "zend.h" +#include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" #include "zend_modules.h" @@ -32,6 +33,7 @@ #include "zend_ini.h" #include "zend_enum.h" #include "zend_observer.h" +#include "zend_types.h" #include @@ -468,6 +470,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32_t arg_num) { zend_function *func = EG(current_execute_data)->func; + zend_class_entry *scope = EG(current_execute_data)->func->common.scope; ZEND_ASSERT(arg_num > 0); uint32_t arg_offset = arg_num - 1; if (arg_offset >= func->common.num_args) { @@ -481,7 +484,7 @@ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32 /* If no type is specified in arginfo, use the specified fallback_type determined through * zend_parse_parameters instead. */ - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, scope); const char *type = type_str ? ZSTR_VAL(type_str) : fallback_type; zend_error(E_DEPRECATED, "%s(): Passing null to parameter #%" PRIu32 "%s%s%s of type %s is deprecated", @@ -1424,7 +1427,7 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) { return FAILURE; } /* property initializers must always be evaluated with strict types */; - if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, /* strict */ 1))) { + if (UNEXPECTED(!zend_verify_property_type(NULL, prop_info, &tmp, /* strict */ 1))) { zval_ptr_dtor(&tmp); return FAILURE; } @@ -1491,8 +1494,8 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } } - if (class_type->parent) { - if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { + if (class_type->num_parents) { + if (UNEXPECTED(zend_update_class_constants(class_type->parents[0]->ce) != SUCCESS)) { return FAILURE; } } @@ -1639,13 +1642,13 @@ ZEND_API void object_properties_init(zend_object *object, zend_class_entry *clas ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties) /* {{{ */ { object->properties = properties; - if (object->ce->default_properties_count) { + if (OBJ_CE(object)->default_properties_count) { zval *prop; zend_string *key; zend_property_info *property_info; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(properties, key, prop) { - property_info = zend_get_property_info(object->ce, key, 1); + property_info = zend_get_property_info(OBJ_CE(object), key, 1); if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info && (property_info->flags & ZEND_ACC_STATIC) == 0) { @@ -1655,7 +1658,7 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti zval tmp; ZVAL_COPY_VALUE(&tmp, prop); - if (UNEXPECTED(!zend_verify_property_type(property_info, &tmp, 0))) { + if (UNEXPECTED(!zend_verify_property_type(object, property_info, &tmp, 0))) { continue; } ZVAL_COPY_VALUE(slot, &tmp); @@ -1689,14 +1692,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) EG(fake_scope) = zend_lookup_class(cname); zend_string_release_ex(cname, 0); } - property_info = zend_get_property_info(object->ce, pname, 1); + property_info = zend_get_property_info(OBJ_CE(object), pname, 1); zend_string_release_ex(pname, 0); EG(fake_scope) = prev_scope; } else { property_info = ZEND_WRONG_PROPERTY_INFO; } } else { - property_info = zend_get_property_info(object->ce, key, 1); + property_info = zend_get_property_info(OBJ_CE(object), key, 1); } if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info && @@ -1710,13 +1713,13 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zend_hash_update(object->properties, key, &tmp); } } else { - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", - ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + ZSTR_VAL(OBJ_NAME(object)), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); return; - } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(object)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + ZSTR_VAL(OBJ_NAME(object)), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); } if (!object->properties) { @@ -1726,12 +1729,12 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zval_add_ref(prop); } } else { - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_throw_error(NULL, "Cannot create dynamic property %s::$" ZEND_LONG_FMT, ZSTR_VAL(object->ce->name), h); + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$" ZEND_LONG_FMT, ZSTR_VAL(OBJ_CE(object)->name), h); return; - } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(object)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$" ZEND_LONG_FMT " is deprecated", - ZSTR_VAL(object->ce->name), h); + ZSTR_VAL(OBJ_CE(object)->name), h); } if (!object->properties) { @@ -1744,60 +1747,158 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) } /* }}} */ +ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce) +{ + const zend_type_list *type_args = &name_ref->args; + zend_string *key = name_ref->key; + + if (UNEXPECTED(type_args->num_types > ce->num_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + type_args->num_types); + return NULL; + } + + if (UNEXPECTED(type_args->num_types < ce->num_required_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + type_args->num_types); + return NULL; + } + + zend_class_reference *class_ref = emalloc(ZEND_CLASS_REF_SIZE(ce->num_generic_params)); + class_ref->ce = ce; + class_ref->key = key; + + uint8_t param_id = 0; + + for (; param_id < type_args->num_types; param_id++) { + if (ZEND_TYPE_IS_SET(ce->generic_params[param_id].bound_type)) { + if (UNEXPECTED(!zend_type_accepts(&ce->generic_params[param_id].bound_type, + NULL, &type_args->types[param_id], NULL, NULL))) { + zend_string *bound_type_name = zend_type_to_string( + ce->generic_params[param_id].bound_type, ce); + zend_string *actual_type_name = zend_type_to_string( + type_args->types[param_id], ce); + zend_throw_error(zend_ce_type_error, + "Class %s: Generic type argument #%d (%s) must be of type %s, %s given", + ZSTR_VAL(ce->name), param_id, + ZSTR_VAL(ce->generic_params[param_id].name), + ZSTR_VAL(bound_type_name), + ZSTR_VAL(actual_type_name)); + zend_string_release(bound_type_name); + zend_string_release(actual_type_name); + efree(class_ref); + return NULL; + } + } + class_ref->args.types[param_id] = type_args->types[param_id]; + } + + for (; param_id < ce->num_generic_params; param_id++) { + class_ref->args.types[param_id] = ce->generic_params[param_id].default_type; + } + + class_ref->args.num_types = param_id; + + if (ZSTR_HAS_CE_CACHE(key) && ZSTR_VALID_CE_CACHE(key) && + (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { + ZSTR_SET_CE_CACHE(key, class_ref); + } + + return class_ref; +} + /* This function requires 'properties' to contain all props declared in the * class and all props being public. If only a subset is given or the class * has protected members then you need to merge the properties separately by * calling zend_merge_properties(). */ -static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */ -{ - if (UNEXPECTED(class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_ENUM))) { - if (class_type->ce_flags & ZEND_ACC_INTERFACE) { - zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_TRAIT) { - zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_ENUM) { - zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(class_type->name)); +static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties) /* {{{ */ +{ + zend_class_entry *ce = class_ref->ce; + + if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_ENUM))) { + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(ce->name)); + } else if (ce->ce_flags & ZEND_ACC_TRAIT) { + zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(ce->name)); + } else if (ce->ce_flags & ZEND_ACC_ENUM) { + zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(ce->name)); } else { - zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name)); + zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(ce->name)); } ZVAL_NULL(arg); Z_OBJ_P(arg) = NULL; return FAILURE; } - if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) { + if (UNEXPECTED(class_ref->args.num_types > ce->num_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + class_ref->args.num_types); + ZVAL_NULL(arg); + Z_OBJ_P(arg) = NULL; + return FAILURE; + } else if (UNEXPECTED(class_ref->args.num_types < ce->num_required_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + class_ref->args.num_types == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + class_ref->args.num_types); + ZVAL_NULL(arg); + Z_OBJ_P(arg) = NULL; + return FAILURE; + } + + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { ZVAL_NULL(arg); Z_OBJ_P(arg) = NULL; return FAILURE; } } - if (class_type->create_object == NULL) { - zend_object *obj = zend_objects_new(class_type); + if (ce->create_object == NULL) { + zend_object *obj = zend_objects_new_ref(class_ref); ZVAL_OBJ(arg, obj); if (properties) { object_properties_init_ex(obj, properties); } else { - _object_properties_init(obj, class_type); + _object_properties_init(obj, ce); } } else { - ZVAL_OBJ(arg, class_type->create_object(class_type)); + ZVAL_OBJ(arg, ce->create_object(ce)); } return SUCCESS; } /* }}} */ -ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */ +ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties) /* {{{ */ { - return _object_and_properties_init(arg, class_type, properties); + return _object_and_properties_init(arg, class_ref, properties); +} +/* }}} */ + +ZEND_API zend_result object_init_ref(zval *arg, zend_class_reference *class_ref) /* {{{ */ +{ + return _object_and_properties_init(arg, class_ref, NULL); } /* }}} */ ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /* {{{ */ { - return _object_and_properties_init(arg, class_type, NULL); + return object_init_ref(arg, ZEND_CE_TO_REF(class_type)); } /* }}} */ @@ -2564,7 +2665,7 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_ zend_error(error_type, "%s::%s(): Parameter #%d ($%s) must be of type %s when declared", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name), arg_num + 1, ZSTR_VAL(fptr->common.arg_info[arg_num].name), - ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(arg_type)))); + ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(arg_type), ce))); } } @@ -2590,7 +2691,7 @@ static void zend_check_magic_method_return_type(const zend_class_entry *ce, cons if (extra_types || (is_complex_type && return_type != MAY_BE_OBJECT)) { zend_error(error_type, "%s::%s(): Return type must be %s when declared", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name), - ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(return_type)))); + ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(return_type), ce))); } } @@ -2771,19 +2872,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() static zend_always_inline void zend_normalize_internal_type(zend_type *type) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); zend_type *current; ZEND_TYPE_FOREACH(*type, current) { - if (ZEND_TYPE_HAS_NAME(*current)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current)); + if (ZEND_TYPE_HAS_PNR(*current)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_PNR_NAME(*current)); zend_alloc_ce_cache(name); ZEND_TYPE_SET_PTR(*current, name); } else if (ZEND_TYPE_HAS_LIST(*current)) { zend_type *inner; ZEND_TYPE_FOREACH(*current, inner) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner)); - if (ZEND_TYPE_HAS_NAME(*inner)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); + ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*inner)); + if (ZEND_TYPE_HAS_PNR(*inner)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_PNR_NAME(*inner)); zend_alloc_ce_cache(name); ZEND_TYPE_SET_PTR(*inner, name); } @@ -2862,7 +2962,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend internal_function->num_args--; } if (ZEND_TYPE_IS_SET(info->type)) { - if (ZEND_TYPE_HAS_NAME(info->type)) { + if (ZEND_TYPE_HAS_LITERAL_NAME(info->type)) { const char *type_name = ZEND_TYPE_LITERAL_NAME(info->type); if (!scope && (!strcasecmp(type_name, "self") || !strcasecmp(type_name, "parent"))) { zend_error_noreturn(E_CORE_ERROR, "Cannot declare a return type of %s outside of a class scope", type_name); @@ -2975,7 +3075,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend // As a temporary workaround, we split the type name on `|` characters, // converting it to an union type if necessary. const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type); - new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; size_t num_types = 1; const char *p = class_name; @@ -2989,7 +3088,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1); zend_alloc_ce_cache(str); ZEND_TYPE_SET_PTR(new_arg_info[i].type, str); - new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; + new_arg_info[i].type.type_mask |= _ZEND_TYPE_PNR_BIT; } else { /* Union type */ zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); @@ -3003,7 +3102,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend const char *end = strchr(start, '|'); zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1); zend_alloc_ce_cache(str); - list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0); + list->types[j] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(str), 0, 0); if (!end) { break; } @@ -3287,8 +3386,12 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { - zend_class_entry *class_entry = malloc(sizeof(zend_class_entry)); - zend_string *lowercase_name; + zend_class_entry_storage *ref = (zend_class_entry_storage*) malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); + + zend_string *lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); + lowercase_name = zend_new_interned_string(lowercase_name); + + zend_class_entry *class_entry = zend_init_class_entry_header(ref, lowercase_name); *class_entry = *orig_class_entry; class_entry->type = ZEND_INTERNAL_CLASS; @@ -3301,10 +3404,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); } - lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); - lowercase_name = zend_new_interned_string(lowercase_name); zend_hash_update_ptr(CG(class_table), lowercase_name, class_entry); - zend_string_release_ex(lowercase_name, 1); if (class_entry->__tostring && !zend_string_equals_literal(class_entry->name, "Stringable") && !(class_entry->ce_flags & ZEND_ACC_TRAIT)) { @@ -3573,7 +3673,8 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"self\" in callables is deprecated"); } - fcc->called_scope = zend_get_called_scope(frame); + zend_class_reference *class_ref = zend_get_called_scope(frame); + fcc->called_scope = class_ref ? class_ref->ce : NULL; if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope)) { fcc->called_scope = scope; } @@ -3586,17 +3687,18 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc } else if (zend_string_equals_literal(lcname, "parent")) { if (!scope) { if (error) *error = estrdup("cannot access \"parent\" when no class scope is active"); - } else if (!scope->parent) { + } else if (!scope->num_parents) { if (error) *error = estrdup("cannot access \"parent\" when current class scope has no parent"); } else { if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"parent\" in callables is deprecated"); } - fcc->called_scope = zend_get_called_scope(frame); - if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parent)) { - fcc->called_scope = scope->parent; + zend_class_reference *class_ref = zend_get_called_scope(frame); + fcc->called_scope = class_ref ? class_ref->ce : NULL; + if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parents[0]->ce)) { + fcc->called_scope = scope->parents[0]->ce; } - fcc->calling_scope = scope->parent; + fcc->calling_scope = scope->parents[0]->ce; if (!fcc->object) { fcc->object = zend_get_this_object(frame); } @@ -3604,7 +3706,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc ret = 1; } } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_STATIC))) { - zend_class_entry *called_scope = zend_get_called_scope(frame); + zend_class_reference *called_scope = zend_get_called_scope(frame); if (!called_scope) { if (error) *error = estrdup("cannot access \"static\" when no class scope is active"); @@ -3612,8 +3714,8 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"static\" in callables is deprecated"); } - fcc->called_scope = called_scope; - fcc->calling_scope = called_scope; + fcc->called_scope = called_scope->ce; + fcc->calling_scope = called_scope->ce; if (!fcc->object) { fcc->object = zend_get_this_object(frame); } @@ -3627,15 +3729,15 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, scope) && + instanceof_function(OBJ_CE(object), scope) && instanceof_function(scope, ce)) { fcc->object = object; - fcc->called_scope = object->ce; + fcc->called_scope = OBJ_CE(object); } else { fcc->called_scope = ce; } } else { - fcc->called_scope = fcc->object ? fcc->object->ce : ce; + fcc->called_scope = fcc->object ? OBJ_CE(fcc->object) : ce; } *strict_class = 1; ret = 1; @@ -3728,20 +3830,21 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ cname = zend_string_init_interned(Z_STRVAL_P(callable), clen, 0); if (ZSTR_HAS_CE_CACHE(cname) && ZSTR_GET_CE_CACHE(cname)) { - fcc->calling_scope = ZSTR_GET_CE_CACHE(cname); + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(cname); + fcc->calling_scope = class_ref->ce; if (scope && !fcc->object) { zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, scope) && + instanceof_function(OBJ_CE(object), scope) && instanceof_function(scope, fcc->calling_scope)) { fcc->object = object; - fcc->called_scope = object->ce; + fcc->called_scope = OBJ_CE(object); } else { fcc->called_scope = fcc->calling_scope; } } else { - fcc->called_scope = fcc->object ? fcc->object->ce : fcc->calling_scope; + fcc->called_scope = fcc->object ? OBJ_CE(fcc->object) : fcc->calling_scope; } strict_class = 1; } else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error, suppress_deprecation || ce_org != NULL)) { @@ -3849,7 +3952,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ if (call_via_handler && !fcc->object) { zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, fcc->calling_scope)) { + instanceof_function(OBJ_CE(object), fcc->calling_scope)) { fcc->object = object; } } @@ -3898,7 +4001,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ zend_string_release_ex(mname, 0); if (fcc->object) { - fcc->called_scope = fcc->object->ce; + fcc->called_scope = OBJ_CE(fcc->object); if (fcc->function_handler && (fcc->function_handler->common.fn_flags & ZEND_ACC_STATIC)) { fcc->object = NULL; @@ -3914,7 +4017,7 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj switch (Z_TYPE_P(callable)) { case IS_STRING: if (object) { - return zend_create_member_string(object->ce->name, Z_STR_P(callable)); + return zend_create_member_string(OBJ_NAME(object), Z_STR_P(callable)); } return zend_string_copy(Z_STR_P(callable)); @@ -3988,7 +4091,7 @@ ZEND_API bool zend_is_callable_at_frame( case IS_STRING: if (object) { fcc->object = object; - fcc->calling_scope = object->ce; + fcc->calling_scope = OBJ_CE(object); } if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { @@ -4869,7 +4972,7 @@ ZEND_API zend_result zend_update_static_property_ex(zend_class_entry *scope, zen Z_TRY_ADDREF_P(value); if (ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_COPY_VALUE(&tmp, value); - if (!zend_verify_property_type(prop_info, &tmp, /* strict */ 0)) { + if (!zend_verify_property_type(NULL, prop_info, &tmp, /* strict */ 0)) { Z_TRY_DELREF_P(value); return FAILURE; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 6e44cdd7628b0..1b46867b2cbda 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -25,6 +25,7 @@ #include "zend_modules.h" #include "zend_list.h" #include "zend_operators.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_execute.h" #include "zend_type_info.h" @@ -303,7 +304,7 @@ typedef struct _zend_fcall_info_cache { class_container.__debugInfo = NULL; \ class_container.__serialize = NULL; \ class_container.__unserialize = NULL; \ - class_container.parent = NULL; \ + class_container.num_parents = 0; \ class_container.num_interfaces = 0; \ class_container.trait_names = NULL; \ class_container.num_traits = 0; \ @@ -531,11 +532,13 @@ ZEND_API const char *zend_get_type_by_const(int type); #define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) #define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) ZEND_API void object_init(zval *arg); -ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce); -ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties); +ZEND_API zend_result object_init_ref(zval *arg, zend_class_reference *class_ref); +ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type); +ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties); ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type); ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties); ZEND_API void object_properties_load(zend_object *object, HashTable *properties); +ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce); ZEND_API void zend_merge_properties(zval *obj, HashTable *properties); @@ -850,7 +853,7 @@ static zend_always_inline void zend_call_known_instance_method( zend_function *fn, zend_object *object, zval *retval_ptr, uint32_t param_count, zval *params) { - zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params, NULL); + zend_call_known_function(fn, object, OBJ_CE(object), retval_ptr, param_count, params, NULL); } static zend_always_inline void zend_call_known_instance_method_with_0_params( @@ -2514,7 +2517,7 @@ static zend_always_inline bool zend_parse_arg_obj_or_class_name( return *destination != NULL; } else if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { - *destination = Z_OBJ_P(arg)->ce; + *destination = Z_OBJCE_P(arg); } else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) { *destination = NULL; } else { @@ -2539,6 +2542,11 @@ static zend_always_inline bool zend_parse_arg_obj_or_str( return zend_parse_arg_str(arg, destination_string, allow_null, arg_num); } +/* Get root class at start of "extends" chain. */ +static zend_always_inline zend_class_entry *zend_class_entry_get_root(zend_class_entry *ce) { + return ce->num_parents ? ce->parents[ce->num_parents - 1]->ce : ce; +} + END_EXTERN_C() #endif /* ZEND_API_H */ diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 525d9dfe9a742..1c731f655c860 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -604,6 +604,9 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( ZVAL_EMPTY_STRING(result); } break; + case ZEND_AST_CLASS_REF: + // TODO + return zend_ast_evaluate(&op1, ast->child[0], scope); case ZEND_AST_CLASS_NAME: if (!scope) { zend_throw_error(NULL, "Cannot use \"self\" when no class scope is active"); @@ -612,12 +615,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( if (ast->attr == ZEND_FETCH_CLASS_SELF) { ZVAL_STR_COPY(result, scope->name); } else if (ast->attr == ZEND_FETCH_CLASS_PARENT) { - if (!scope->parent) { + if (!scope->num_parents) { zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); return FAILURE; } - ZVAL_STR_COPY(result, scope->parent->name); + ZVAL_STR_COPY(result, scope->parents[0]->ce->name); } else { ZEND_ASSERT(0 && "Should have errored during compilation"); } @@ -833,7 +836,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } case ZEND_AST_CLASS_CONST: { - zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->kind == ZEND_AST_CLASS_REF); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + + zend_string *class_name = zend_ast_get_str(class_ast->child[0]); if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) { return FAILURE; } @@ -869,7 +879,13 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } case ZEND_AST_NEW: { - zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->kind == ZEND_AST_CLASS_REF); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + zend_class_entry *ce = zend_ast_fetch_class(class_ast->child[0], scope); if (!ce) { return FAILURE; } @@ -991,7 +1007,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } zend_object *zobj = Z_OBJ(op1); - if (!(zobj->ce->ce_flags & ZEND_ACC_ENUM)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM)) { zend_throw_error(NULL, "Fetching properties on non-enums in constant expressions is not allowed"); zval_ptr_dtor_nogc(&op1); zval_ptr_dtor_nogc(&op2); @@ -1328,6 +1344,16 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int zend_ast_export_ex(str, ast, priority, indent); } +static ZEND_COLD void zend_ast_export_class_name(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_CLASS_REF) { + ZEND_ASSERT(ast->child[1] == NULL && "Generic params not supported yet"); + zend_ast_export_ns_name(str, ast->child[0], priority, indent); + return; + } + zend_ast_export_ex(str, ast, priority, indent); +} + static ZEND_COLD bool zend_ast_valid_var_char(char ch) { unsigned char c = (unsigned char)ch; @@ -1440,7 +1466,7 @@ static ZEND_COLD void zend_ast_export_name_list_ex(smart_str *str, zend_ast_list if (i != 0) { smart_str_appends(str, separator); } - zend_ast_export_name(str, list->child[i], 0, indent); + zend_ast_export_class_name(str, list->child[i], 0, indent); i++; } } @@ -1604,7 +1630,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_decl *decl, int indent) { if (decl->child[0]) { smart_str_appends(str, " extends "); - zend_ast_export_ns_name(str, decl->child[0], 0, indent); + zend_ast_export_class_name(str, decl->child[0], 0, indent); } if (decl->child[1]) { smart_str_appends(str, " implements "); @@ -1624,7 +1650,7 @@ static ZEND_COLD void zend_ast_export_attribute_group(smart_str *str, zend_ast * if (i) { smart_str_appends(str, ", "); } - zend_ast_export_ns_name(str, attr->child[0], 0, indent); + zend_ast_export_class_name(str, attr->child[0], 0, indent); if (attr->child[1]) { smart_str_appendc(str, '('); @@ -1686,7 +1712,7 @@ static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int in if (ast->attr & ZEND_TYPE_NULLABLE) { smart_str_appendc(str, '?'); } - zend_ast_export_ns_name(str, ast, 0, indent); + zend_ast_export_class_name(str, ast, 0, indent); } #define BINARY_OP(_op, _p, _pl, _pr) do { \ @@ -2095,7 +2121,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], 0, indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], 0, indent); break; @@ -2109,7 +2135,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "..."); break; case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2126,7 +2152,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio EMPTY_SWITCH_DEFAULT_CASE() } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); } smart_str_appends(str, "::class"); break; @@ -2204,7 +2230,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } zend_ast_export_class_no_header(str, decl, indent); } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2213,7 +2239,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_INSTANCEOF: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, " instanceof "); - zend_ast_export_ns_name(str, ast->child[1], 0, indent); + zend_ast_export_class_name(str, ast->child[1], 0, indent); break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); @@ -2341,7 +2367,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_METHOD_REFERENCE: if (ast->child[0]) { - zend_ast_export_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); } zend_ast_export_name(str, ast->child[1], 0, indent); @@ -2395,7 +2421,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], 0, indent); smart_str_appendc(str, '('); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index b5001a9ff8f54..b7cbb6ab1ac3e 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -67,6 +67,8 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, ZEND_AST_MODIFIER_LIST, + ZEND_AST_GENERIC_PARAM_LIST, + ZEND_AST_GENERIC_ARG_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -149,6 +151,7 @@ enum _zend_ast_kind { ZEND_AST_MATCH, ZEND_AST_MATCH_ARM, ZEND_AST_NAMED_ARG, + ZEND_AST_CLASS_REF, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -170,6 +173,7 @@ enum _zend_ast_kind { ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, ZEND_AST_ENUM_CASE, + ZEND_AST_GENERIC_PARAM, /* 5 child nodes */ ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 74290c1f5aee7..1a062a4e83146 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -31,6 +31,7 @@ #include "zend_generators.h" #include "zend_builtin_functions_arginfo.h" #include "zend_smart_str.h" +#include "zend_types.h" /* }}} */ @@ -579,7 +580,7 @@ ZEND_FUNCTION(get_class) /* {{{ Retrieves the "Late Static Binding" class name */ ZEND_FUNCTION(get_called_class) { - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_NONE(); @@ -589,7 +590,7 @@ ZEND_FUNCTION(get_called_class) RETURN_THROWS(); } - RETURN_STR_COPY(called_scope->name); + RETURN_STR_COPY(called_scope->ce->name); } /* }}} */ @@ -611,8 +612,8 @@ ZEND_FUNCTION(get_parent_class) ce = zend_get_executed_scope(); } - if (ce && ce->parent) { - RETURN_STR_COPY(ce->parent->name); + if (ce && ce->num_parents) { + RETURN_STR_COPY(ce->parents[0]->ce->name); } else { RETURN_FALSE; } @@ -773,7 +774,7 @@ ZEND_FUNCTION(get_object_vars) RETURN_EMPTY_ARRAY(); } - if (!zobj->ce->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) { + if (!OBJ_CE(zobj)->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) { /* fast copy */ if (EXPECTED(zobj->handlers == &std_object_handlers)) { RETURN_ARR(zend_proptable_to_symtable(properties, 0)); @@ -840,7 +841,7 @@ ZEND_FUNCTION(get_mangled_object_vars) } properties = zend_proptable_to_symtable(properties, - (obj->ce->default_properties_count || + (OBJ_CE(obj)->default_properties_count || obj->handlers != &std_object_handlers || GC_IS_RECURSIVE(properties))); RETURN_ARR(properties); @@ -995,8 +996,9 @@ static inline void class_exists_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, in ZEND_PARSE_PARAMETERS_END(); if (ZSTR_HAS_CE_CACHE(name)) { - ce = ZSTR_GET_CE_CACHE(name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; RETURN_BOOL(((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags)); } } @@ -1842,7 +1844,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int if (func->common.scope) { ZVAL_STR_COPY(&tmp, func->common.scope->name); } else if (object->handlers->get_class_name == zend_std_get_class_name) { - ZVAL_STR_COPY(&tmp, object->ce->name); + ZVAL_STR_COPY(&tmp, OBJ_CE(object)->name); } else { ZVAL_STR(&tmp, object->handlers->get_class_name(object)); } diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index f1b29af7967c9..c702f03d9c8bd 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -22,11 +22,13 @@ #include "zend_API.h" #include "zend_closures.h" #include "zend_exceptions.h" +#include "zend_execute.h" #include "zend_interfaces.h" #include "zend_objects.h" #include "zend_objects_API.h" #include "zend_globals.h" #include "zend_closures_arginfo.h" +#include "zend_types.h" typedef struct _zend_closure { zend_object std; @@ -140,7 +142,7 @@ ZEND_METHOD(Closure, call) closure = (zend_closure *) Z_OBJ_P(ZEND_THIS); newobj = Z_OBJ_P(newthis); - newclass = newobj->ce; + newclass = OBJ_CE(newobj); if (!zend_valid_closure_binding(closure, newthis, newclass)) { return; @@ -224,7 +226,7 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); if (scope_obj) { - ce = scope_obj->ce; + ce = OBJ_CE(scope_obj); } else if (scope_str) { if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) { ce = closure->func.common.scope; @@ -318,7 +320,8 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ { } fcc.object = fci.object = Z_OBJ_P(ZEND_THIS); - fcc.called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + fcc.called_scope = class_ref ? class_ref->ce : NULL; zend_call_function(&fci, &fcc); @@ -339,7 +342,7 @@ static zend_result zend_create_closure_from_callable(zval *return_value, zval *c mptr = fcc.function_handler; if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { /* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */ - if (fcc.object && fcc.object->ce == zend_ce_closure + if (fcc.object && OBJ_CE(fcc.object) == zend_ce_closure && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { RETVAL_OBJ_COPY(fcc.object); zend_free_trampoline(mptr); @@ -793,7 +796,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) { /* avoid infinity recursion, by taking handler from nested closure */ zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func)); - ZEND_ASSERT(nested->std.ce == zend_ce_closure); + ZEND_ASSERT(OBJ_CE(&nested->std) == zend_ce_closure); closure->orig_internal_handler = nested->orig_internal_handler; } else { closure->orig_internal_handler = closure->func.internal_function.handler; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index afce5150e08d3..d0713d0cee2b7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -20,6 +20,7 @@ #include #include "zend.h" +#include "zend_ast.h" #include "zend_attributes.h" #include "zend_compile.h" #include "zend_constants.h" @@ -27,6 +28,9 @@ #include "zend_API.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "zend_operators.h" +#include "zend_portability.h" +#include "zend_types.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" @@ -35,6 +39,9 @@ #include "zend_enum.h" #include "zend_observer.h" #include "zend_call_stack.h" +#include "zend_smart_str.h" +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_optimizer.h" // TODO: for revert_pass_two #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -630,6 +637,24 @@ static int zend_add_class_name_literal(zend_string *name) /* {{{ */ } /* }}} */ +static int zend_add_pnr_literal(zend_packed_name_reference pnr) /* {{{ */ +{ + zend_string *name = ZEND_PNR_GET_NAME(pnr); + + zval zpnr; + ZVAL_PNR(&zpnr, pnr); + + /* Original PNR */ + int ret = zend_add_literal(&zpnr); + + /* Lowercased name */ + zend_string *lc_name = zend_string_tolower(name); + zend_add_literal_string(&lc_name); + + return ret; +} +/* }}} */ + static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {{{ */ { zend_string *tmp_name; @@ -1307,12 +1332,13 @@ static zend_string *add_type_string(zend_string *type, zend_string *new_type, bo return result; } -static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scope) { - if (scope) { +static zend_string *resolve_class_name( + zend_string *name, const zend_class_entry *scope, bool resolve) { + if (resolve && scope) { if (zend_string_equals_literal_ci(name, "self")) { name = scope->name; - } else if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { - name = scope->parent->name; + } else if (zend_string_equals_literal_ci(name, "parent") && scope->num_parents) { + name = scope->parents[0]->ce->name; } } @@ -1326,20 +1352,69 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop return zend_string_copy(name); } +static zend_string *zend_format_generic_name(zend_string *name, zend_type_list *args) { + if (args->num_types == 0) { + return zend_string_copy(name); + } else { + smart_str str = {0}; + smart_str_append(&str, name); + smart_str_appendc(&str, '<'); + for (uint32_t i = 0; i < args->num_types; i++) { + if (i != 0) { + smart_str_appends(&str, ", "); + } + zend_string *type_str = zend_type_to_string(args->types[i], NULL); + smart_str_append(&str, type_str); + zend_string_release(type_str); + } + smart_str_appendc(&str, '>'); + return smart_str_extract(&str); + } +} + +static zend_string *zend_class_ref_to_string(zend_class_reference *ce_ref) { + return zend_format_generic_name(ce_ref->ce->name, &ce_ref->args); +} + +static zend_string *zend_name_ref_to_string(zend_name_reference *name_ref) { + return zend_format_generic_name(name_ref->name, &name_ref->args); +} + +static zend_string *zend_pnr_to_string(zend_packed_name_reference pnr) { + if (ZEND_PNR_IS_COMPLEX(pnr)) { + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + return zend_name_ref_to_string(ref); + } else { + return zend_string_copy(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } +} + static zend_string *add_intersection_type(zend_string *str, - zend_type_list *intersection_type_list, zend_class_entry *scope, - bool is_bracketed) + zend_type_list *intersection_type_list, const zend_class_entry *scope, + bool is_bracketed, bool resolve) { zend_type *single_type; zend_string *intersection_str = NULL; ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type)); - zend_string *name = ZEND_TYPE_NAME(*single_type); - zend_string *resolved = resolve_class_name(name, scope); - intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); - zend_string_release(resolved); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*single_type); + zend_string *resolved = resolve_class_name(name, scope, resolve); + intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); + zend_string_release(resolved); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*single_type)); + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*single_type); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + intersection_str = add_type_string(intersection_str, param->name, /* is_intersection */ true); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + intersection_str = add_type_string(intersection_str, id_str, /* is_intersection */ true); + zend_string_release(id_str); + } + } } ZEND_TYPE_LIST_FOREACH_END(); ZEND_ASSERT(intersection_str); @@ -1354,30 +1429,66 @@ static zend_string *add_intersection_type(zend_string *str, return str; } -zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) { + +static zend_string *zend_type_to_string_impl( + zend_type type, const zend_class_entry *scope, bool resolve) { zend_string *str = NULL; /* Pure intersection type */ if (ZEND_TYPE_IS_INTERSECTION(type)) { ZEND_ASSERT(!ZEND_TYPE_IS_UNION(type)); - str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false); + str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false, resolve); } else if (ZEND_TYPE_HAS_LIST(type)) { /* A union type might not be a list */ zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { - str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true); + str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true, resolve); continue; } - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type)); - zend_string *name = ZEND_TYPE_NAME(*list_type); - zend_string *resolved = resolve_class_name(name, scope); - str = add_type_string(str, resolved, /* is_intersection */ false); - zend_string_release(resolved); + if (ZEND_TYPE_HAS_CLASS_REF(*list_type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(*list_type); + zend_string *name = zend_class_ref_to_string(ce_ref); + str = add_type_string(str, name, /* is_intersection */ false); + zend_string_release(name); + } else if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = zend_pnr_to_string(ZEND_TYPE_PNR(*list_type)); + zend_string *resolved = resolve_class_name(name, scope, resolve); + str = add_type_string(str, resolved, /* is_intersection */ false); + zend_string_release(resolved); + zend_string_release(name); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = add_type_string(str, param->name, /* is_intersection */ false); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + str = add_type_string(str, id_str, /* is_intersection */ false); + zend_string_release(id_str); + } + } } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(type)) { - str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name = zend_pnr_to_string(ZEND_TYPE_PNR(type)); + str = resolve_class_name(name, scope, resolve); + zend_string_release(name); + } else if (ZEND_TYPE_HAS_CLASS_REF(type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(type); + str = zend_class_ref_to_string(ce_ref); + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = zend_string_copy(param->name); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + str = add_type_string(str, id_str, /* is_intersection */ false); + zend_string_release(id_str); + } } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); @@ -1391,9 +1502,9 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC); // During compilation of eval'd code the called scope refers to the scope calling the eval if (scope && !zend_is_compiling()) { - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); if (called_scope) { - name = called_scope->name; + name = called_scope->ce->name; } } str = add_type_string(str, name, /* is_intersection */ false); @@ -1444,8 +1555,111 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop return str; } -ZEND_API zend_string *zend_type_to_string(zend_type type) { - return zend_type_to_string_resolved(type, NULL); +ZEND_API zend_string *zend_type_to_string(zend_type type, const zend_class_entry *scope) { + return zend_type_to_string_impl(type, scope, 0); +} + +zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry *scope) { + return zend_type_to_string_impl(type, scope, 1); +} + +#define zend_type_to_key_append_separator() \ + if (smart_str_get_len(key) != orig_len) { \ + smart_str_appendc(key, '|'); \ + } + +static void zend_type_to_key_impl(smart_str *key, zend_type type) { + size_t orig_len = smart_str_get_len(key); + + if (ZEND_TYPE_IS_INTERSECTION(type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + smart_str_appendc(key, '&'); + zend_type_to_key_impl(key, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + return; + } else if (ZEND_TYPE_HAS_LIST(type)) { + /* A union type might not be a list */ + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + smart_str_appendc(key, '|'); + zend_type_to_key_impl(key, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_packed_name_reference pnr = ZEND_TYPE_PNR(type); + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_string *lcname = zend_string_tolower(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + smart_str_append(key, lcname); + zend_string_release(lcname); + } else { + smart_str_append(key, ZEND_PNR_COMPLEX_GET_KEY(pnr)); + } + } else if (ZEND_TYPE_HAS_CLASS_REF(type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(type); + smart_str_append(key, ce_ref->key); + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + smart_str_appendc(key, 0); + smart_str_appendl(key, (char*)&generic_param_id, sizeof(generic_param_id)); + } + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + + if (type_mask == MAY_BE_ANY) { + smart_str_setl(key, ZSTR_VAL(ZSTR_KNOWN(ZEND_STR_MIXED)), ZSTR_LEN(ZSTR_KNOWN(ZEND_STR_MIXED))); + return; + } + if (type_mask & MAY_BE_STATIC) { + ZEND_ASSERT(0 && "Unsupported yet"); + } + if (type_mask & MAY_BE_CALLABLE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_CALLABLE)); + } + if (type_mask & MAY_BE_OBJECT) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_OBJECT)); + } + if (type_mask & MAY_BE_ARRAY) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_ARRAY)); + } + if (type_mask & MAY_BE_STRING) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_STRING)); + } + if (type_mask & MAY_BE_LONG) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_INT)); + } + if (type_mask & MAY_BE_DOUBLE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FLOAT)); + } + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_BOOL)); + } else if (type_mask & MAY_BE_FALSE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FALSE)); + } else if (type_mask & MAY_BE_TRUE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_TRUE)); + } + if (type_mask & MAY_BE_VOID) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_VOID)); + } + if (type_mask & MAY_BE_NEVER) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NEVER)); + } + + if (type_mask & MAY_BE_NULL) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE)); + } } static bool is_generator_compatible_class_type(zend_string *name) { @@ -1467,8 +1681,8 @@ static void zend_mark_function_as_generator(void) /* {{{ */ if (!valid_type) { zend_type *single_type; ZEND_TYPE_FOREACH(return_type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type) - && is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) { + if (ZEND_TYPE_HAS_PNR(*single_type) + && is_generator_compatible_class_type(ZEND_TYPE_PNR_NAME(*single_type))) { valid_type = 1; break; } @@ -1476,7 +1690,7 @@ static void zend_mark_function_as_generator(void) /* {{{ */ } if (!valid_type) { - zend_string *str = zend_type_to_string(return_type); + zend_string *str = zend_type_to_string(return_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Generator return type must be a supertype of Generator, %s given", ZSTR_VAL(str)); @@ -1656,6 +1870,15 @@ static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint3 } /* }}} */ +static zend_ast *unwrap_non_generic_class_ref(zend_ast *class_ast) +{ + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + return class_ast->child[0]; +} + uint32_t zend_get_class_fetch_type(const zend_string *name) /* {{{ */ { if (zend_string_equals_literal_ci(name, "self")) { @@ -1681,15 +1904,128 @@ static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ } /* }}} */ -static zend_string *zend_resolve_const_class_name_reference(zend_ast *ast, const char *type) +static zend_string *zend_resolve_const_class_name_reference(zend_ast *class_ast, const char *type) { - zend_string *class_name = zend_ast_get_str(ast); - if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(ast)) { + zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + zend_string *class_name = zend_ast_get_str(name_ast); + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(name_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as %s, as it is reserved", ZSTR_VAL(class_name), type); } - return zend_resolve_class_name(class_name, ast->attr); + return zend_resolve_class_name(class_name, name_ast->attr); +} + +static zend_type zend_compile_typename(zend_ast *ast); + +static zend_always_inline void zend_name_reference_key_start(smart_str *key, zend_string *class_name) +{ + zend_string *lcname = zend_string_tolower(class_name); + smart_str_append(key, lcname); + zend_string_release(lcname); + + smart_str_appendc(key, '<'); +} + +static zend_always_inline void zend_name_reference_key_start_lc(smart_str *key, zend_string *lcname) +{ + smart_str_append(key, lcname); + smart_str_appendc(key, '<'); +} + +static zend_always_inline void zend_name_reference_key_add_type(smart_str *key, uint32_t nth_type, zend_type type) +{ + if (nth_type != 0) { + smart_str_appendc(key, ','); + } + + zend_type_to_key_impl(key, type); +} + +static zend_always_inline void zend_name_reference_key_end(smart_str *key) +{ + smart_str_appendc(key, '>'); +} + +void zend_compile_name_reference_key(zend_name_reference *ref, zend_string *lcname) { + if (ref->args.num_types == 0) { + if (lcname) { + ref->key = zend_new_interned_string(lcname); + } else { + lcname = zend_string_tolower(ref->name); + ref->key = zend_new_interned_string(lcname); + zend_string_release(lcname); + } + } else { + smart_str key = {0}; + if (lcname) { + zend_name_reference_key_start_lc(&key, lcname); + } else { + zend_name_reference_key_start(&key, ref->name); + } + for (uint32_t i = 0; i < ref->args.num_types; i++) { + zend_name_reference_key_add_type(&key, i, ref->args.types[i]); + } + zend_name_reference_key_end(&key); + ref->key = zend_new_interned_string(smart_str_extract(&key)); + } +} + +static zend_name_reference *zend_compile_name_reference( + zend_string *name, zend_ast *args_ast) { + zend_ast_list *list = args_ast ? zend_ast_get_list(args_ast) : NULL; + uint32_t num_types = list ? list->children : 0; + size_t alloc_size = ZEND_CLASS_REF_SIZE(num_types); + zend_name_reference *ref = zend_arena_alloc(&CG(arena), alloc_size); + ref->name = name; + if (num_types == 0) { + ref->key = zend_new_interned_string(zend_string_tolower(name)); + } else { + ZEND_ASSERT(list); + smart_str key = {0}; + zend_name_reference_key_start(&key, name); + for (uint32_t i = 0; i < num_types; i++) { + zend_ast *type_ast = list->child[i]; + ref->args.types[i] = zend_compile_typename(type_ast); + zend_name_reference_key_add_type(&key, i, ref->args.types[i]); + } + zend_name_reference_key_end(&key); + ref->key = zend_new_interned_string(smart_str_extract(&key)); + } + ref->args.num_types = num_types; + return ref; +} + +static zend_packed_name_reference zend_compile_pnr( + zend_string *name, zend_ast *args_ast) { + if (args_ast) { + return ZEND_PNR_ENCODE_REF(zend_compile_name_reference(name, args_ast)); + } else { + return ZEND_PNR_ENCODE_NAME(name); + } +} + +ZEND_API void zend_pnr_destroy(zend_packed_name_reference pnr) { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } else { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + zend_string_release(name_ref->name); + zend_string_release(name_ref->key); + } +} + +static zend_packed_name_reference zend_compile_default_pnr( + zend_ast *class_ast, const char *type) { + zend_ast *name_ast = class_ast->child[0]; + zend_string *class_name = zend_ast_get_str(name_ast); + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(name_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as %s, as it is reserved", + ZSTR_VAL(class_name), type); + } + class_name = zend_resolve_class_name(class_name, name_ast->attr); + return zend_compile_pnr(class_name, class_ast->child[1]); } static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ @@ -1712,12 +2048,14 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c { uint32_t fetch_type; zval *class_name; + zend_ast *name_ast; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { return 0; } - class_name = zend_ast_get_zval(class_ast); + name_ast = unwrap_non_generic_class_ref(class_ast); + class_name = zend_ast_get_zval(name_ast); if (Z_TYPE_P(class_name) != IS_STRING) { zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); @@ -1734,16 +2072,16 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c } return 0; case ZEND_FETCH_CLASS_PARENT: - if (CG(active_class_entry) && CG(active_class_entry)->parent_name + if (CG(active_class_entry) && CG(active_class_entry)->num_parents && zend_is_scope_known()) { - ZVAL_STR_COPY(zv, CG(active_class_entry)->parent_name); + ZVAL_STR_COPY(zv, ZEND_PNR_GET_NAME(CG(active_class_entry)->parent_name)); return 1; } return 0; case ZEND_FETCH_CLASS_STATIC: return 0; case ZEND_FETCH_CLASS_DEFAULT: - ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast)); + ZVAL_STR(zv, zend_resolve_class_name_ast(name_ast)); return 1; EMPTY_SWITCH_DEFAULT_CASE() } @@ -1770,13 +2108,14 @@ static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry if (ce == scope) { return 1; } - if (!ce->parent) { + if (!ce->num_parents) { break; } if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - ce = ce->parent; + ce = ce->parents[0]->ce; } else { - ce = zend_hash_find_ptr_lc(CG(class_table), ce->parent_name); + zend_string *parent_name = ZEND_PNR_GET_NAME(ce->parent_name); + ce = zend_hash_find_ptr_lc(CG(class_table), parent_name); if (!ce) { break; } @@ -2016,11 +2355,14 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand ce->iterator_funcs_ptr = NULL; ce->arrayaccess_funcs_ptr = NULL; ce->get_static_method = NULL; - ce->parent = NULL; - ce->parent_name = NULL; + ce->num_parents = 0; + ce->parent_name = 0; ce->num_interfaces = 0; ce->interfaces = NULL; ce->num_traits = 0; + ce->num_generic_params = 0; + ce->num_required_generic_params = 0; + ce->num_bound_generic_args = 0; ce->trait_names = NULL; ce->trait_aliases = NULL; ce->trait_precedences = NULL; @@ -2697,13 +3039,14 @@ static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ +static inline bool zend_is_const_default_class_ref(zend_ast *class_ast) /* {{{ */ { - if (name_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { return 0; } - return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast); + return ZEND_FETCH_CLASS_DEFAULT == + zend_get_class_fetch_type_ast(unwrap_non_generic_class_ref(class_ast)); } /* }}} */ @@ -2751,14 +3094,14 @@ static inline void zend_set_class_name_op1(zend_op *opline, znode *class_node) / } /* }}} */ -static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t fetch_flags) /* {{{ */ +static void zend_compile_class_ref_ex(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ { uint32_t fetch_type; - if (name_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { znode name_node; - zend_compile_expr(&name_node, name_ast); + zend_compile_expr(&name_node, class_ast); if (name_node.op_type == IS_CONST) { zend_string *name; @@ -2787,25 +3130,44 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f return; } + zend_ast *name_ast = class_ast->child[0]; + /* Fully qualified names are always default refs */ if (name_ast->attr == ZEND_NAME_FQ) { result->op_type = IS_CONST; - ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + zend_string *name = zend_new_interned_string(zend_resolve_class_name_ast(name_ast)); + ZVAL_PNR(&result->u.constant, zend_compile_pnr(name, class_ast->child[1])); return; } fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { result->op_type = IS_CONST; - ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + zend_string *name = zend_new_interned_string(zend_resolve_class_name_ast(name_ast)); + ZVAL_PNR(&result->u.constant, zend_compile_pnr(name, class_ast->child[1])); } else { zend_ensure_valid_class_fetch_type(fetch_type); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } result->op_type = IS_UNUSED; result->u.op.num = fetch_type | fetch_flags; } } /* }}} */ +static void zend_compile_class_ref(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ +{ + if (class_ast->kind == ZEND_AST_CLASS_REF && class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + + zend_compile_class_ref_ex(result, class_ast, fetch_flags); +} +/* }}} */ + static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *name_ast = ast->child[0]; @@ -4850,20 +5212,28 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ /* anon class declaration */ zend_compile_class_decl(&class_node, class_ast, 0); } else { - zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); } opline = zend_emit_op(result, ZEND_NEW, NULL, NULL); if (class_node.op_type == IS_CONST) { opline->op1_type = IS_CONST; - opline->op1.constant = zend_add_class_name_literal( - Z_STR(class_node.u.constant)); + opline->op1.constant = zend_add_pnr_literal(Z_PNR(class_node.u.constant)); + + if (ZEND_PNR_IS_SIMPLE(Z_PNR(class_node.u.constant))) { + /* literal placeholder for type inference */ + zval zv; + ZVAL_NULL(&zv); + zend_add_literal(&zv); + } + opline->op2.num = zend_alloc_cache_slot(); } else { SET_NODE(opline->op1, &class_node); } + zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno); zend_do_free(&ctor_result); } @@ -6146,7 +6516,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */ opline->opcode = ZEND_CATCH; opline->op1_type = IS_CONST; opline->op1.constant = zend_add_class_name_literal( - zend_resolve_class_name_ast(class_ast)); + zend_resolve_class_name_ast(unwrap_non_generic_class_ref(class_ast))); opline->extended_value = zend_alloc_cache_slot(); if (var_name && zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -6410,6 +6780,20 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ } /* }}} */ +static uint32_t lookup_generic_param_id(zend_string *name) { + if (!CG(active_class_entry) || CG(active_class_entry)->num_generic_params == 0) { + return (uint32_t) -1; + } + + for (uint32_t i = 0; i < CG(active_class_entry)->num_generic_params; i++) { + zend_generic_param *param = &CG(active_class_entry)->generic_params[i]; + if (zend_string_equals(param->name, name)) { + return i; + } + } + return (uint32_t) -1; +} + static zend_type zend_compile_single_typename(zend_ast *ast) { ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE)); @@ -6421,11 +6805,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0); } else { - zend_string *class_name = zend_ast_get_str(ast); + ZEND_ASSERT(ast->kind == ZEND_AST_CLASS_REF); + zend_ast *name_ast = ast->child[0]; + zend_ast *args_ast = ast->child[1]; + zend_string *class_name = zend_ast_get_str(name_ast); uint8_t type_code = zend_lookup_builtin_type_by_name(class_name); if (type_code != 0) { - if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { + if ((name_ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { zend_error_noreturn(E_COMPILE_ERROR, "Type declaration '%s' must be unqualified", ZSTR_VAL(zend_string_tolower(class_name))); @@ -6442,17 +6829,27 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0); } else { const char *correct_name; - zend_string *orig_name = zend_ast_get_str(ast); - uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); + zend_string *orig_name = zend_ast_get_str(name_ast); + uint32_t generic_param_id = lookup_generic_param_id(orig_name); + if (name_ast->attr == ZEND_NAME_NOT_FQ && generic_param_id != (uint32_t) -1) { + if (CG(in_static_class_member)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use type parameter \"%s\" in static context", + ZSTR_VAL(orig_name)); + } + return (zend_type) ZEND_TYPE_INIT_GENERIC_PARAM(generic_param_id, 0); + } + + uint32_t fetch_type = zend_get_class_fetch_type_ast(name_ast); if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { - class_name = zend_resolve_class_name_ast(ast); + class_name = zend_resolve_class_name_ast(name_ast); zend_assert_valid_class_name(class_name); } else { zend_ensure_valid_class_fetch_type(fetch_type); zend_string_addref(class_name); } - if (ast->attr == ZEND_NAME_NOT_FQ + if (name_ast->attr == ZEND_NAME_NOT_FQ && !args_ast && zend_is_confusable_type(orig_name, &correct_name) && zend_is_not_imported(orig_name)) { const char *extra = @@ -6472,8 +6869,8 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } class_name = zend_new_interned_string(class_name); - zend_alloc_ce_cache(class_name); - return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0); + zend_packed_name_reference pnr = zend_compile_pnr(class_name, args_ast); + return (zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, _ZEND_TYPE_ARENA_BIT); } } } @@ -6501,7 +6898,7 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type) zend_type *inner_type; ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type) - if (zend_string_equals_ci(ZEND_TYPE_NAME(*inner_type), ZEND_TYPE_NAME(*outer_type))) { + if (zend_string_equals_ci(ZEND_TYPE_PNR_NAME(*inner_type), ZEND_TYPE_PNR_NAME(*outer_type))) { sum++; break; } @@ -6512,11 +6909,11 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type zend_string *smaller_type_str; zend_string *larger_type_str; if (flipped) { - smaller_type_str = zend_type_to_string(right_type); - larger_type_str = zend_type_to_string(left_type); + smaller_type_str = zend_type_to_string(right_type, CG(active_class_entry)); + larger_type_str = zend_type_to_string(left_type, CG(active_class_entry)); } else { - smaller_type_str = zend_type_to_string(left_type); - larger_type_str = zend_type_to_string(right_type); + smaller_type_str = zend_type_to_string(left_type, CG(active_class_entry)); + larger_type_str = zend_type_to_string(right_type, CG(active_class_entry)); } if (smaller_type_list->num_types == larger_type_list->num_types) { zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant with type %s", @@ -6535,9 +6932,9 @@ static void zend_is_intersection_type_redundant_by_single_type(zend_type interse zend_type *single_intersection_type = NULL; ZEND_TYPE_FOREACH(intersection_type, single_intersection_type) - if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_intersection_type), ZEND_TYPE_NAME(single_type))) { - zend_string *single_type_str = zend_type_to_string(single_type); - zend_string *complete_type = zend_type_to_string(intersection_type); + if (zend_string_equals_ci(ZEND_TYPE_PNR_NAME(*single_intersection_type), ZEND_TYPE_PNR_NAME(single_type))) { + zend_string *single_type_str = zend_type_to_string(single_type, CG(active_class_entry)); + zend_string *complete_type = zend_type_to_string(intersection_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant as it is more restrictive than type %s", ZSTR_VAL(complete_type), ZSTR_VAL(single_type_str)); } @@ -6553,15 +6950,23 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list zend_is_intersection_type_redundant_by_single_type(type_list->types[i], type); continue; } - if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) { - zend_string *single_type_str = zend_type_to_string(type); - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + if (ZEND_TYPE_HAS_PNR(type)) { + if (ZEND_TYPE_HAS_PNR(type_list->types[i]) + && zend_string_equals_ci(ZEND_TYPE_PNR_NAME(type_list->types[i]), ZEND_TYPE_PNR_NAME(type))) { + zend_string *single_type_str = zend_type_to_string(type, CG(active_class_entry)); + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(type)); + if (ZEND_TYPE_HAS_GENERIC_PARAM(type_list->types[i]) + && ZEND_TYPE_GENERIC_PARAM_ID(type_list->types[i]) == ZEND_TYPE_GENERIC_PARAM_ID(type)) { + zend_string *single_type_str = zend_type_to_string(type, CG(active_class_entry)); + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } } } } -static zend_type zend_compile_typename(zend_ast *ast); - static zend_type zend_compile_typename_ex( zend_ast *ast, bool force_allow_null, bool *forced_allow_null) /* {{{ */ { @@ -6631,7 +7036,8 @@ static zend_type zend_compile_typename_ex( uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & single_type_mask; if (type_mask_overlap) { zend_type overlap_type = ZEND_TYPE_INIT_MASK(type_mask_overlap); - zend_string *overlap_type_str = zend_type_to_string(overlap_type); + zend_string *overlap_type_str = + zend_type_to_string(overlap_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str)); } @@ -6646,11 +7052,15 @@ static zend_type zend_compile_typename_ex( if (ZEND_TYPE_IS_COMPLEX(single_type)) { if (!ZEND_TYPE_IS_COMPLEX(type) && !is_composite) { - /* The first class type can be stored directly as the type ptr payload. */ - ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); - ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(single_type) || ZEND_TYPE_HAS_GENERIC_PARAM(single_type)); + /* The first class type or generic type parameter can be + * stored as the type payload */ + uint32_t extra_type_mask = ZEND_TYPE_PURE_MASK(type); + type = single_type; + ZEND_TYPE_FULL_MASK(type) |= extra_type_mask; } else { if (type_list->num_types == 0) { + /* Switch from single name to name list. */ type_list->num_types = 1; type_list->types[0] = type; @@ -6681,7 +7091,7 @@ static zend_type zend_compile_typename_ex( uint32_t type_mask = ZEND_TYPE_FULL_MASK(type); if ((type_mask & MAY_BE_OBJECT) && ((!has_only_iterable_class && ZEND_TYPE_IS_COMPLEX(type)) || (type_mask & MAY_BE_STATIC))) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s contains both object and a class type, which is redundant", ZSTR_VAL(type_str)); @@ -6704,23 +7114,24 @@ static zend_type zend_compile_typename_ex( /* An intersection of union types cannot exist so invalidate it * Currently only can happen with iterable getting canonicalized to Traversable|array */ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(single_type)) { - zend_string *standard_type_str = zend_type_to_string(single_type); + zend_string *standard_type_str = zend_type_to_string(single_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str)); zend_string_release_ex(standard_type_str, false); } /* An intersection of standard types cannot exist so invalidate it */ - if (ZEND_TYPE_IS_ONLY_MASK(single_type)) { - zend_string *standard_type_str = zend_type_to_string(single_type); + if (!ZEND_TYPE_IS_COMPLEX(single_type)) { + zend_string *standard_type_str = zend_type_to_string(single_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str)); zend_string_release_ex(standard_type_str, false); } /* Check for "self" and "parent" too */ - if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self") - || zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) { + if (ZEND_TYPE_HAS_PNR(single_type) + && (zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "self") + || zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "parent"))) { zend_error_noreturn(E_COMPILE_ERROR, - "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type))); + "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_PNR_NAME(single_type))); } /* Add type to the type list */ @@ -6836,7 +7247,7 @@ static void zend_compile_attributes( "Cannot create Closure as attribute argument"); } - zend_string *name = zend_resolve_class_name_ast(el->child[0]); + zend_string *name = zend_resolve_class_name_ast(unwrap_non_generic_class_ref(el->child[0])); zend_string *lcname = zend_string_tolower_ex(name, false); zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL; @@ -7057,7 +7468,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 if (default_type != IS_UNDEF && default_type != IS_CONSTANT_AST && !force_nullable && !zend_is_valid_default_value(arg_info->type, &default_node.u.constant)) { - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as default value for parameter $%s of type %s", zend_get_type_by_const(default_type), @@ -7127,7 +7538,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 ZSTR_VAL(scope->name), ZSTR_VAL(name)); } if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_CALLABLE) { - zend_string *str = zend_type_to_string(arg_info->type); + zend_string *str = zend_type_to_string(arg_info->type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Property %s::$%s cannot have type %s", ZSTR_VAL(scope->name), ZSTR_VAL(name), ZSTR_VAL(str)); @@ -7553,6 +7964,11 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) closure_info info; memset(&info, 0, sizeof(closure_info)); + bool orig_in_static_class_member = CG(in_static_class_member); + if (orig_class_entry && (decl->flags & ZEND_ACC_STATIC)) { + CG(in_static_class_member) = true; + } + init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE); if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { @@ -7672,6 +8088,11 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) zend_emit_final_return(0); pass_two(CG(active_op_array)); + + zend_revert_pass_two(op_array); /* TODO */ + zend_symbolic_inference(op_array, &CG(arena)); + zend_redo_pass_two(op_array); + zend_oparray_context_end(&orig_oparray_context); /* Pop the loop variable stack separator */ @@ -7685,6 +8106,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) CG(active_op_array) = orig_op_array; CG(active_class_entry) = orig_class_entry; + CG(in_static_class_member) = orig_in_static_class_member; } /* }}} */ @@ -7702,6 +8124,10 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include properties", ZSTR_VAL(ce->name)); } + if (flags & ZEND_ACC_STATIC) { + CG(in_static_class_member) = true; + } + for (i = 0; i < children; ++i) { zend_property_info *info; zend_ast *prop_ast = list->child[i]; @@ -7717,7 +8143,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f type = zend_compile_typename(type_ast); if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_NEVER|MAY_BE_CALLABLE)) { - zend_string *str = zend_type_to_string(type); + zend_string *str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Property %s::$%s cannot have type %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(str)); @@ -7739,10 +8165,10 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv) && !zend_is_valid_default_value(type, &value_zv)) { - zend_string *str = zend_type_to_string(type); + zend_string *str = zend_type_to_string(type, CG(active_class_entry)); if (Z_TYPE(value_zv) == IS_NULL && !ZEND_TYPE_IS_INTERSECTION(type)) { ZEND_TYPE_FULL_MASK(type) |= MAY_BE_NULL; - zend_string *nullable_str = zend_type_to_string(type); + zend_string *nullable_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type %s may not be null. " @@ -7788,6 +8214,8 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0); } } + + CG(in_static_class_member) = false; } /* }}} */ @@ -7817,6 +8245,8 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_class_entry *ce = CG(active_class_entry); uint32_t i, children = list->children; + CG(in_static_class_member) = true; + for (i = 0; i < children; ++i) { zend_class_constant *c; zend_ast *const_ast = list->child[i]; @@ -7834,7 +8264,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER))) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Class constant %s::%s cannot have type %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -7851,7 +8281,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s", zend_zval_type_name(&value_zv), ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -7863,6 +8293,8 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0); } } + + CG(in_static_class_member) = false; } static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */ @@ -7948,7 +8380,7 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */ zend_ast *trait_ast = traits->child[i]; if (ce->ce_flags & ZEND_ACC_INTERFACE) { - zend_string *name = zend_ast_get_str(trait_ast); + zend_string *name = zend_ast_get_str(unwrap_non_generic_class_ref(trait_ast)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use traits inside of interfaces. " "%s is used in %s", ZSTR_VAL(name), ZSTR_VAL(ce->name)); } @@ -8025,7 +8457,7 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type type = zend_compile_typename(enum_backing_type_ast); uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); if (ZEND_TYPE_IS_COMPLEX(type) || (type_mask != MAY_BE_LONG && type_mask != MAY_BE_STRING)) { - zend_string *type_string = zend_type_to_string(type); + zend_string *type_string = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Enum backing type must be int or string, %s given", ZSTR_VAL(type_string)); @@ -8039,15 +8471,101 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +static zend_generic_param_variance zend_compile_generic_param_variance(zend_string *str) +{ + if (str == NULL) { + return ZEND_GENERIC_PARAM_INVARIANT; + } + if (zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), "in", 2) == 0) { + return ZEND_GENERIC_PARAM_IN; + } + if (zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), "out", 3) == 0) { + return ZEND_GENERIC_PARAM_OUT; + } + zend_error_noreturn(E_COMPILE_ERROR, + "Unexpected \"%s\", expected \"in\" or \"out\"", ZSTR_VAL(str)); +} + +static void zend_compile_generic_params(zend_ast *params_ast) +{ + zend_ast_list *list = zend_ast_get_list(params_ast); + zend_generic_param *generic_params = emalloc(list->children * sizeof(zend_generic_param)); + CG(active_class_entry)->generic_params = generic_params; + + bool have_optional = 0; + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *param_ast = list->child[i]; + zend_string *name = zend_ast_get_str(param_ast->child[1]); + zend_type bound_type = ZEND_TYPE_INIT_NONE(0); + zend_type default_type = ZEND_TYPE_INIT_NONE(0); + zend_generic_param_variance variance = ZEND_GENERIC_PARAM_INVARIANT; + + if (param_ast->child[0] != NULL) { + variance = zend_compile_generic_param_variance( + zend_ast_get_str(param_ast->child[0])); + } + + if (zend_string_equals(name, CG(active_class_entry)->name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic parameter %s has same name as class", ZSTR_VAL(name)); + } + + for (uint32_t j = 0; j < i; j++) { + if (zend_string_equals(name, generic_params[j].name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Duplicate generic parameter %s", ZSTR_VAL(name)); + } + } + + if (param_ast->child[2]) { + bound_type = zend_compile_typename(param_ast->child[2]); + } + if (param_ast->child[3]) { + default_type = zend_compile_typename(param_ast->child[3]); + } + + if (ZEND_TYPE_IS_SET(default_type)) { + have_optional = 1; + } else if (have_optional) { + zend_error_noreturn(E_COMPILE_ERROR, + "Required generic parameter %s follows optional", ZSTR_VAL(name)); + } + + generic_params[i].name = zend_string_copy(name); + generic_params[i].bound_type = bound_type; + generic_params[i].default_type = default_type; + generic_params[i].variance = variance; + // TODO: Validate potential additional constraints on the types. + // For example, can "void" be used? + + /* Update number of parameters on the fly, so that previous parameters can be + * referenced in the type bound or default of following parameters. */ + CG(active_class_entry)->num_generic_params = i + 1; + if (!have_optional) { + CG(active_class_entry)->num_required_generic_params = i + 1; + } + } +} + +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key) { + zend_class_reference *ref = (zend_class_reference*) ptr; + zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE); + ref->ce = ce; + ref->key = key; + zend_string_addref(key); + ref->args.num_types = 0; + return ce; +} + static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; zend_ast *extends_ast = decl->child[0]; zend_ast *implements_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; - zend_ast *enum_backing_type_ast = decl->child[4]; zend_string *name, *lcname; - zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); + void *ce_ref = zend_arena_alloc(&CG(arena), + sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); zend_op *opline; zend_class_entry *original_ce = CG(active_class_entry); @@ -8087,6 +8605,8 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } lcname = zend_new_interned_string(lcname); + zend_class_entry *ce = zend_init_class_entry_header(ce_ref, lcname); + ce->type = ZEND_USER_CLASS; ce->name = name; zend_initialize_class_data(ce, 1); @@ -8114,12 +8634,24 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; } - if (extends_ast) { - ce->parent_name = - zend_resolve_const_class_name_reference(extends_ast, "class name"); + CG(active_class_entry) = ce; + + if (!(ce->ce_flags & ZEND_ACC_ENUM)) { + zend_ast *generic_params_ast = decl->child[4]; + if (generic_params_ast) { + zend_compile_generic_params(generic_params_ast); + } + + if (!(decl->flags & ZEND_ACC_ANON_CLASS) && + ce->num_generic_params > 0 && ce->num_required_generic_params == 0) { + zend_alloc_ce_cache(ZEND_CE_TO_REF(ce)->key); + } } - CG(active_class_entry) = ce; + if (extends_ast) { + ce->parent_name = zend_compile_default_pnr(extends_ast, "class name"); + ce->num_parents = 1; + } if (decl->child[3]) { zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); @@ -8130,6 +8662,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } if (ce->ce_flags & ZEND_ACC_ENUM) { + zend_ast *enum_backing_type_ast = decl->child[4]; if (enum_backing_type_ast != NULL) { zend_compile_enum_backing_type(ce, enum_backing_type_ast); } @@ -8158,7 +8691,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) if (toplevel) { if (extends_ast) { zend_class_entry *parent_ce = zend_lookup_class_ex( - ce->parent_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + ZEND_PNR_GET_NAME(ce->parent_name), NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (parent_ce && ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES)) @@ -8192,7 +8725,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) if (ce->parent_name) { /* Lowercased parent name */ - zend_string *lc_parent_name = zend_string_tolower(ce->parent_name); + zend_string *lc_parent_name = zend_string_tolower(ZEND_PNR_GET_NAME(ce->parent_name)); opline->op2_type = IS_CONST; LITERAL_STR(opline->op2, lc_parent_name); } @@ -9868,9 +10401,10 @@ static void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */ return; } - if (class_ast->kind == ZEND_AST_ZVAL) { + if (class_ast->kind == ZEND_AST_CLASS_REF) { zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); - opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + opline->op1.num = + zend_get_class_fetch_type(zend_ast_get_str(unwrap_non_generic_class_ref(class_ast))); } else { znode expr_node; zend_compile_expr(&expr_node, class_ast); @@ -10076,7 +10610,7 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_UNPACK || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_CLASS_NAME + || kind == ZEND_AST_CLASS_NAME || kind == ZEND_AST_CLASS_REF || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE || kind == ZEND_AST_CONST_ENUM_INIT || kind == ZEND_AST_NEW || kind == ZEND_AST_ARG_LIST @@ -10089,18 +10623,20 @@ static void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; + zend_ast *name_ast; zend_string *class_name; int fetch_type; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Dynamic class names are not allowed in compile-time class constant references"); } - if (Z_TYPE_P(zend_ast_get_zval(class_ast)) != IS_STRING) { + if (Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING) { zend_throw_error(NULL, "Class name must be a valid object or a string"); } - class_name = zend_ast_get_str(class_ast); + name_ast = unwrap_non_generic_class_ref(class_ast); + class_name = zend_ast_get_str(name_ast); fetch_type = zend_get_class_fetch_type(class_name); if (ZEND_FETCH_CLASS_STATIC == fetch_type) { @@ -10109,13 +10645,13 @@ static void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ } if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { - zend_string *tmp = zend_resolve_class_name_ast(class_ast); + zend_string *tmp = zend_resolve_class_name_ast(name_ast); zend_string_release_ex(class_name, 0); if (tmp != class_name) { - zval *zv = zend_ast_get_zval(class_ast); + zval *zv = zend_ast_get_zval(name_ast); ZVAL_STR(zv, tmp); - class_ast->attr = ZEND_NAME_FQ; + name_ast->attr = ZEND_NAME_FQ; } } @@ -10127,12 +10663,12 @@ static void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "(expression)::class cannot be used in constant expressions"); } - zend_string *class_name = zend_ast_get_str(class_ast); + zend_string *class_name = zend_ast_get_str(unwrap_non_generic_class_ref(class_ast)); uint32_t fetch_type = zend_get_class_fetch_type(class_name); switch (fetch_type) { @@ -10197,22 +10733,23 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr) zend_error_noreturn(E_COMPILE_ERROR, "Cannot use anonymous class in constant expression"); } - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use dynamic class name in constant expression"); } - zend_string *class_name = zend_resolve_class_name_ast(class_ast); + zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + zend_string *class_name = zend_resolve_class_name_ast(name_ast); int fetch_type = zend_get_class_fetch_type(class_name); if (ZEND_FETCH_CLASS_STATIC == fetch_type) { zend_error_noreturn(E_COMPILE_ERROR, "\"static\" is not allowed in compile-time constants"); } - zval *class_ast_zv = zend_ast_get_zval(class_ast); - zval_ptr_dtor_nogc(class_ast_zv); - ZVAL_STR(class_ast_zv, class_name); - class_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT; + zval *name_ast_zv = zend_ast_get_zval(name_ast); + zval_ptr_dtor_nogc(name_ast_zv); + ZVAL_STR(name_ast_zv, class_name); + name_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT; } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 765e54fb56ee8..a1228852770af 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -868,7 +868,11 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_type_release(zend_type type, bool persistent); ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name); +ZEND_API void zend_pnr_destroy(zend_packed_name_reference pnr); +void zend_compile_name_reference_key(zend_name_reference *ref, zend_string *lcname); +void zend_name_reference_release(zend_name_reference *name_ref, bool uses_arena, bool persistent); +void zend_packed_name_reference_release(zend_packed_name_reference ref, bool persistent); ZEND_API ZEND_COLD void zend_user_exception_handler(void); @@ -884,6 +888,7 @@ void zend_free_internal_arg_info(zend_internal_function *function); ZEND_API void destroy_zend_function(zend_function *function); ZEND_API void zend_function_dtor(zval *zv); ZEND_API void destroy_zend_class(zval *zv); +ZEND_API void destroy_zend_class_reference(zval *zv); void zend_class_add_ref(zval *zv); ZEND_API zend_string *zend_mangle_property_name(const char *src1, size_t src1_length, const char *src2, size_t src2_length, bool internal); @@ -899,6 +904,7 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen #define ZEND_FUNCTION_DTOR zend_function_dtor #define ZEND_CLASS_DTOR destroy_zend_class +#define ZEND_GENERIC_CLASS_DTOR destroy_zend_class_reference typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline); ZEND_API void zend_recalc_live_ranges( @@ -908,6 +914,7 @@ ZEND_API void pass_two(zend_op_array *op_array); ZEND_API bool zend_is_compiling(void); ZEND_API char *zend_make_compiled_string_description(const char *name); ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers); +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key); uint32_t zend_get_class_fetch_type(const zend_string *name); ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc); ZEND_API bool zend_is_smart_branch(const zend_op *opline); @@ -931,8 +938,8 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem); void zend_assert_valid_class_name(const zend_string *const_name); -zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope); -ZEND_API zend_string *zend_type_to_string(zend_type type); +ZEND_API zend_string *zend_type_to_string(zend_type type, const zend_class_entry *scope); +zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry *scope); /* BEGIN: OPCODES */ diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 6530c1f063dd8..998cc98f51e81 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -21,6 +21,7 @@ #include "zend_constants.h" #include "zend_exceptions.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_operators.h" #include "zend_globals.h" @@ -306,8 +307,10 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * zval *ret_constant = NULL; if (ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (!ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_fetch_class(class_name, flags); } } else if (zend_string_equals_literal_ci(class_name, "self")) { @@ -320,18 +323,19 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active"); goto failure; - } else if (UNEXPECTED(!scope->parent)) { + } else if (UNEXPECTED(!scope->num_parents)) { zend_throw_error(NULL, "Cannot access \"parent\" when current class scope has no parent"); goto failure; } else { - ce = scope->parent; + ce = scope->parents[0]->ce; } } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_STATIC))) { - ce = zend_get_called_scope(EG(current_execute_data)); - if (UNEXPECTED(!ce)) { + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + if (UNEXPECTED(!class_ref)) { zend_throw_error(NULL, "Cannot access \"static\" when no class scope is active"); goto failure; } + ce = class_ref->ce; } else { ce = zend_fetch_class(class_name, flags); } @@ -433,11 +437,11 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active"); goto failure; - } else if (UNEXPECTED(!scope->parent)) { + } else if (UNEXPECTED(!scope->num_parents)) { zend_throw_error(NULL, "Cannot access \"parent\" when current class scope has no parent"); goto failure; } else { - ce = scope->parent; + ce = scope->parents[0]->ce; } } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_STATIC))) { ce = zend_get_called_scope(EG(current_execute_data)); diff --git a/Zend/zend_enum.h b/Zend/zend_enum.h index 7b58592d42bd5..f8b6f6da125d9 100644 --- a/Zend/zend_enum.h +++ b/Zend/zend_enum.h @@ -48,14 +48,14 @@ ZEND_API zend_result zend_enum_get_case_by_value(zend_object **result, zend_clas static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj) { - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM); return OBJ_PROP_NUM(zobj, 0); } static zend_always_inline zval *zend_enum_fetch_case_value(zend_object *zobj) { - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM); - ZEND_ASSERT(zobj->ce->enum_backing_type != IS_UNDEF); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM); + ZEND_ASSERT(OBJ_CE(zobj)->enum_backing_type != IS_UNDEF); return OBJ_PROP_NUM(zobj, 1); } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7dcf2ad65b7f6..14c6baf9943fb 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -24,6 +24,9 @@ #include "zend_builtin_functions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_operators.h" +#include "zend_string.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_dtrace.h" #include "zend_smart_str.h" @@ -44,10 +47,12 @@ ZEND_API zend_class_entry *zend_ce_division_by_zero_error; ZEND_API zend_class_entry *zend_ce_unhandled_match_error; /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */ -static zend_class_entry zend_ce_unwind_exit; +static zend_class_entry_storage zend_ces_unwind_exit; +#define zend_ce_unwind_exit ZEND_CES_TO_CE(zend_ces_unwind_exit) /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does* execute finally blocks. */ -static zend_class_entry zend_ce_graceful_exit; +static zend_class_entry_storage zend_ces_graceful_exit; +#define zend_ce_graceful_exit ZEND_CES_TO_CE(zend_ces_graceful_exit) ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); @@ -59,8 +64,8 @@ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entr /* zend_ce_exception and zend_ce_error may not be initialized yet when this is called (e.g when * implementing Throwable for Exception itself). Perform a manual inheritance check. */ zend_class_entry *root = class_type; - while (root->parent) { - root = root->parent; + if (root->num_parents) { + root = root->parents[root->num_parents-1]->ce; } if (zend_string_equals_literal(root->name, "Exception") || zend_string_equals_literal(root->name, "Error")) { @@ -82,7 +87,7 @@ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entr static inline zend_class_entry *i_get_exception_base(zend_object *object) /* {{{ */ { - return instanceof_function(object->ce, zend_ce_exception) ? zend_ce_exception : zend_ce_error; + return instanceof_function(OBJ_CE(object), zend_ce_exception) ? zend_ce_exception : zend_ce_error; } /* }}} */ @@ -107,7 +112,7 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo return; } - ZEND_ASSERT(instanceof_function(add_previous->ce, zend_ce_throwable) + ZEND_ASSERT(instanceof_function(OBJ_CE(add_previous), zend_ce_throwable) && "Previous exception must implement Throwable"); ZVAL_OBJ(&pv, add_previous); @@ -172,7 +177,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_THROWN_ENABLED()) { if (exception != NULL) { - DTRACE_EXCEPTION_THROWN(ZSTR_VAL(exception->ce->name)); + DTRACE_EXCEPTION_THROWN(ZSTR_VAL(OBJ_NAME(exception))); } else { DTRACE_EXCEPTION_THROWN(NULL); } @@ -195,7 +200,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* } } if (!EG(current_execute_data)) { - if (exception && (exception->ce == zend_ce_parse_error || exception->ce == zend_ce_compile_error)) { + if (exception && (OBJ_CE(exception) == zend_ce_parse_error || OBJ_CE(exception) == zend_ce_compile_error)) { return; } if (EG(exception)) { @@ -794,9 +799,13 @@ void zend_register_default_exception(void) /* {{{ */ zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error); zend_init_exception_class_entry(zend_ce_unhandled_match_error); - INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); + INIT_CLASS_ENTRY((*zend_ce_unwind_exit), "UnwindExit", NULL); + zend_init_class_entry_header(&zend_ces_unwind_exit, + zend_new_interned_string(zend_string_tolower_ex(zend_ce_unwind_exit->name, true))); - INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL); + INIT_CLASS_ENTRY((*zend_ce_graceful_exit), "GracefulExit", NULL); + zend_init_class_entry_header(&zend_ces_graceful_exit, + zend_new_interned_string(zend_string_tolower_ex(zend_ce_graceful_exit->name, true))); } /* }}} */ @@ -900,7 +909,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_result result = FAILURE; ZVAL_OBJ(&exception, ex); - ce_exception = ex->ce; + ce_exception = OBJ_CE(ex); EG(exception) = NULL; if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) { zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE)); @@ -918,7 +927,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_string *str, *file = NULL; zend_long line = 0; - zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); + zend_call_known_instance_method_with_0_params(OBJ_CE(ex)->__tostring, ex, &tmp); if (!EG(exception)) { if (Z_TYPE(tmp) != IS_STRING) { zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name)); @@ -957,7 +966,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_string_release_ex(str, 0); zend_string_release_ex(file, 0); - } else if (ce_exception == &zend_ce_unwind_exit || ce_exception == &zend_ce_graceful_exit) { + } else if (ce_exception == zend_ce_unwind_exit || ce_exception == zend_ce_graceful_exit) { /* We successfully unwound, nothing more to do. * We still return FAILURE in this case, as further execution should still be aborted. */ } else { @@ -1005,12 +1014,12 @@ ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */ ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void) { - return zend_objects_new(&zend_ce_unwind_exit); + return zend_objects_new(zend_ce_unwind_exit); } ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void) { - return zend_objects_new(&zend_ce_graceful_exit); + return zend_objects_new(zend_ce_graceful_exit); } ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) @@ -1031,10 +1040,10 @@ ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) ZEND_API bool zend_is_unwind_exit(const zend_object *ex) { - return ex->ce == &zend_ce_unwind_exit; + return ex->cr == (zend_class_reference*) &zend_ces_unwind_exit; } ZEND_API bool zend_is_graceful_exit(const zend_object *ex) { - return ex->ce == &zend_ce_graceful_exit; + return ex->cr == (zend_class_reference*) &zend_ces_graceful_exit; } diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h index c0e274d64ef70..2154ba1e658a3 100644 --- a/Zend/zend_exceptions_arginfo.h +++ b/Zend/zend_exceptions_arginfo.h @@ -246,7 +246,7 @@ static zend_class_entry *register_class_Exception(zend_class_entry *class_entry_ ZVAL_NULL(&property_previous_default_value); zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); - zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previous_class_Throwable, 0, MAY_BE_NULL)); zend_string_release(property_previous_name); return class_entry; @@ -316,7 +316,7 @@ static zend_class_entry *register_class_Error(zend_class_entry *class_entry_Thro ZVAL_NULL(&property_previous_default_value); zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); - zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previous_class_Throwable, 0, MAY_BE_NULL)); zend_string_release(property_previous_name); return class_entry; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 470d52922c39c..e659e2ccc9860 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -44,6 +44,11 @@ #include "zend_system_id.h" #include "zend_call_stack.h" #include "Optimizer/zend_func_info.h" +#include "zend_bitset.h" +#include "zend_portability.h" +#include "zend_types.h" +#include "zend_type_tools.h" +#include "zend_symbolic_inference.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" @@ -567,9 +572,10 @@ static inline void zend_assign_to_variable_reference(zval *variable_ptr, zval *v ZVAL_REF(variable_ptr, ref); } -static zend_never_inline zval* zend_assign_to_typed_property_reference(zend_property_info *prop_info, zval *prop, zval *value_ptr, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) +static zend_never_inline zval* zend_assign_to_typed_property_reference( + zend_object *obj, zend_property_info *prop_info, zval *prop, zval *value_ptr, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) { - if (!zend_verify_prop_assignable_by_ref(prop_info, value_ptr, EX_USES_STRICT_TYPES())) { + if (!zend_verify_prop_assignable_by_ref(obj, prop_info, value_ptr, EX_USES_STRICT_TYPES())) { return &EG(uninitialized_zval); } if (Z_ISREF_P(prop)) { @@ -606,7 +612,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error( "Cannot auto-initialize an array inside property %s::$%s of type %s", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), @@ -616,7 +622,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_ } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error( "Cannot auto-initialize an array inside a reference held by property %s::$%s of type %s", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), @@ -668,6 +674,50 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro } } +static void zend_verify_type_generic_params_explainer(smart_str *dest, zend_class_entry *scope, zend_type type) { + // TODO: It's all a hack... + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); + + if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + zend_generic_param *param = &scope->generic_params[generic_param_id - scope->num_bound_generic_args]; + for (;;) { + if (generic_param_id >= called_scope->ce->num_bound_generic_args) { + ZEND_ASSERT(generic_param_id - called_scope->ce->num_bound_generic_args < called_scope->args.num_types); + type = called_scope->args.types[generic_param_id - called_scope->ce->num_bound_generic_args]; + } else { + type = called_scope->ce->bound_generic_args[generic_param_id]; + } + if (!ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + break; + } + generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + } + zend_string *real_type_string = zend_type_to_string(type, called_scope->ce); + smart_str_append(dest, param->name); + smart_str_appends(dest, " = "); + smart_str_append(dest, real_type_string); + zend_string_release(real_type_string); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name; + const zend_type_list *args; + const zend_type *arg; + ZEND_PNR_UNPACK(ZEND_TYPE_PNR(type), name, args); + (void) name; + ZEND_TYPE_LIST_FOREACH_CONST(args, arg) { + zend_verify_type_generic_params_explainer(dest, scope, *arg); + } ZEND_TYPE_LIST_FOREACH_CONST_END(); + } else if (ZEND_TYPE_HAS_LIST(type)) { + ZEND_ASSERT(ZEND_TYPE_IS_UNION(type) || ZEND_TYPE_IS_INTERSECTION(type)); + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + zend_verify_type_generic_params_explainer(dest, scope, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + } +} + static ZEND_COLD void zend_verify_type_error_common( const zend_function *zf, const zend_arg_info *arg_info, zval *value, const char **fname, const char **fsep, const char **fclass, @@ -681,9 +731,24 @@ static ZEND_COLD void zend_verify_type_error_common( *fsep = ""; *fclass = ""; } - *need_msg = zend_type_to_string_resolved(arg_info->type, zf->common.scope); + smart_str generic_params_explainer = {0}; + zend_verify_type_generic_params_explainer(&generic_params_explainer, + zf->common.scope, arg_info->type); + + if (smart_str_get_len(&generic_params_explainer)) { + smart_str str = {0}; + smart_str_append(&str, *need_msg); + smart_str_appends(&str, " (where "); + smart_str_append_smart_str(&str, &generic_params_explainer); + smart_str_appends(&str, ")"); + zend_string_release(*need_msg); + *need_msg = smart_str_extract(&str); + } + + smart_str_free(&generic_params_explainer); + if (value) { *given_kind = zend_zval_value_name(value); } else { @@ -825,7 +890,7 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant) { - zend_string *type_str = zend_type_to_string(c->type); + zend_string *type_str = zend_type_to_string(c->type, NULL); zend_type_error("Cannot assign %s to class constant %s::%s of type %s", zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -842,7 +907,7 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(const zend_prop return; } - type_str = zend_type_to_string(info->type); + type_str = zend_type_to_string(info->type, info->ce); zend_type_error("Cannot assign %s to property %s::$%s of type %s", zend_zval_value_name(property), ZSTR_VAL(info->ce->name), @@ -858,7 +923,7 @@ ZEND_COLD zend_never_inline void zend_magic_get_property_type_inconsistency_erro return; } - zend_string *type_str = zend_type_to_string(info->type); + zend_string *type_str = zend_type_to_string(info->type, info->ce); zend_type_error("Value of type %s returned from %s::__get() must be compatible with unset property %s::$%s of type %s", zend_zval_type_name(property), ZSTR_VAL(info->ce->name), @@ -914,25 +979,30 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons if (zend_string_equals_literal_ci(name, "self")) { return self_ce; } else if (zend_string_equals_literal_ci(name, "parent")) { - return self_ce->parent; + return self_ce->num_parents ? self_ce->parents[0]->ce : NULL; } else { return zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); } } +#if 0 static zend_always_inline const zend_class_entry *zend_ce_from_type( const zend_class_entry *scope, const zend_type *type) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type)); - zend_string *name = ZEND_TYPE_NAME(*type); + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*type)); + zend_string *name = ZEND_TYPE_PNR_NAME(*type); if (ZSTR_HAS_CE_CACHE(name)) { - zend_class_entry *ce = ZSTR_GET_CE_CACHE(name); - if (!ce) { + zend_class_entry *ce; + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); } return ce; } return resolve_single_class_type(name, scope); } +#endif static bool zend_check_intersection_for_property_or_class_constant_class_type( const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce) @@ -941,8 +1011,18 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type( ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(scope, list_type); - if (!ce || !instanceof_function(value_ce, ce)) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*list_type); + ce = resolve_single_class_type(name, scope); + if (!ce) { + return false; + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_CLASS_REF(*list_type)); + ce = ZEND_TYPE_CLASS_REF(*list_type)->ce; + } + if (!instanceof_function(value_ce, ce)) { return false; } } ZEND_TYPE_LIST_FOREACH_END(); @@ -966,8 +1046,17 @@ static bool zend_check_and_resolve_property_or_class_constant_class_type( continue; } ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(scope, list_type); - if (ce && instanceof_function(value_ce, ce)) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*list_type); + ce = resolve_single_class_type(name, scope); + if (!ce) { + continue; + } + } else { + ce = ZEND_TYPE_CLASS_REF(*list_type)->ce; + } + if (instanceof_function(value_ce, ce)) { return true; } } ZEND_TYPE_LIST_FOREACH_END(); @@ -980,15 +1069,59 @@ static bool zend_check_and_resolve_property_or_class_constant_class_type( } } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC) && value_ce == scope) { return true; - } else if (ZEND_TYPE_HAS_NAME(member_type)) { - const zend_class_entry *ce = zend_ce_from_type(scope, &member_type); - return ce && instanceof_function(value_ce, ce); + } else if (UNEXPECTED(ZEND_TYPE_HAS_PNR(member_type) || ZEND_TYPE_HAS_CLASS_REF(member_type))) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(member_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(member_type); + ce = resolve_single_class_type(name, scope); + if (UNEXPECTED(!ce)) { + return false; + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_CLASS_REF(member_type)); + ce = ZEND_TYPE_CLASS_REF(member_type)->ce; + } + return instanceof_function(value_ce, ce); } return false; } -static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict) +#if 0 +static zend_never_inline bool check_property_type_generic( + zend_type real_type, zval *property, bool strict) { + /*if (ZEND_TYPE_HAS_CLASS(info->type) && Z_TYPE_P(property) == IS_OBJECT + && zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(property))) { + return 1; + }*/ + + ZEND_ASSERT(!(ZEND_TYPE_FULL_MASK(real_type) & MAY_BE_CALLABLE)); + if ((ZEND_TYPE_FULL_MASK(real_type) & MAY_BE_ITERABLE) && zend_is_iterable(property)) { + return 1; + } + return zend_verify_scalar_type_hint(ZEND_TYPE_FULL_MASK(real_type), property, strict, 0); +} +#endif + +static zend_always_inline zend_type zend_resolve_generic_type( + const zend_class_reference *scope, uint32_t param_id) { + if (!scope) { + zend_error_noreturn(E_CORE_ERROR, + "Unexpectedly tried to resolve a type parameter without a scope (todo/bug)"); + } + if (param_id >= scope->ce->num_bound_generic_args) { + if (param_id - scope->ce->num_bound_generic_args < scope->args.num_types) { + return scope->args.types[param_id - scope->ce->num_bound_generic_args]; + } else { + zend_error_noreturn(E_CORE_ERROR, + "Scope does not bind parameter %d (todo/bug)", param_id); + } + } else { + return scope->ce->bound_generic_args[param_id]; + } +} + +static zend_always_inline bool i_zend_check_property_type(const zend_object *obj, const zend_property_info *info, zval *property, bool strict) { ZEND_ASSERT(!Z_ISREF_P(property)); if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { @@ -1000,14 +1133,23 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in return 1; } + if (ZEND_TYPE_HAS_GENERIC_PARAM(info->type)) { + zend_type real_type = zend_resolve_generic_type(obj->cr, ZEND_TYPE_GENERIC_PARAM_ID(info->type)); + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(property))) { + return 1; + } + /*return zend_check_type_slow( + real_type, arg, ref, cache_slot, scope, is_return_type, is_internal);*/ + } + uint32_t type_mask = ZEND_TYPE_FULL_MASK(info->type); ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_STATIC|MAY_BE_NEVER|MAY_BE_VOID))); return zend_verify_scalar_type_hint(type_mask, property, strict, 0); } -static zend_always_inline bool i_zend_verify_property_type(const zend_property_info *info, zval *property, bool strict) +static zend_always_inline bool i_zend_verify_property_type(const zend_object *obj, const zend_property_info *info, zval *property, bool strict) { - if (i_zend_check_property_type(info, property, strict)) { + if (i_zend_check_property_type(obj, info, property, strict)) { return 1; } @@ -1015,11 +1157,13 @@ static zend_always_inline bool i_zend_verify_property_type(const zend_property_i return 0; } -ZEND_API bool zend_never_inline zend_verify_property_type(const zend_property_info *info, zval *property, bool strict) { - return i_zend_verify_property_type(info, property, strict); +ZEND_API bool zend_never_inline zend_verify_property_type( + zend_object *obj, const zend_property_info *info, zval *property, bool strict) { + return i_zend_verify_property_type(obj, info, property, strict); } -static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) +static zend_never_inline zval *zend_assign_to_typed_prop( + zend_object *obj, zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) { zval tmp; @@ -1031,7 +1175,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf ZVAL_DEREF(value); ZVAL_COPY(&tmp, value); - if (UNEXPECTED(!i_zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) { + if (UNEXPECTED(!i_zend_verify_property_type(obj, info, &tmp, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(&tmp); return &EG(uninitialized_zval); } @@ -1046,11 +1190,42 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) { return 0; } - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); if (!called_scope) { return 0; } - return instanceof_function(Z_OBJCE_P(zv), called_scope); + return instanceof_function(Z_OBJCE_P(zv), called_scope->ce); +} + +static ZEND_COLD void zend_validate_generic_args_error( + zend_class_entry *ce, const zend_type_list *args) { + if (args->num_types > ce->num_generic_params) { + zend_throw_error(NULL, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + args->num_types); + } else { + ZEND_ASSERT(args->num_types < ce->num_required_generic_params); + zend_throw_error(NULL, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + args->num_types); + } +} + +static zend_always_inline bool zend_validate_generic_args( + zend_class_entry *ce, const zend_type_list *args) { + if (EXPECTED(args->num_types <= ce->num_generic_params + && args->num_types >= ce->num_required_generic_params)) { + return 1; + } + + zend_validate_generic_args_error(ce, args); + return 0; } /* The cache_slot may only be NULL in debug builds, where arginfo verification of @@ -1061,20 +1236,29 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) { # define HAVE_CACHE_SLOT 1 #endif +// TODO: cache_slot may not be worth it since ZSTR_CE_CACHE +#undef HAVE_CACHE_SLOT +#define HAVE_CACHE_SLOT 0 +#define PROGRESS_CACHE_SLOT_EX(_) +#define ZEND_TYPE_NUM_NAMES(_) + #define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;} static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( - void **cache_slot, zend_type *type) + void **cache_slot, const zend_type *type) { if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) { return (zend_class_entry *) *cache_slot; } - zend_string *name = ZEND_TYPE_NAME(*type); + zend_string *name = ZEND_TYPE_PNR_NAME(*type); zend_class_entry *ce; + if (ZSTR_HAS_CE_CACHE(name)) { - ce = ZSTR_GET_CE_CACHE(name); - if (!ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (UNEXPECTED(!ce)) { /* Cannot resolve */ @@ -1094,65 +1278,369 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return ce; } +static zend_always_inline zend_class_reference *zend_fetch_cr_from_cache_slot( + void **cache_slot, const zend_type *type) +{ + if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) { + return (zend_class_reference *) *cache_slot; + } + + zend_class_reference *class_ref; + zend_packed_name_reference pnr = ZEND_TYPE_PNR(*type); + zend_string *key = ZEND_PNR_IS_SIMPLE(pnr) + ? ZEND_PNR_SIMPLE_GET_NAME(pnr) + : ZEND_PNR_COMPLEX_GET_KEY(pnr); + + if (ZSTR_HAS_CE_CACHE(key)) { + class_ref = ZSTR_GET_CE_CACHE(key); + if (!class_ref) { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), NULL, + ZEND_FETCH_CLASS_NO_AUTOLOAD); + class_ref = ce ? ZEND_CE_TO_REF(ce) : NULL; + } else { + class_ref = zend_lookup_generic_class( + ZEND_PNR_COMPLEX_GET_REF(pnr), NULL, + ZEND_FETCH_CLASS_NO_AUTOLOAD); + } + if (UNEXPECTED(!class_ref)) { + /* Cannot resolve */ + return NULL; + } + } + } else { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), NULL, + ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); + class_ref = ce ? ZEND_CE_TO_REF(ce) : NULL; + } else { + class_ref = zend_lookup_generic_class( + ZEND_PNR_COMPLEX_GET_REF(pnr), NULL, + ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); + } + + if (UNEXPECTED(!class_ref)) { + return NULL; + } + } + + if (HAVE_CACHE_SLOT) { + *cache_slot = (void *) class_ref; + } + + return class_ref; +} + +static bool zend_type_accepts_slow( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot); + +ZEND_API bool zend_type_accepts( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot) +{ + uint32_t accepted_mask = ZEND_TYPE_PURE_MASK(*accepted_type); + uint32_t instance_mask = (ZEND_TYPE_FULL_MASK(*instance_type) + & (_ZEND_TYPE_MAY_BE_MASK | (_ZEND_TYPE_COMPLEX_MASK&~_ZEND_TYPE_PNR_BIT))) + | (ZEND_TYPE_HAS_PNR(*instance_type) ? MAY_BE_OBJECT : 0); + + if (accepted_mask && (instance_mask & ~accepted_mask) == 0) { + return true; + } + + return zend_type_accepts_slow(accepted_type, accepted_scope, instance_type, + instance_scope, cache_slot); +} + +static bool zend_type_accepts_slow( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot) +{ + if (ZEND_TYPE_HAS_GENERIC_PARAM(*instance_type)) { + zend_type tmp = zend_resolve_generic_type(instance_scope, + ZEND_TYPE_GENERIC_PARAM_ID(*instance_type)); + return zend_type_accepts(accepted_type, accepted_scope, + &tmp, instance_scope, cache_slot); + } + + if (ZEND_TYPE_HAS_PNR(*accepted_type)) { + if (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) { + return false; + } + + zend_class_reference *accepted_ref = zend_fetch_cr_from_cache_slot(cache_slot, accepted_type); + if (!accepted_ref) { + return false; // TODO? + } + + if (ZEND_TYPE_HAS_PNR(*instance_type)) { + // TODO: type name may not have a cache ptr if it does not appear in + // a type. + zend_class_reference *instance_ref = zend_fetch_cr_from_cache_slot(NULL, instance_type); + if (!instance_ref) { + return false; // TODO? + } + return instanceof_ref(instance_ref, instance_scope, + accepted_ref, accepted_scope); + } else if (ZEND_TYPE_IS_INTERSECTION(*instance_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), single_type) { + if (zend_type_accepts(accepted_type, accepted_scope, + single_type, instance_scope, NULL)) { + return true; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return false; + } else if (ZEND_TYPE_IS_UNION(*instance_type)) { + zend_type *instance_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), instance_item) { + if (!zend_type_accepts(accepted_type, accepted_scope, + instance_item, instance_scope, NULL)) { + return false; + } + } ZEND_TYPE_LIST_FOREACH_END(); + + return true; + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(*instance_type)); + return false; + } + } else if (ZEND_TYPE_IS_INTERSECTION(*accepted_type)) { + if (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) { + return false; + } + + zend_type *accepted_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*accepted_type), accepted_item) { + if (!zend_type_accepts(accepted_item, accepted_scope, + instance_type, instance_scope, cache_slot)) { + return false; + } + PROGRESS_CACHE_SLOT_EX(ZEND_TYPE_NUM_NAMES(*accepted_item)); + } ZEND_TYPE_LIST_FOREACH_END(); + return true; + } else if (ZEND_TYPE_IS_UNION(*accepted_type)) { + if (ZEND_TYPE_IS_UNION(*instance_type)) { + zend_type *instance_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), instance_item) { + if (!zend_type_accepts_slow(accepted_type, accepted_scope, + instance_item, instance_scope, cache_slot)) { + return false; + } + /* Do not progress cache slot, as we are iterating instance_type */ + } ZEND_TYPE_LIST_FOREACH_END(); + return (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) == 0; + } else { + zend_type *accepted_item; + zend_type tmp = *instance_type; + bool result; + if (ZEND_TYPE_IS_COMPLEX(*instance_type)) { + result = false; + ZEND_TYPE_FULL_MASK(tmp) &= ~ZEND_TYPE_PURE_MASK(tmp); + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*accepted_type), accepted_item) { + if (zend_type_accepts(accepted_item, accepted_scope, + &tmp, instance_scope, cache_slot)) { + result = true; + break; + } + PROGRESS_CACHE_SLOT_EX(ZEND_TYPE_NUM_NAMES(*accepted_item)); + } ZEND_TYPE_LIST_FOREACH_END(); + if (!result) { + return false; + } + } + uint32_t mask = ZEND_TYPE_PURE_MASK(*instance_type); + if ((mask & ~ZEND_TYPE_PURE_MASK(*accepted_type)) == 0) { + return true; + } + if (mask) { + for (uint32_t i = (1<ce, &class_ref->args) && + instanceof_ref(Z_OBJCR_P(arg), Z_OBJCR_P(arg), + class_ref, zend_get_called_scope(EG(current_execute_data)))) { + return true; + } + } + PROGRESS_CACHE_SLOT(); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); + zend_type real_type = zend_resolve_generic_type(zend_get_called_scope(EG(current_execute_data)), ZEND_TYPE_GENERIC_PARAM_ID(*list_type)); + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { + return true; + } + if (ZEND_TYPE_HAS_PNR(real_type) && Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, &real_type); + if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + return true; + } + } + if (zend_check_type_slow_recursive( + &real_type, arg, ref, cache_slot, scope, is_return_type, is_internal)) { + return true; + } } - PROGRESS_CACHE_SLOT(); } } ZEND_TYPE_LIST_FOREACH_END(); } - } else { - ce = zend_fetch_ce_from_cache_slot(cache_slot, type); - /* If we have a CE we check if it satisfies the type constraint, + } else if (ZEND_TYPE_HAS_PNR(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { + zend_class_reference *class_ref = zend_fetch_cr_from_cache_slot(cache_slot, type); + /* If we have a CR we check if it satisfies the type constraint, * otherwise it will check if a standard type satisfies it. */ - if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + if (class_ref && zend_validate_generic_args(class_ref->ce, &class_ref->args) + && instanceof_ref(Z_OBJCR_P(arg), Z_OBJCR_P(arg), class_ref, + zend_get_called_scope(EG(current_execute_data)))) { + return true; + } + } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(*type))) { + // TODO: Deduplicate this code. + zend_type real_type = zend_resolve_generic_type(zend_get_called_scope(EG(current_execute_data)), ZEND_TYPE_GENERIC_PARAM_ID(*type)); + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { + return true; + } + if (ZEND_TYPE_HAS_PNR(real_type) && Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, &real_type); + if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + return true; + } + } + if (zend_check_type_slow_recursive( + &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { return true; } } @@ -1185,6 +1673,13 @@ static zend_always_inline bool zend_check_type_slow( * because this case is already checked at compile-time. */ } +static bool zend_check_type_slow_recursive( + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type, bool is_internal) +{ + return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal); +} + static zend_always_inline bool zend_check_type( zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope, bool is_return_type, bool is_internal) @@ -1201,14 +1696,15 @@ static zend_always_inline bool zend_check_type( return 1; } - return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal); + return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal); } ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type) + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type) { return zend_check_type_slow( - type, arg, ref, cache_slot, is_return_type, /* is_internal */ false); + type, arg, ref, cache_slot, scope, is_return_type, /* is_internal */ false); } static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot) @@ -1391,6 +1887,31 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data * } } +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_type_arg_error(zend_execute_data *execute_data) +{ + zend_execute_data *ptr = EX(prev_execute_data); + + if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected", + EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + EX(func)->common.scope ? "::" : "", + ZSTR_VAL(EX(func)->common.function_name), + EX_NUM_ARGS(), + ZSTR_VAL(ptr->func->op_array.filename), + ptr->opline->lineno, + EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least", + EX(func)->common.required_num_args); + } else { + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed and %s %d expected", + EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + EX(func)->common.scope ? "::" : "", + ZSTR_VAL(EX(func)->common.function_name), + EX_NUM_ARGS(), + EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least", + EX(func)->common.required_num_args); + } +} + ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; @@ -1506,7 +2027,7 @@ ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_const static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(const zend_object *object) { - zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(object->ce->name)); + zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(OBJ_NAME(object))); } static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_array_offset_access(const zval *offset) @@ -1615,7 +2136,9 @@ static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *re } } -static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline void zend_binary_assign_op_typed_prop( + zend_object *obj, zend_property_info *prop_info, + zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) { zval z_copy; @@ -1627,7 +2150,7 @@ static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_inf } zend_binary_op(&z_copy, zptr, value OPLINE_CC); - if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) { + if (EXPECTED(zend_verify_property_type(obj, prop_info, &z_copy, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(zptr); ZVAL_COPY_VALUE(zptr, &z_copy); } else { @@ -1890,7 +2413,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re static ZEND_COLD zend_long zend_throw_incdec_ref_error( zend_reference *ref, zend_property_info *error_prop OPLINE_DC) { - zend_string *type_str = zend_type_to_string(error_prop->type); + zend_string *type_str = zend_type_to_string(error_prop->type, error_prop->ce); if (ZEND_IS_INCREMENT(opline->opcode)) { zend_type_error( "Cannot increment a reference held by property %s::$%s of type %s past its maximal value", @@ -1911,7 +2434,7 @@ static ZEND_COLD zend_long zend_throw_incdec_ref_error( } static ZEND_COLD zend_long zend_throw_incdec_prop_error(zend_property_info *prop OPLINE_DC) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); if (ZEND_IS_INCREMENT(opline->opcode)) { zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value", ZSTR_VAL(prop->ce->name), @@ -1961,7 +2484,7 @@ static void zend_incdec_typed_ref(zend_reference *ref, zval *copy OPLINE_DC EXEC } } -static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, zval *copy OPLINE_DC EXECUTE_DATA_DC) +static void zend_incdec_typed_prop(zend_object *obj, zend_property_info *prop_info, zval *var_ptr, zval *copy OPLINE_DC EXECUTE_DATA_DC) { zval tmp; @@ -1982,7 +2505,7 @@ static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, zend_long val = zend_throw_incdec_prop_error(prop_info OPLINE_CC); ZVAL_LONG(var_ptr, val); } - } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) { + } else if (UNEXPECTED(!zend_verify_property_type(obj, prop_info, var_ptr, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(var_ptr); ZVAL_COPY_VALUE(var_ptr, copy); ZVAL_UNDEF(copy); @@ -1991,7 +2514,7 @@ static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, } } -static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) +static void zend_pre_incdec_property_zval(zend_object *obj, zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) { if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { if (ZEND_IS_INCREMENT(opline->opcode)) { @@ -2016,7 +2539,7 @@ static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_i } if (UNEXPECTED(prop_info)) { - zend_incdec_typed_prop(prop_info, prop, NULL OPLINE_CC EXECUTE_DATA_CC); + zend_incdec_typed_prop(obj, prop_info, prop, NULL OPLINE_CC EXECUTE_DATA_CC); } else if (ZEND_IS_INCREMENT(opline->opcode)) { increment_function(prop); } else { @@ -2029,7 +2552,8 @@ static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_i } } -static void zend_post_incdec_property_zval(zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) +static void zend_post_incdec_property_zval( + zend_object *obj, zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) { if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(prop)); @@ -2054,7 +2578,7 @@ static void zend_post_incdec_property_zval(zval *prop, zend_property_info *prop_ } if (UNEXPECTED(prop_info)) { - zend_incdec_typed_prop(prop_info, prop, EX_VAR(opline->result.var) OPLINE_CC EXECUTE_DATA_CC); + zend_incdec_typed_prop(obj, prop_info, prop, EX_VAR(opline->result.var) OPLINE_CC EXECUTE_DATA_CC); } else { ZVAL_COPY(EX_VAR(opline->result.var), prop); if (ZEND_IS_INCREMENT(opline->opcode)) { @@ -2639,7 +3163,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * retval = obj->handlers->read_dimension(obj, dim, type, result); if (UNEXPECTED(retval == &EG(uninitialized_zval))) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); ZVAL_NULL(result); zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); @@ -2650,7 +3174,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * retval = result; } if (Z_TYPE_P(retval) != IS_OBJECT) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); } } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { @@ -3097,13 +3621,13 @@ ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref) { static zend_property_info *zend_object_fetch_property_type_info( zend_object *obj, zval *slot) { - if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) { + if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(OBJ_CE(obj)))) { return NULL; } /* Not a declared property */ if (UNEXPECTED(slot < obj->properties_table || - slot >= obj->properties_table + obj->ce->default_properties_count)) { + slot >= obj->properties_table + OBJ_CE(obj)->default_properties_count)) { return NULL; } @@ -3188,7 +3712,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c zobj = Z_OBJ_P(container); if (prop_op_type == IS_CONST && - EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -3310,7 +3834,7 @@ static zend_always_inline void zend_assign_to_property_reference(zval *container } if (UNEXPECTED(prop_info)) { - variable_ptr = zend_assign_to_typed_property_reference(prop_info, variable_ptr, value_ptr, &garbage EXECUTE_DATA_CC); + variable_ptr = zend_assign_to_typed_property_reference(Z_OBJ_P(container), prop_info, variable_ptr, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(variable_ptr, value_ptr, &garbage); } @@ -3468,8 +3992,8 @@ static zend_always_inline zend_result zend_fetch_static_property_address(zval ** } ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) { - zend_string *type1_str = zend_type_to_string(prop1->type); - zend_string *type2_str = zend_type_to_string(prop2->type); + zend_string *type1_str = zend_type_to_string(prop1->type, prop1->ce); + zend_string *type2_str = zend_type_to_string(prop2->type, prop2->ce); zend_type_error("Reference with value of type %s held by property %s::$%s of type %s is not compatible with property %s::$%s of type %s", zend_zval_type_name(zv), ZSTR_VAL(prop1->ce->name), @@ -3484,7 +4008,7 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info } ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info *prop, const zval *zv) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s", zend_zval_value_name(zv), ZSTR_VAL(prop->ce->name), @@ -3495,8 +4019,8 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info } ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) { - zend_string *type1_str = zend_type_to_string(prop1->type); - zend_string *type2_str = zend_type_to_string(prop2->type); + zend_string *type1_str = zend_type_to_string(prop1->type, prop1->ce); + zend_string *type2_str = zend_type_to_string(prop2->type, prop2->ce); zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s and property %s::$%s of type %s, as this would result in an inconsistent type conversion", zend_zval_value_name(zv), ZSTR_VAL(prop1->ce->name), @@ -3672,7 +4196,8 @@ ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, ui return result; } -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context) { +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex( + const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context) { zval *val = orig_val; if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) { int result; @@ -3698,7 +4223,7 @@ ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_pro } } else { ZVAL_DEREF(val); - if (i_zend_check_property_type(prop_info, val, strict)) { + if (i_zend_check_property_type(obj, prop_info, val, strict)) { return 1; } } @@ -3713,8 +4238,8 @@ ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_pro return 0; } -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_property_info *prop_info, zval *orig_val, bool strict) { - return zend_verify_prop_assignable_by_ref_ex(prop_info, orig_val, strict, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict) { + return zend_verify_prop_assignable_by_ref_ex(obj, prop_info, orig_val, strict, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT); } ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop) @@ -4754,7 +5279,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zend_o } } } else { - zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(function->ce->name)); + zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(OBJ_NAME(function))); return NULL; } @@ -4829,13 +5354,13 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(object->ce, Z_STR_P(method)); + zend_undefined_method(OBJ_CE(object), Z_STR_P(method)); } return NULL; } if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - object_or_called_scope = object->ce; + object_or_called_scope = OBJ_CE(object); } else { call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; GC_ADDREF(object); /* For $this pointer */ @@ -5501,3 +6026,199 @@ ZEND_API zval *zend_get_zval_ptr(const zend_op *opline, int op_type, const znode } return ret; } + +zend_type zend_materialize_generic_types(zend_arena **arena, zend_type t, zend_class_reference *scope) { + if (!ZEND_TYPE_IS_COMPLEX(t)) { + return t; + } + if (ZEND_TYPE_HAS_LIST(t)) { + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type_list *new_list = NULL; + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(list, single_type) { + zend_type tmp = zend_materialize_generic_types(arena, *single_type, scope); + if (tmp.type_mask != single_type->type_mask + || (ZEND_TYPE_IS_COMPLEX(tmp) && tmp.ptr != single_type->ptr)) { + if (!new_list) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_TYPE_SET_PTR(t, new_list); + } + new_list->types[single_type-list->types] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + if (ZEND_TYPE_IS_UNION(t)) { + t = zend_type_normalize_union_in_place(t); + } else { + t = zend_type_normalize_intersection_in_place(t); + } + } else if (ZEND_TYPE_HAS_COMPLEX_PNR(t)) { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(t)); + zend_name_reference *new_ref = NULL; + for (uint32_t i = 0; i < name_ref->args.num_types; i++) { + zend_type *single_type = &name_ref->args.types[i]; + zend_type tmp = zend_materialize_generic_types(arena, *single_type, scope); + if (tmp.type_mask != single_type->type_mask + || (ZEND_TYPE_IS_COMPLEX(tmp) && tmp.ptr != single_type->ptr)) { + if (!new_ref) { + new_ref = zend_arena_alloc(arena, ZEND_CLASS_REF_SIZE(name_ref->args.num_types)); + memcpy(new_ref, name_ref, ZEND_CLASS_REF_SIZE(name_ref->args.num_types)); + } + new_ref->args.types[i] = tmp; + } + } + if (new_ref) { + ZEND_TYPE_SET_PNR(t, ZEND_PNR_ENCODE_REF(new_ref)); + zend_compile_name_reference_key(new_ref, NULL); + } + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(t)) { + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(t); + return zend_resolve_generic_type(scope, param_id); + } + + return t; +} + +void zend_infer_types(zend_arena **arena, zend_type_list *infered_types, + zend_type param_type, zend_type arg_type, zend_class_reference *scope) { + if (!ZEND_TYPE_IS_COMPLEX(param_type)) { + return; + } + + if (ZEND_TYPE_IS_UNION(param_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param_type), single_type) { + // TODO: semantics: might be desirable that T|null -> null not in T + zend_infer_types(arena, infered_types, *single_type, arg_type, + scope); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_IS_INTERSECTION(param_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param_type), single_type) { + zend_infer_types(arena, infered_types, *single_type, arg_type, + scope); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_PNR(param_type)) { + if (ZEND_TYPE_HAS_COMPLEX_PNR(param_type)) { + zend_class_reference *param_ref = zend_fetch_cr_from_cache_slot(NULL, ¶m_type); + if (!param_ref) { + return; + } + // TODO + if (ZEND_TYPE_HAS_PNR(arg_type)) { + zend_class_reference *arg_ref = zend_fetch_cr_from_cache_slot(NULL, &arg_type); + if (!arg_ref) { + return; + } + if (arg_ref->ce == param_ref->ce) { + for (uint32_t j = 0; j < param_ref->args.num_types; j++) { + // TODO + if (j < arg_ref->args.num_types) { + zend_infer_types(arena, infered_types, + param_ref->args.types[j], + arg_ref->args.types[j], arg_ref); + } + } + return; + } + + for (uint32_t i = 0; i < arg_ref->ce->num_parents; i++) { + zend_class_reference *ref = arg_ref->ce->parents[i]; + if (ref->ce == param_ref->ce) { + for (uint32_t j = 0; j < param_ref->args.num_types; j++) { + // TODO + if (j < ref->args.num_types) { + zend_infer_types(arena, infered_types, + param_ref->args.types[j], + ref->args.types[j], + arg_ref); + } + } + return; + } + } + } + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(param_type)); + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(param_type); + ZEND_ASSERT(param_id <= infered_types->num_types); + infered_types->types[param_id] = zend_type_union( + &CG(arena), infered_types->types[param_id], + zend_materialize_generic_types(arena, arg_type, scope)); + } +} + +zend_type zend_value_type(zval *value) { + if (Z_TYPE_P(value) == IS_OBJECT) { + zend_class_reference *class_ref = Z_OBJ_P(value)->cr; + if (class_ref->args.num_types == 0) { + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(class_ref->ce->name), 0, 0); + } + zend_name_reference *name_ref = emalloc(ZEND_CLASS_REF_SIZE(class_ref->args.num_types)); + name_ref->name = class_ref->ce->name; + name_ref->key = class_ref->key; + memcpy(&name_ref->args, &class_ref->args, ZEND_TYPE_LIST_SIZE(class_ref->args.num_types)); + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_REF(name_ref), 0, 0); + } + + if (Z_TYPE_P(value) & (IS_TRUE|IS_FALSE)) { + return (zend_type) ZEND_TYPE_INIT_MASK(1 << (IS_TRUE|IS_FALSE)); + } + + if (UNEXPECTED(Z_TYPE_P(value) >= IS_REFERENCE)) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + return (zend_type) ZEND_TYPE_INIT_MASK(1 << Z_TYPE_P(value)); +} + +void zend_infer_instantiation_types(zend_type_list *dest, zend_class_entry *ce, + zend_type_list *args, zend_class_reference *scope) { + if (UNEXPECTED(ce->num_generic_params == 0)) { + return; + } + + zend_function* constructor = ce->constructor; + if (EXPECTED(constructor)) { + ZEND_ASSERT(dest->num_types == ce->num_generic_params); + zend_arg_info *arg_infos = constructor->common.arg_info; + uint32_t num_args = constructor->common.num_args; + + for (uint32_t i = 0, l = MIN(num_args, args->num_types); i < l; i++) { + zend_arg_info *arg_info = &arg_infos[i]; + zend_type t = zend_resolve_symbolic_type(&CG(arena), args->types[i]); + zend_infer_types(&CG(arena), dest, arg_info->type, t, scope); + } + } + + for (uint32_t i = 0; i < ce->num_generic_params; i++) { + if (EXPECTED(ZEND_TYPE_IS_SET(dest->types[i]))) { + continue; + } + if (EXPECTED(i >= ce->num_required_generic_params)) { + dest->types[i] = ce->generic_params[i].default_type; + } else { + // TODO: Check if the cause is always true and check this at compile time + zend_throw_error(NULL, "Generic type %s can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter", + ZSTR_VAL(ce->generic_params->name)); + return; + } + } +} + +zend_name_reference *zend_infer_instantiation_name_reference( + zend_class_entry *ce, zend_string *lcname, zend_type_list *args, + zend_class_reference *scope) { + zend_name_reference *ref = ecalloc(1, ZEND_CLASS_REF_SIZE(ce->num_generic_params)); + + ref->args.num_types = ce->num_generic_params; + zend_infer_instantiation_types(&ref->args, ce, args, scope); + + ref->name = ce->name; + zend_string_addref(ref->name); + + zend_compile_name_reference_key(ref, lcname); + + return ref; +} diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index d0c18040ab46d..612e81e0a772d 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -24,6 +24,7 @@ #include "zend_compile.h" #include "zend_hash.h" #include "zend_operators.h" +#include "zend_types.h" #include "zend_variables.h" #include @@ -50,7 +51,9 @@ ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_val ZEND_API bool zend_is_valid_class_name(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex); +ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *name_ref, zend_string *base_key, uint32_t flags); +ZEND_API zend_class_reference *zend_lookup_class_by_pnr(zend_packed_name_reference pnr, zend_string *base_key, uint32_t flags); +ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex); ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex); ZEND_API zend_result zend_eval_string(const char *str, zval *retval_ptr, const char *string_name); ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *retval_ptr, const char *string_name); @@ -72,8 +75,9 @@ typedef enum { ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET, } zend_verify_prop_assignable_by_ref_context; -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context); -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_property_info *prop_info, zval *orig_val, bool strict); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref( + const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict); ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info *prop, const zval *zv); ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv); @@ -99,7 +103,12 @@ ZEND_API ZEND_COLD void zend_verify_never_error( const zend_function *zf); ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type); + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type); +ZEND_API bool zend_type_accepts( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call); @@ -387,6 +396,7 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void); ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type); ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, uint32_t fetch_type, zend_class_entry *scope); ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type); +ZEND_API zend_class_reference *zend_fetch_generic_class_by_ref(zend_name_reference *name_ref, zend_string *base_key, uint32_t fetch_type); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len); @@ -486,11 +496,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal #define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) - ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant); ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant); -ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); +ZEND_API bool zend_verify_property_type(zend_object *obj, const zend_property_info *info, zval *property, bool strict); ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property); ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property); @@ -529,6 +538,10 @@ static zend_always_inline void *zend_get_bad_ptr(void) return NULL; } +zend_name_reference *zend_infer_instantiation_name_reference( + zend_class_entry *ce, zend_string *lcname, zend_type_list *args, + zend_class_reference *scope); + END_EXTERN_C() #endif /* ZEND_EXECUTE_H */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index feeff659bd5f4..a5dfa957d1ece 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -25,12 +25,14 @@ #include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" +#include "zend_hash.h" #include "zend_stack.h" #include "zend_constants.h" #include "zend_extensions.h" #include "zend_exceptions.h" #include "zend_closures.h" #include "zend_generators.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_float.h" #include "zend_fibers.h" @@ -145,6 +147,7 @@ void init_executor(void) /* {{{ */ EG(function_table) = CG(function_table); EG(class_table) = CG(class_table); + zend_hash_init(&EG(generic_class_table), 8, NULL, ZEND_GENERIC_CLASS_DTOR, 0); EG(in_autoload) = NULL; EG(error_handling) = EH_NORMAL; @@ -317,6 +320,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) } } } ZEND_HASH_FOREACH_END(); + zend_hash_clean(&EG(generic_class_table)); ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -431,6 +435,7 @@ void shutdown_executor(void) /* {{{ */ * each allocated block separately. */ zend_hash_discard(EG(function_table), EG(persistent_functions_count)); + zend_hash_discard(&EG(generic_class_table), 0); zend_hash_discard(EG(class_table), EG(persistent_classes_count)); } else { zend_vm_stack_destroy(); @@ -457,6 +462,9 @@ void shutdown_executor(void) /* {{{ */ } ZEND_HASH_MAP_FOREACH_END_DEL(); } + // TODO: reverse_clean + zend_hash_graceful_reverse_destroy(&EG(generic_class_table)); + while (EG(symtable_cache_ptr) > EG(symtable_cache)) { EG(symtable_cache_ptr)--; zend_hash_destroy(*EG(symtable_cache_ptr)); @@ -1115,6 +1123,50 @@ ZEND_API bool zend_is_valid_class_name(zend_string *name) { return 1; } +ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *name_ref, zend_string *base_key, uint32_t flags) /* {{{ */ +{ + zend_class_entry *ce = NULL; + zval *zv; + zend_string *name = name_ref->name; + zend_string *key = name_ref->key; + uint32_t ce_cache = 0; + + if (ZSTR_HAS_CE_CACHE(key) && ZSTR_VALID_CE_CACHE(key)) { + ce_cache = GC_REFCOUNT(key); + zend_class_reference *class_ref = GET_CE_CACHE(ce_cache); + if (EXPECTED(class_ref)) { + return class_ref; + } + } + + zv = zend_hash_find(&EG(generic_class_table), key); + if (zv) { + zend_class_reference *class_ref = Z_CR_P(zv); + /* Don't populate CE_CACHE for mutable classes during compilation. + * The class may be freed while persisting. */ + if (ce_cache && + (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { + SET_CE_CACHE(ce_cache, class_ref); + } + return class_ref; + } + + ce = zend_lookup_class_ex(name, base_key, flags); + if (!ce) { + return NULL; + } + + zend_class_reference *class_ref = zend_build_class_reference(name_ref, ce); + if (!UNEXPECTED(class_ref)) { + return NULL; + } + + void *ptr = zend_hash_add_ptr(&EG(generic_class_table), name_ref->key, class_ref); + ZEND_ASSERT(ptr); + + return class_ref; +} + ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */ { zend_class_entry *ce = NULL; @@ -1125,9 +1177,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (ZSTR_HAS_CE_CACHE(name) && ZSTR_VALID_CE_CACHE(name)) { ce_cache = GC_REFCOUNT(name); - ce = GET_CE_CACHE(ce_cache); - if (EXPECTED(ce)) { - return ce; + zend_class_reference *class_ref = GET_CE_CACHE(ce_cache); + if (EXPECTED(class_ref)) { + return class_ref->ce; } } @@ -1169,7 +1221,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * * The class may be freed while persisting. */ if (ce_cache && (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { - SET_CE_CACHE(ce_cache, ce); + SET_CE_CACHE(ce_cache, ZEND_CE_TO_REF(ce)); } return ce; } @@ -1226,7 +1278,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (ce) { ZEND_ASSERT(!CG(in_compilation)); if (ce_cache) { - SET_CE_CACHE(ce_cache, ce); + SET_CE_CACHE(ce_cache, ZEND_CE_TO_REF(ce)); } } return ce; @@ -1239,13 +1291,28 @@ ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ } /* }}} */ -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ +ZEND_API zend_class_reference *zend_lookup_class_by_pnr(zend_packed_name_reference pnr, zend_string *base_key, uint32_t flags) /* {{{ */ +{ + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), base_key, flags); + if (!ce) { + return NULL; + } + return ZEND_CE_TO_REF(ce); + } + + return zend_lookup_generic_class(ZEND_PNR_COMPLEX_GET_REF(pnr), base_key, flags); +} +/* }}} */ + +ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ { while (ex) { if (Z_TYPE(ex->This) == IS_OBJECT) { - return Z_OBJCE(ex->This); + return Z_OBJCR(ex->This); } else if (Z_CE(ex->This)) { - return Z_CE(ex->This); + return ZEND_CE_TO_REF(Z_CE(ex->This)); } else if (ex->func) { if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) { return NULL; @@ -1660,17 +1727,19 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type) zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active"); return NULL; } - if (UNEXPECTED(!scope->parent)) { + if (UNEXPECTED(!scope->num_parents)) { zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent"); - } - return scope->parent; - case ZEND_FETCH_CLASS_STATIC: - ce = zend_get_called_scope(EG(current_execute_data)); - if (UNEXPECTED(!ce)) { - zend_throw_or_error(fetch_type, NULL, "Cannot access \"static\" when no class scope is active"); return NULL; } - return ce; + return scope->parents[0]->ce; + case ZEND_FETCH_CLASS_STATIC: { + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + if (UNEXPECTED(!class_ref)) { + zend_throw_or_error(fetch_type, NULL, "Cannot access \"static\" when no class scope is active"); + return NULL; + } + return class_ref->ce; + } case ZEND_FETCH_CLASS_AUTO: { fetch_sub_type = zend_get_class_fetch_type(class_name); if (UNEXPECTED(fetch_sub_type != ZEND_FETCH_CLASS_DEFAULT)) { @@ -1704,10 +1773,11 @@ zend_class_entry *zend_fetch_class_with_scope( zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active"); return NULL; } - if (UNEXPECTED(!scope->parent)) { + if (UNEXPECTED(!scope->num_parents)) { zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent"); + return NULL; } - return scope->parent; + return scope->parents[0]->ce; case 0: break; /* Other fetch types are not supported by this function. */ @@ -1733,6 +1803,17 @@ zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string } /* }}} */ +zend_class_reference *zend_fetch_generic_class_by_ref(zend_name_reference *name_ref, zend_string *base_key, uint32_t fetch_type) /* {{{ */ +{ + zend_class_reference *class_ref = zend_lookup_generic_class(name_ref, base_key, fetch_type); + if (!class_ref) { + report_class_fetch_error(name_ref->name, fetch_type); + return NULL; + } + return class_ref; +} +/* }}} */ + ZEND_API zend_result zend_delete_global_variable(zend_string *name) /* {{{ */ { return zend_hash_del_ind(&EG(symbol_table), name); diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index b92ead02eb4a0..bc7ed05fda114 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -1442,7 +1442,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta } if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED) && (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor != NULL)) { + || OBJ_CE(obj)->destructor != NULL)) { *flags |= GC_HAS_DESTRUCTORS; } @@ -1846,7 +1846,7 @@ ZEND_API int zend_gc_collect_cycles(void) if (GC_TYPE(p) == IS_OBJECT && !(OBJ_FLAGS(p) & IS_OBJ_DESTRUCTOR_CALLED)) { zend_object *obj = (zend_object *) p; if (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor) { + || OBJ_CE(obj)->destructor) { current->ref = GC_MAKE_DTOR_GARBAGE(obj); GC_REF_SET_COLOR(obj, GC_PURPLE); } else { diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8900a5f416f53..407f1fdd12f43 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -83,6 +83,7 @@ struct _zend_compiler_globals { zend_stack loop_var_stack; zend_class_entry *active_class_entry; + bool in_static_class_member; /* compiling a static prop, static method, or class const */ zend_string *compiled_filename; @@ -181,6 +182,7 @@ struct _zend_executor_globals { HashTable *function_table; /* function symbol table */ HashTable *class_table; /* class table */ + HashTable generic_class_table;/* generic class table */ HashTable *zend_constants; /* constants table */ zval *vm_stack_top; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0785c7bcafa0d..bc1f2368f1132 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -30,6 +30,9 @@ #include "zend_attributes.h" #include "zend_constants.h" #include "zend_observer.h" +#include "zend_string.h" +#include "zend_types.h" +#include "zend_type_tools.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; @@ -60,38 +63,6 @@ static void ZEND_COLD emit_incompatible_method_error( const zend_function *parent, zend_class_entry *parent_scope, inheritance_status status); -static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); - -static void zend_type_list_copy_ctor( - zend_type *const parent_type, - bool use_arena, - bool persistent -) { - const zend_type_list *const old_list = ZEND_TYPE_LIST(*parent_type); - size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); - zend_type_list *new_list = use_arena - ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); - - memcpy(new_list, old_list, size); - ZEND_TYPE_SET_LIST(*parent_type, new_list); - if (use_arena) { - ZEND_TYPE_FULL_MASK(*parent_type) |= _ZEND_TYPE_ARENA_BIT; - } - - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(new_list, list_type) { - zend_type_copy_ctor(list_type, use_arena, persistent); - } ZEND_TYPE_LIST_FOREACH_END(); -} - -static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { - if (ZEND_TYPE_HAS_LIST(*type)) { - zend_type_list_copy_ctor(type, use_arena, persistent); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string_addref(ZEND_TYPE_NAME(*type)); - } -} - static zend_function *zend_duplicate_internal_function(zend_function *func, zend_class_entry *ce) /* {{{ */ { zend_function *new_function; @@ -129,7 +100,7 @@ static zend_always_inline zend_function *zend_duplicate_function(zend_function * static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ { - zend_class_entry *parent = ce->parent; + zend_class_entry *parent = ce->parents[0]->ce; ZEND_ASSERT(parent != NULL); @@ -211,11 +182,11 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */ static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) { ZEND_ASSERT(scope); - if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { + if (zend_string_equals_literal_ci(name, "parent") && scope->num_parents) { if (scope->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - return scope->parent->name; + return scope->parents[0]->ce->name; } else { - return scope->parent_name; + return ZEND_PNR_GET_NAME(scope->parent_name); } } else if (zend_string_equals_literal_ci(name, "self")) { return scope->name; @@ -303,12 +274,12 @@ static bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { return instanceof_function(ce1, ce2); } - if (ce1->parent) { + if (ce1->num_parents) { zend_class_entry *parent_ce; if (ce1->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - parent_ce = ce1->parent; + parent_ce = ce1->parents[0]->ce; } else { - parent_ce = zend_lookup_class_ex(ce1->parent_name, NULL, + parent_ce = zend_lookup_class_ex(ZEND_PNR_GET_NAME(ce1->parent_name), NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); } @@ -356,8 +327,8 @@ static bool zend_type_permits_self( * for this case. */ zend_type *single_type; ZEND_TYPE_FOREACH(type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *name = resolve_class_name(scope, ZEND_TYPE_PNR_NAME(*single_type)); zend_class_entry *ce = lookup_class(self, name); if (ce && unlinked_instanceof(self, ce)) { return 1; @@ -367,6 +338,31 @@ static bool zend_type_permits_self( return 0; } +/* Resolve generic type parameters that have been determined through inheritance. */ +static void zend_type_resolve_generic_params(zend_type *type, zend_class_entry *ce) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*type); + if (generic_param_id < ce->num_bound_generic_args) { + uint32_t orig_type_mask = ZEND_TYPE_PURE_MASK(*type); + *type = ce->bound_generic_args[generic_param_id]; + ZEND_TYPE_FULL_MASK(*type) |= orig_type_mask; + zend_type_copy_ctor(type, /* use_arena */ true, /* persistent */ false); + } + } else if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + if (generic_param_id < ce->num_bound_generic_args) { + ZEND_ASSERT(0); + //*type = ce->parent_generic_args[generic_param_id]; + //zend_type_copy_ctor(type, /* use_arena */ true, /* persistent */ false); + } + } + } ZEND_TYPE_LIST_FOREACH_END(); + } +} + static void track_class_dependency(zend_class_entry *ce, zend_string *class_name) { HashTable *ht; @@ -424,9 +420,9 @@ static inheritance_status zend_is_intersection_subtype_of_class( ZEND_TYPE_FOREACH(fe_type, single_type) { zend_class_entry *fe_ce; zend_string *fe_class_name = NULL; - if (ZEND_TYPE_HAS_NAME(*single_type)) { + if (ZEND_TYPE_HAS_PNR(*single_type)) { fe_class_name = - resolve_class_name(fe_scope, ZEND_TYPE_NAME(*single_type)); + resolve_class_name(fe_scope, ZEND_TYPE_PNR_NAME(*single_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { return INHERITANCE_SUCCESS; } @@ -505,9 +501,9 @@ static inheritance_status zend_is_class_subtype_of_type( zend_class_entry *proto_ce; zend_string *proto_class_name = NULL; - if (ZEND_TYPE_HAS_NAME(*single_type)) { + if (ZEND_TYPE_HAS_PNR(*single_type)) { proto_class_name = - resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type)); + resolve_class_name(proto_scope, ZEND_TYPE_PNR_NAME(*single_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { if (!is_intersection) { return INHERITANCE_SUCCESS; @@ -547,8 +543,8 @@ static inheritance_status zend_is_class_subtype_of_type( } static zend_string *get_class_from_type(zend_class_entry *scope, zend_type single_type) { - if (ZEND_TYPE_HAS_NAME(single_type)) { - return resolve_class_name(scope, ZEND_TYPE_NAME(single_type)); + if (ZEND_TYPE_HAS_PNR(single_type)) { + return resolve_class_name(scope, ZEND_TYPE_PNR_NAME(single_type)); } return NULL; } @@ -560,8 +556,8 @@ static void register_unresolved_classes(zend_class_entry *scope, zend_type type) register_unresolved_classes(scope, *single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *class_name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *class_name = resolve_class_name(scope, ZEND_TYPE_PNR_NAME(*single_type)); lookup_class_ex(scope, class_name, /* register_unresolved */ true); } } ZEND_TYPE_FOREACH_END(); @@ -635,9 +631,12 @@ static inheritance_status zend_is_intersection_subtype_of_type( static inheritance_status zend_perform_covariant_type_check( zend_class_entry *fe_scope, zend_type fe_type, - zend_class_entry *proto_scope, zend_type proto_type) + zend_class_entry *proto_scope, zend_type proto_type, + zend_class_entry *generic_scope) /* {{{ */ { ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); + zend_type_resolve_generic_params(&fe_type, generic_scope); + zend_type_resolve_generic_params(&proto_type, generic_scope); /* Apart from void, everything is trivially covariant to the mixed type. * Handle this case separately to ensure it never requires class loading. */ @@ -684,7 +683,15 @@ static inheritance_status zend_perform_covariant_type_check( if (status == INHERITANCE_UNRESOLVED) { have_unresolved = true; } + } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(fe_type))) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(proto_type)) { + if (ZEND_TYPE_GENERIC_PARAM_ID(fe_type) == ZEND_TYPE_GENERIC_PARAM_ID(proto_type)) { + return INHERITANCE_SUCCESS; + } + } + return INHERITANCE_ERROR; } else { + ZEND_ASSERT(ZEND_TYPE_HAS_LIST(fe_type) || ZEND_TYPE_HAS_PNR(fe_type) || !ZEND_TYPE_IS_COMPLEX(fe_type)); /* U_1|...|U_n < V_1|...|V_m if forall U_i. exists V_j. U_i < V_j. * U_1|...|U_n < V_1&...&V_m if forall U_i. forall V_j. U_i < V_j. * We need to iterate over fe_type (U_i) first and the logic is independent of @@ -741,7 +748,9 @@ static inheritance_status zend_do_perform_arg_type_hint_check( /* Contravariant type check is performed as a covariant type check with swapped * argument order. */ return zend_perform_covariant_type_check( - proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type); + proto_scope, proto_arg_info->type, + fe_scope, fe_arg_info->type, + fe_scope); } /* }}} */ @@ -843,7 +852,9 @@ static inheritance_status zend_do_perform_implementation_check( } local_status = zend_perform_covariant_type_check( - fe_scope, fe->common.arg_info[-1].type, proto_scope, proto->common.arg_info[-1].type); + fe_scope, fe->common.arg_info[-1].type, + proto_scope, proto->common.arg_info[-1].type, + fe->common.scope); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (local_status == INHERITANCE_ERROR @@ -974,7 +985,9 @@ static ZEND_COLD zend_string *zend_get_function_declaration( if (ast->kind == ZEND_AST_CONSTANT) { smart_str_append(&str, zend_ast_get_constant_name(ast)); } else if (ast->kind == ZEND_AST_CLASS_CONST) { - smart_str_append(&str, zend_ast_get_str(ast->child[0])); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->child[1] == NULL && "Generic params not supported yet"); + smart_str_append(&str, zend_ast_get_str(class_ast->child[0])); smart_str_appends(&str, "::"); smart_str_append(&str, zend_ast_get_str(ast->child[1])); } else { @@ -1246,7 +1259,7 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function inheritance_status property_types_compatible( const zend_property_info *parent_info, const zend_property_info *child_info) { if (ZEND_TYPE_PURE_MASK(parent_info->type) == ZEND_TYPE_PURE_MASK(child_info->type) - && ZEND_TYPE_NAME(parent_info->type) == ZEND_TYPE_NAME(child_info->type)) { + && parent_info->type.ptr == child_info->type.ptr) { return INHERITANCE_SUCCESS; } @@ -1256,9 +1269,9 @@ inheritance_status property_types_compatible( /* Perform a covariant type check in both directions to determined invariance. */ inheritance_status status1 = zend_perform_covariant_type_check( - child_info->ce, child_info->type, parent_info->ce, parent_info->type); + child_info->ce, child_info->type, parent_info->ce, parent_info->type, NULL); inheritance_status status2 = zend_perform_covariant_type_check( - parent_info->ce, parent_info->type, child_info->ce, child_info->type); + parent_info->ce, parent_info->type, child_info->ce, child_info->type, NULL); if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) { return INHERITANCE_SUCCESS; } @@ -1405,7 +1418,7 @@ static inheritance_status class_constant_types_compatible(const zend_class_const return INHERITANCE_ERROR; } - return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type); + return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type, NULL); } static bool do_inherit_constant_check( @@ -1424,7 +1437,7 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; - if (ce->parent->ce_flags & ZEND_ACC_IMMUTABLE) { + if (ce->parents[0]->ce->ce_flags & ZEND_ACC_IMMUTABLE) { c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(c, parent_const, sizeof(zend_class_constant)); parent_const = c; @@ -1460,15 +1473,16 @@ void zend_build_properties_info_table(zend_class_entry *ce) /* Dead slots may be left behind during inheritance. Make sure these are NULLed out. */ memset(table, 0, size); - if (ce->parent && ce->parent->default_properties_count != 0) { - zend_property_info **parent_table = ce->parent->properties_info_table; + if (ce->num_parents && ce->parents[0]->ce->default_properties_count != 0) { + zend_class_entry *parent_ce = ce->parents[0]->ce; + zend_property_info **parent_table = parent_ce->properties_info_table; memcpy( table, parent_table, - sizeof(zend_property_info *) * ce->parent->default_properties_count + sizeof(zend_property_info *) * parent_ce->default_properties_count ); /* Child did not add any new properties, we are done */ - if (ce->default_properties_count == ce->parent->default_properties_count) { + if (ce->default_properties_count == parent_ce->default_properties_count) { return; } } @@ -1480,6 +1494,155 @@ void zend_build_properties_info_table(zend_class_entry *ce) } ZEND_HASH_FOREACH_END(); } +static void zend_type_fixup(zend_type *type, uint32_t generic_offset) { + zend_type *single_type; + ZEND_TYPE_FOREACH(*type, single_type) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*single_type)) { + ZEND_TYPE_SET_GENERIC_PARAM_ID(*single_type, + ZEND_TYPE_GENERIC_PARAM_ID(*single_type) + generic_offset); + } + } ZEND_TYPE_FOREACH_END(); +} + +static void zend_bind_parent_generic_args( + zend_class_entry *ce, zend_class_entry *parent_ce, const zend_type_list *parent_args) { + uint32_t num_required_params = parent_ce->num_required_generic_params; + if (parent_args->num_types > parent_ce->num_generic_params) { + zend_error(E_COMPILE_ERROR, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(parent_ce->name), + num_required_params == parent_ce->num_generic_params ? "exactly" : "at most", + parent_ce->num_generic_params, parent_ce->num_generic_params == 1 ? "" : "s", + parent_args->num_types); + } else if (parent_args->num_types < num_required_params) { + zend_error(E_COMPILE_ERROR, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(parent_ce->name), + num_required_params == parent_ce->num_generic_params ? "exactly" : "at least", + num_required_params, num_required_params == 1 ? "" : "s", + parent_args->num_types); + } + + // TODO: Validate type bounds. + + uint32_t num_inherited_generic_args = + parent_ce->num_generic_params + parent_ce->num_bound_generic_args; + zend_type *inherited_generic_args = emalloc(num_inherited_generic_args * sizeof(zend_type)); + for (uint32_t i = 0; i < parent_ce->num_bound_generic_args; i++) { + /* Inherit generic args for all parent classes. */ + inherited_generic_args[i] = parent_ce->bound_generic_args[i]; + zend_type_copy_ctor(&inherited_generic_args[i], /* use_arena */ true, /* persistent */ false); + } + + uint32_t offset = parent_ce->num_bound_generic_args; + for (uint32_t i = 0; i < parent_ce->num_generic_params; i++) { + /* Substitute generic args to our direct parent (or use defaults). */ + if (i < parent_args->num_types) { + inherited_generic_args[i + offset] = parent_args->types[i]; + zend_type_fixup(&inherited_generic_args[i + offset], num_inherited_generic_args); + } else { + inherited_generic_args[i + offset] = parent_ce->generic_params[i].default_type; + zend_type_copy_ctor(&inherited_generic_args[i + offset], /* use_arena */ true, /* persistent */ false); + } + } + + ce->num_bound_generic_args = num_inherited_generic_args; + ce->bound_generic_args = inherited_generic_args; + + for (uint32_t i = 0; i < ce->num_bound_generic_args; i++) { + zend_type_resolve_generic_params(&ce->bound_generic_args[i], ce); + } + + /* Fixup all generic parameter references (outside of opcodes) */ + zend_function *func; + ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { + zend_arg_info *arg_info = func->common.arg_info; + uint32_t num_args = func->common.num_args; + if (!(func->common.fn_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_HAS_RETURN_TYPE))) { + continue; + } + if (func->common.fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + arg_info--; + num_args++; + } + + for (uint32_t i = 0; i < num_args; i++) { + zend_type_fixup(&arg_info[i].type, num_inherited_generic_args); + } + } ZEND_HASH_FOREACH_END(); + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + zend_type_fixup(&prop->type, num_inherited_generic_args); + } ZEND_HASH_FOREACH_END(); + } +} + +static void update_parents(zend_class_entry *ce, zend_class_entry *parent_ce) { + /* We copy the bound generic arguments into the parent references. These copies + * are shallow, in that the structures are considered owned by the bound generic + * args. Note that the bound args and parents have classes in the reverse order, + * so start with a pointer to the end, which will be decremented below. */ + zend_type *generic_args = ce->num_bound_generic_args + ? ce->bound_generic_args + ce->num_bound_generic_args : NULL; + + zend_class_reference **parents = + pemalloc(sizeof(zend_class_reference *) * (parent_ce->num_parents + 1), + ce->type == ZEND_INTERNAL_CLASS); + if (ce->parent_name) { + zend_string *key; + if (ZEND_PNR_IS_COMPLEX(ce->parent_name)) { + /* Ownership of the type arguments has been taken by generic arg binding. */ + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ce->parent_name); + zend_string_release(ref->name); + key = ZEND_PNR_COMPLEX_GET_KEY(ce->parent_name); + zend_string_addref(key); + // efree(ref); + } else { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(ce->parent_name)); + key = ZEND_CE_TO_REF(ce)->key; + zend_string_addref(key); + } + + zend_class_reference *ref = emalloc(ZEND_CLASS_REF_SIZE(parent_ce->num_generic_params)); + ref->ce = parent_ce; + ref->key = key; + ref->args.num_types = parent_ce->num_generic_params; + generic_args -= ref->args.num_types; + memcpy(ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); + parents[0] = ref; + } else { + /* Internal inheritance. */ + parents[0] = ZEND_CE_TO_REF(parent_ce); + } + + for (uint32_t i = 0; i < parent_ce->num_parents; i++) { + zend_class_reference *ref = parent_ce->parents[i]; + if (ZEND_REF_IS_TRIVIAL(ref)) { + parents[i + 1] = ref; + } else { + zend_class_reference *new_ref = emalloc(ZEND_CLASS_REF_SIZE(ref->args.num_types)); + new_ref->ce = ref->ce; + new_ref->key = ref->key; + zend_string_addref(new_ref->key); + new_ref->args.num_types = ref->args.num_types; + generic_args -= ref->args.num_types; + memcpy(new_ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); + parents[i + 1] = new_ref; + } + } + + ZEND_ASSERT(generic_args == NULL || generic_args == ce->bound_generic_args); + ce->num_parents = parent_ce->num_parents + 1; + ce->parents = parents; + ce->default_object_handlers = parent_ce->default_object_handlers; + ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT; +} + ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */ { zend_property_info *property_info; @@ -1512,12 +1675,17 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ); } - if (ce->parent_name) { - zend_string_release_ex(ce->parent_name, 0); + const zend_type_list *parent_args = &zend_empty_type_list; + if (ce->parent_name && ZEND_PNR_IS_COMPLEX(ce->parent_name)) { + parent_args = &ZEND_PNR_COMPLEX_GET_REF(ce->parent_name)->args; } - ce->parent = parent_ce; - ce->default_object_handlers = parent_ce->default_object_handlers; - ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT; + + if (parent_args->num_types || parent_ce->num_generic_params) { + zend_bind_parent_generic_args(ce, parent_ce, parent_args); + } + + /* Update parents list, including grandparents. */ + update_parents(ce, parent_ce); /* Inherit properties */ if (parent_ce->default_properties_count) { @@ -1813,7 +1981,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry { 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; + uint32_t parent_iface_num = ce->num_parents ? ce->parents[0]->ce->num_interfaces : 0; zend_string *key; zend_class_constant *c; @@ -1854,7 +2022,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry **interfaces) /* {{{ */ { zend_class_entry *iface; - uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0; + uint32_t num_parent_interfaces = ce->num_parents ? ce->parents[0]->ce->num_interfaces : 0; uint32_t num_interfaces = num_parent_interfaces; zend_string *key; zend_class_constant *c; @@ -2373,8 +2541,8 @@ static bool do_trait_constant_check( emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); return false; } else if (ZEND_TYPE_IS_SET(trait_constant->type)) { - inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type); - inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type); + inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type, NULL); + inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type, NULL); if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) { emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); return false; @@ -2857,11 +3025,14 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) { static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce) { + zend_class_reference *class_ref; zend_class_entry *ce; Bucket *p, *end; - ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); - memcpy(ce, pce, sizeof(zend_class_entry)); + class_ref = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry_storage)); + memcpy(class_ref, ZEND_CE_TO_REF(pce), sizeof(zend_class_entry_storage)); + ce = (zend_class_entry*)((char*)class_ref + ZEND_CLASS_ENTRY_HEADER_SIZE); + class_ref->ce = ce; ce->ce_flags &= ~ZEND_ACC_IMMUTABLE; ce->refcount = 1; ce->inheritance_cache = NULL; @@ -3010,7 +3181,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string if (ce->parent_name) { parent = zend_fetch_class_by_name( - ce->parent_name, lc_parent_name, + ZEND_PNR_GET_NAME(ce->parent_name), lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION); if (!parent) { check_unrecoverable_load_failure(ce); @@ -3232,7 +3403,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string } if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE(ce->name, ce); + ZSTR_SET_CE_CACHE(ce->name, ZEND_CE_TO_REF(ce)); } return ce; @@ -3336,7 +3507,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ zend_class_entry *orig_linking_class; if (ce->ce_flags & ZEND_ACC_LINKED) { - ZEND_ASSERT(ce->parent == NULL); + ZEND_ASSERT(ce->num_parents == 0); if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ce))) { return NULL; } @@ -3427,7 +3598,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ } if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE(ce->name, ce); + ZSTR_SET_CE_CACHE(ce->name, ZEND_CE_TO_REF(ce)); } zend_observer_class_linked_notify(ce, lcname); diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 5148668d945c7..e21f280cf188a 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -49,7 +49,7 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z } if (!obj_ce) { - obj_ce = object ? object->ce : NULL; + obj_ce = object ? OBJ_CE(object) : NULL; } if (!fn_proxy || !*fn_proxy) { if (EXPECTED(obj_ce)) { @@ -74,7 +74,7 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z } if (object) { - called_scope = object->ce; + called_scope = OBJ_CE(object); } else { called_scope = obj_ce; } @@ -306,7 +306,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_new_iterator) { /* get_iterator was explicitly assigned for an internal class. */ - if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) { + if (!class_type->num_parents || class_type->parents[0]->ce->get_iterator != class_type->get_iterator) { ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS); return SUCCESS; } @@ -352,7 +352,7 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry &class_type->function_table, "next", sizeof("next") - 1); if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) { - if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) { + if (!class_type->num_parents || class_type->parents[0]->ce->get_iterator != class_type->get_iterator) { /* get_iterator was explicitly assigned for an internal class. */ ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS); return SUCCESS; @@ -460,9 +460,9 @@ ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const uns /* {{{ zend_implement_serializable */ static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type) { - if (class_type->parent - && (class_type->parent->serialize || class_type->parent->unserialize) - && !zend_class_implements_interface(class_type->parent, zend_ce_serializable)) { + if (class_type->num_parents + && (class_type->parents[0]->ce->serialize || class_type->parents[0]->ce->unserialize) + && !zend_class_implements_interface(class_type->parents[0]->ce, zend_ce_serializable)) { return FAILURE; } if (!class_type->serialize) { diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index f67033b11161c..fae4d98991943 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -20,7 +20,8 @@ #include "zend.h" #include "zend_API.h" -static zend_class_entry zend_iterator_class_entry; +static zend_class_entry_storage zend_iterator_class_entry_storage; +#define zend_iterator_class_entry ZEND_CES_TO_CE(&zend_iterator_class_entry_storage) static void iter_wrapper_free(zend_object *object); static void iter_wrapper_dtor(zend_object *object); @@ -56,8 +57,10 @@ static const zend_object_handlers iterator_object_handlers = { ZEND_API void zend_register_iterator_wrapper(void) { - INIT_CLASS_ENTRY(zend_iterator_class_entry, "__iterator_wrapper", NULL); - zend_iterator_class_entry.default_object_handlers = &iterator_object_handlers; + INIT_CLASS_ENTRY((*zend_iterator_class_entry), "__iterator_wrapper", NULL); + zend_iterator_class_entry->default_object_handlers = &iterator_object_handlers; + zend_init_class_entry_header(&zend_iterator_class_entry_storage, + zend_new_interned_string(zend_string_tolower_ex(zend_iterator_class_entry->name, true))); } static void iter_wrapper_free(zend_object *object) @@ -83,7 +86,7 @@ static HashTable *iter_wrapper_get_gc(zend_object *object, zval **table, int *n) ZEND_API void zend_iterator_init(zend_object_iterator *iter) { - zend_object_std_init(&iter->std, &zend_iterator_class_entry); + zend_object_std_init(&iter->std, zend_iterator_class_entry); } ZEND_API void zend_iterator_dtor(zend_object_iterator *iter) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 298eaf95ad055..23b6adeeae754 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -42,11 +42,15 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); #include "zend_compile.h" } +/* The conflicts are caused by generics syntax. + * foo(new Bar(42)) is ambiguous and resolved in favor of generics. */ +%glr-parser %define api.prefix {zend} -%define api.pure full +%define api.pure true %define api.value.type {zend_parser_stack_elem} %define parse.error verbose -%expect 0 +%expect 1 +%expect-rr 1 %destructor { zend_ast_destroy($$); } %destructor { if ($$) zend_string_release_ex($$, 0); } @@ -256,7 +260,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_expr anonymous_class class_name class_name_reference simple_variable -%type internal_functions_in_yacc +%type internal_functions_in_yacc simple_class_name generic_arg_list %type exit_expr scalar backticks_expr lexical_var function_call member_name property_name %type variable_class_name dereferenceable_scalar constant class_constant %type fully_dereferenceable array_object_dereferenceable @@ -279,6 +283,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type enum_declaration_statement enum_backing_type enum_case enum_case_expr %type function_name non_empty_member_modifiers +%type optional_generic_params generic_params generic_param generic_param_variance %type returns_ref function fn is_reference is_variadic property_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -586,13 +591,42 @@ is_variadic: | T_ELLIPSIS { $$ = ZEND_PARAM_VARIADIC; } ; +optional_generic_params: + %empty { $$ = NULL; } + | '<' generic_params '>' { $$ = $2; } +; + +generic_params: + generic_param + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_PARAM_LIST, $1); } + | generic_params ',' generic_param + { $$ = zend_ast_list_add($1, $3); } +; + +generic_param: + T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, NULL, NULL); } + | generic_param_variance T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, NULL, NULL); } + | T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, $3, NULL); } + | generic_param_variance T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, $4, NULL); } + | T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, NULL, $3); } + | generic_param_variance T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, NULL, $4); } + | T_STRING ':' type_expr '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, $3, $5); } + | generic_param_variance T_STRING ':' type_expr '=' type_expr + { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, $4, $6); } +; + +generic_param_variance: + /* TODO: maybe introduce actual tokens */ + T_STRING { $$ = $1; } +; + class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING optional_generic_params extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, NULL, $5); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING optional_generic_params extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, NULL, $4); } ; class_modifiers: @@ -621,14 +655,14 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } - T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } + T_STRING optional_generic_params backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $5, zend_ast_get_str($3), NULL, NULL, $7, NULL, $4); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } + T_STRING optional_generic_params interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4); } ; enum_declaration_statement: @@ -859,7 +893,9 @@ type_expr_without_static: type_without_static: T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name { $$ = $1; } + | name { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, NULL); } + | name '<' generic_arg_list '>' + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, $3); } ; union_type_without_static_element: @@ -892,10 +928,14 @@ argument_list: | '(' T_ELLIPSIS ')' { $$ = zend_ast_create(ZEND_AST_CALLABLE_CONVERT); } ; +/* The %dprec's resolve the foo(new Bar(42)) ambiguity in a somewhat indirect way: + * We give precedence to the interpretation that has fewer function call arguments, which + * implies that there will be more generic arguments, thus favoring generics. It is necessary + * to place the %dprec's here, as this is where the diverging parses will ultimately merge. */ non_empty_argument_list: - argument + argument %dprec 2 { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } - | non_empty_argument_list ',' argument + | non_empty_argument_list ',' argument %dprec 1 { $$ = zend_ast_list_add($1, $3); } ; @@ -1330,13 +1370,27 @@ function_call: } ; -class_name: +simple_class_name: T_STATIC { zval zv; ZVAL_INTERNED_STR(&zv, ZSTR_KNOWN(ZEND_STR_STATIC)); $$ = zend_ast_create_zval_ex(&zv, ZEND_NAME_NOT_FQ); } | name { $$ = $1; } ; +class_name: + simple_class_name + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, NULL); } + | simple_class_name '<' generic_arg_list '>' + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, $3); } +; + +generic_arg_list: + type_expr + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_ARG_LIST, $1); } + | generic_arg_list ',' type_expr + { $$ = zend_ast_list_add($1, $3); } +; + class_name_reference: class_name { $$ = $1; } | new_variable { $$ = $1; } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 054ed7bdc1ef6..3d5c14a00e9e2 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -46,6 +46,8 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_virtual_cwd.h" +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_optimizer.h" /* TODO: for zend_revert_pass_two */ #define YYCTYPE unsigned char #define YYFILL(n) { if ((YYCURSOR + n) >= (YYLIMIT + ZEND_MMAP_AHEAD)) { return 0; } } @@ -621,6 +623,11 @@ static zend_op_array *zend_compile(int type) op_array->line_start = 1; op_array->line_end = last_lineno; pass_two(op_array); + + zend_revert_pass_two(op_array); /* TODO */ + zend_symbolic_inference(op_array, &CG(arena)); + zend_redo_pass_two(op_array); + zend_oparray_context_end(&original_oparray_context); zend_file_context_end(&original_file_context); diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 573d9eb106df1..6f9fc770b205d 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -62,7 +62,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ { if (!zobj->properties) { zend_property_info *prop_info; - zend_class_entry *ce = zobj->ce; + zend_class_entry *ce = OBJ_CE(zobj); int i; zobj->properties = zend_new_array(ce->default_properties_count); @@ -90,7 +90,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj) /* {{{ */ { zend_property_info *prop_info; - zend_class_entry *ce = zobj->ce; + zend_class_entry *ce = OBJ_CE(zobj); HashTable *ht; zval* prop; int i; @@ -145,7 +145,7 @@ ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /* return zobj->properties; } else { *table = zobj->properties_table; - *n = zobj->ce->default_properties_count; + *n = OBJ_CE(zobj)->default_properties_count; return NULL; } } @@ -154,7 +154,7 @@ ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /* ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval retval; HashTable *ht; @@ -193,7 +193,7 @@ static void zend_std_call_getter(zend_object *zobj, zend_string *prop_name, zval { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__get, zobj, retval, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__get, zobj, retval, &member); } /* }}} */ @@ -202,7 +202,7 @@ static void zend_std_call_setter(zend_object *zobj, zend_string *prop_name, zval zval args[2]; ZVAL_STR(&args[0], prop_name); ZVAL_COPY_VALUE(&args[1], value); - zend_call_known_instance_method(zobj->ce->__set, zobj, NULL, 2, args); + zend_call_known_instance_method(OBJ_CE(zobj)->__set, zobj, NULL, 2, args); } /* }}} */ @@ -210,7 +210,7 @@ static void zend_std_call_unsetter(zend_object *zobj, zend_string *prop_name) /* { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__unset, zobj, NULL, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__unset, zobj, NULL, &member); } /* }}} */ @@ -218,21 +218,18 @@ static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zv { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__isset, zobj, retval, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__isset, zobj, retval, &member); } /* }}} */ static zend_always_inline bool is_derived_class(const zend_class_entry *child_class, const zend_class_entry *parent_class) /* {{{ */ { - child_class = child_class->parent; - while (child_class) { - if (child_class == parent_class) { + for (uint32_t i = 0; i < child_class->num_parents; i++) { + if (child_class->parents[i]->ce == parent_class) { return 1; } - child_class = child_class->parent; } - return 0; } /* }}} */ @@ -285,9 +282,9 @@ static ZEND_COLD zend_never_inline bool zend_deprecated_dynamic_property( zend_object *obj, const zend_string *member) { GC_ADDREF(obj); zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(obj->ce->name), ZSTR_VAL(member)); + ZSTR_VAL(OBJ_NAME(obj)), ZSTR_VAL(member)); if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_objects_store_del(obj); if (!EG(exception)) { /* We cannot continue execution and have to throw an exception */ @@ -503,7 +500,7 @@ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_st zend_unmangle_property_name_ex(prop_info_name, &class_name, &prop_name, &prop_name_len); member = zend_string_init(prop_name, prop_name_len, 0); - property_info = zend_get_property_info(zobj->ce, member, 1); + property_info = zend_get_property_info(OBJ_CE(zobj), member, 1); zend_string_release_ex(member, 0); if (property_info == NULL || property_info == ZEND_WRONG_PROPERTY_INFO) { return FAILURE; @@ -522,7 +519,7 @@ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_st } return SUCCESS; } else { - property_info = zend_get_property_info(zobj->ce, prop_info_name, 1); + property_info = zend_get_property_info(OBJ_CE(zobj), prop_info_name, 1); if (property_info == NULL) { ZEND_ASSERT(is_dynamic); return SUCCESS; @@ -544,7 +541,7 @@ static void zend_property_guard_dtor(zval *el) /* {{{ */ { static zend_always_inline zval *zend_get_guard_value(zend_object *zobj) { - return zobj->properties_table + zobj->ce->default_properties_count; + return zobj->properties_table + OBJ_CE(zobj)->default_properties_count; } ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */ @@ -554,7 +551,7 @@ ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *membe uint32_t *ptr; - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_USE_GUARDS); zv = zend_get_guard_value(zobj); if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) { zend_string *str = Z_STR_P(zv); @@ -597,7 +594,7 @@ ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *membe ZEND_API uint32_t *zend_get_recursion_guard(zend_object *zobj) { - if (!(zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_USE_GUARDS)) { return NULL; } zval *zv = zend_get_guard_value(zobj); @@ -617,7 +614,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int #endif /* make zend_get_property_info silent if we have getter - we may want to use it */ - property_offset = zend_get_property_offset(zobj->ce, name, (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (type == BP_VAR_IS) || (OBJ_CE(zobj)->__get != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { retval = OBJ_PROP(zobj, property_offset); @@ -687,7 +684,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } /* magic isset */ - if ((type == BP_VAR_IS) && zobj->ce->__isset) { + if ((type == BP_VAR_IS) && OBJ_CE(zobj)->__isset) { zval tmp_result; guard = zend_get_property_guard(zobj, name); @@ -710,14 +707,14 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } zval_ptr_dtor(&tmp_result); - if (zobj->ce->__get && !((*guard) & IN_GET)) { + if (OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { goto call_getter; } OBJ_RELEASE(zobj); - } else if (zobj->ce->__get && !((*guard) & IN_GET)) { + } else if (OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { goto call_getter_addref; } - } else if (zobj->ce->__get) { + } else if (OBJ_CE(zobj)->__get) { /* magic get */ guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_GET)) { @@ -734,7 +731,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int if (!Z_ISREF_P(rv) && (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) { if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) { - zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } } } else { @@ -742,14 +739,14 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } if (UNEXPECTED(prop_info)) { - zend_verify_prop_assignable_by_ref_ex(prop_info, retval, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); + zend_verify_prop_assignable_by_ref_ex(zobj, prop_info, retval, (OBJ_CE(zobj)->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); } OBJ_RELEASE(zobj); goto exit; } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) { /* Trigger the correct error */ - zend_get_property_offset(zobj->ce, name, 0, NULL, &prop_info); + zend_get_property_offset(OBJ_CE(zobj), name, 0, NULL, &prop_info); ZEND_ASSERT(EG(exception)); retval = &EG(uninitialized_zval); goto exit; @@ -763,7 +760,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int ZSTR_VAL(prop_info->ce->name), ZSTR_VAL(name)); } else { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } } retval = &EG(uninitialized_zval); @@ -819,7 +816,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva const zend_property_info *prop_info = NULL; ZEND_ASSERT(!Z_ISREF_P(value)); - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__set != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { variable_ptr = OBJ_PROP(zobj, property_offset); @@ -837,7 +834,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva ZVAL_COPY_VALUE(&tmp, value); // Increase refcount to prevent object from being released in __toString() GC_ADDREF(zobj); - bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()); + bool type_matched = zend_verify_property_type(zobj, prop_info, &tmp, property_uses_strict_types()); if (UNEXPECTED(GC_DELREF(zobj) == 0)) { zend_object_released_while_assigning_to_property_error(prop_info); zend_objects_store_del(zobj); @@ -903,7 +900,7 @@ found:; } /* magic set */ - if (zobj->ce->__set) { + if (OBJ_CE(zobj)->__set) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_SET)) { @@ -917,7 +914,7 @@ found:; goto write_std_property; } else { /* Trigger the correct error */ - zend_wrong_offset(zobj->ce, name); + zend_wrong_offset(OBJ_CE(zobj), name); ZEND_ASSERT(EG(exception)); variable_ptr = &EG(error_zval); goto exit; @@ -931,7 +928,7 @@ found:; Z_TRY_ADDREF_P(value); if (UNEXPECTED(prop_info)) { if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) - && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "initialize"))) { + && !verify_readonly_initialization_access(prop_info, OBJ_CE(zobj), name, "initialize"))) { Z_TRY_DELREF_P(value); variable_ptr = &EG(error_zval); goto exit; @@ -940,7 +937,7 @@ found:; ZVAL_COPY_VALUE(&tmp, value); // Increase refcount to prevent object from being released in __toString() GC_ADDREF(zobj); - bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()); + bool type_matched = zend_verify_property_type(zobj, prop_info, &tmp, property_uses_strict_types()); if (UNEXPECTED(GC_DELREF(zobj) == 0)) { zend_object_released_while_assigning_to_property_error(prop_info); zend_objects_store_del(zobj); @@ -959,12 +956,12 @@ found:; ZVAL_COPY_VALUE(variable_ptr, value); } else { - if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_forbidden_dynamic_property(zobj->ce, name); + if (UNEXPECTED(OBJ_CE(zobj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(OBJ_CE(zobj), name); variable_ptr = &EG(error_zval); goto exit; } - if (UNEXPECTED(!(zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { + if (UNEXPECTED(!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { if (UNEXPECTED(!zend_deprecated_dynamic_property(zobj, name))) { variable_ptr = &EG(error_zval); goto exit; @@ -992,7 +989,7 @@ static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry * ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; /* arrayaccess_funcs_ptr is set if (and only if) the class implements zend_ce_arrayaccess */ @@ -1043,7 +1040,7 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval *value) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr; @@ -1065,7 +1062,7 @@ ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval * ZEND_API int zend_std_has_dimension(zend_object *object, zval *offset, int check_empty) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval retval, tmp_offset; int result; @@ -1101,12 +1098,12 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam fprintf(stderr, "Ptr object #%d property: %s\n", zobj->handle, ZSTR_VAL(name)); #endif - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__get != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { retval = OBJ_PROP(zobj, property_offset); if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { - if (EXPECTED(!zobj->ce->__get) || + if (EXPECTED(!OBJ_CE(zobj)->__get) || UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET) || UNEXPECTED(prop_info && (Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT))) { if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { @@ -1117,7 +1114,7 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam ZSTR_VAL(name)); retval = &EG(error_zval); } else { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); /* An error handler may set the property */ if (EXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { ZVAL_NULL(retval); @@ -1149,13 +1146,13 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam return retval; } } - if (EXPECTED(!zobj->ce->__get) || + if (EXPECTED(!OBJ_CE(zobj)->__get) || UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) { - if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_forbidden_dynamic_property(zobj->ce, name); + if (UNEXPECTED(OBJ_CE(zobj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(OBJ_CE(zobj), name); return &EG(error_zval); } - if (UNEXPECTED(!(zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { + if (UNEXPECTED(!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { if (UNEXPECTED(!zend_deprecated_dynamic_property(zobj, name))) { return &EG(error_zval); } @@ -1164,11 +1161,11 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam rebuild_object_properties(zobj); } if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } retval = zend_hash_add(zobj->properties, name, &EG(uninitialized_zval)); } - } else if (zobj->ce->__get == NULL) { + } else if (OBJ_CE(zobj)->__get == NULL) { retval = &EG(error_zval); } @@ -1181,7 +1178,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void uintptr_t property_offset; const zend_property_info *prop_info = NULL; - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__unset != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { zval *slot = OBJ_PROP(zobj, property_offset); @@ -1212,7 +1209,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void } if (UNEXPECTED(Z_PROP_FLAG_P(slot) & IS_PROP_UNINIT)) { if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY) - && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "unset"))) { + && !verify_readonly_initialization_access(prop_info, OBJ_CE(zobj), name, "unset"))) { return; } @@ -1236,7 +1233,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void } /* magic unset */ - if (zobj->ce->__unset) { + if (OBJ_CE(zobj)->__unset) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_UNSET)) { /* have unsetter - try with it! */ @@ -1245,7 +1242,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void (*guard) &= ~IN_UNSET; } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) { /* Trigger the correct error */ - zend_wrong_offset(zobj->ce, name); + zend_wrong_offset(OBJ_CE(zobj), name); ZEND_ASSERT(EG(exception)); return; } else { @@ -1257,7 +1254,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr; @@ -1296,27 +1293,32 @@ static zend_never_inline zend_function *zend_get_parent_private_method(zend_clas */ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_entry *scope) /* {{{ */ { - const zend_class_entry *fbc_scope = ce; + if (ce == scope) { + return 1; + } + + if (scope == NULL) { + return 0; + } /* Is the context that's calling the function, the same as one of * the function's parents? */ - while (fbc_scope) { - if (fbc_scope==scope) { + for (uint32_t i = 0; i < ce->num_parents; i++) { + if (ce->parents[i]->ce == scope) { return 1; } - fbc_scope = fbc_scope->parent; } /* Is the function's scope the same as our current object context, * or any of the parents of our context? */ - while (scope) { - if (scope==ce) { + for (uint32_t i = 0; i < scope->num_parents; i++) { + if (scope->parents[i]->ce == ce) { return 1; } - scope = scope->parent; } + return 0; } /* }}} */ @@ -1422,12 +1424,12 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name)); } - if (UNEXPECTED((func = zend_hash_find(&zobj->ce->function_table, lc_method_name)) == NULL)) { + if (UNEXPECTED((func = zend_hash_find(&OBJ_CE(zobj)->function_table, lc_method_name)) == NULL)) { if (UNEXPECTED(!key)) { ZSTR_ALLOCA_FREE(lc_method_name, use_heap); } - if (zobj->ce->__call) { - return zend_get_user_call_function(zobj->ce, method_name); + if (OBJ_CE(zobj)->__call) { + return zend_get_user_call_function(OBJ_CE(zobj), method_name); } else { return NULL; } @@ -1441,7 +1443,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * if (fbc->common.scope != scope) { if (fbc->op_array.fn_flags & ZEND_ACC_CHANGED) { - zend_function *updated_fbc = zend_get_parent_private_method(scope, zobj->ce, lc_method_name); + zend_function *updated_fbc = zend_get_parent_private_method(scope, OBJ_CE(zobj), lc_method_name); if (EXPECTED(updated_fbc != NULL)) { fbc = updated_fbc; @@ -1452,8 +1454,8 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * } if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { - if (zobj->ce->__call) { - fbc = zend_get_user_call_function(zobj->ce, method_name); + if (OBJ_CE(zobj)->__call) { + fbc = zend_get_user_call_function(OBJ_CE(zobj), method_name); } else { zend_bad_method_call(fbc, method_name, scope); fbc = NULL; @@ -1486,12 +1488,12 @@ static zend_always_inline zend_function *get_static_method_fallback( zend_object *object; if (ce->__call && (object = zend_get_this_object(EG(current_execute_data))) != NULL && - instanceof_function(object->ce, ce)) { + instanceof_function(OBJ_CE(object), ce)) { /* Call the top-level defined __call(). * see: tests/classes/__call_004.phpt */ - ZEND_ASSERT(object->ce->__call); - return zend_get_user_call_function(object->ce, function_name); + ZEND_ASSERT(OBJ_CE(object)->__call); + return zend_get_user_call_function(OBJ_CE(object), function_name); } else if (ce->__callstatic) { return zend_get_user_callstatic_function(ce, function_name); } else { @@ -1558,15 +1560,15 @@ ZEND_API void zend_class_init_statics(zend_class_entry *class_type) /* {{{ */ zval *p; if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) { - if (class_type->parent) { - zend_class_init_statics(class_type->parent); + if (class_type->num_parents) { + zend_class_init_statics(class_type->parents[0]->ce); } ZEND_MAP_PTR_SET(class_type->static_members_table, emalloc(sizeof(zval) * class_type->default_static_members_count)); for (i = 0; i < class_type->default_static_members_count; i++) { p = &class_type->default_static_members_table[i]; if (Z_TYPE_P(p) == IS_INDIRECT) { - zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i]; + zval *q = &CE_STATIC_MEMBERS(class_type->parents[0]->ce)[i]; ZVAL_DEINDIRECT(q); ZVAL_INDIRECT(&CE_STATIC_MEMBERS(class_type)[i], q); } else { @@ -1672,7 +1674,7 @@ static ZEND_COLD zend_never_inline void zend_bad_constructor_call(zend_function ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ { - zend_function *constructor = zobj->ce->constructor; + zend_function *constructor = OBJ_CE(zobj)->constructor; zend_class_entry *scope; if (constructor) { @@ -1743,14 +1745,14 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ if (zobj1 == zobj2) { return 0; /* the same object */ } - if (zobj1->ce != zobj2->ce) { + if (OBJ_CE(zobj1) != OBJ_CE(zobj2)) { return ZEND_UNCOMPARABLE; /* different classes */ } if (!zobj1->properties && !zobj2->properties) { zend_property_info *info; int i; - if (!zobj1->ce->default_properties_count) { + if (!OBJ_CE(zobj1)->default_properties_count) { return 0; } @@ -1764,10 +1766,10 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ } Z_PROTECT_RECURSION_P(o1); - for (i = 0; i < zobj1->ce->default_properties_count; i++) { + for (i = 0; i < OBJ_CE(zobj1)->default_properties_count; i++) { zval *p1, *p2; - info = zobj1->ce->properties_info_table[i]; + info = OBJ_CE(zobj1)->properties_info_table[i]; if (!info) { continue; @@ -1824,7 +1826,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has const zend_property_info *prop_info = NULL; zend_string *tmp_name = NULL; - property_offset = zend_get_property_offset(zobj->ce, name, 1, cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, 1, cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { value = OBJ_PROP(zobj, property_offset); @@ -1880,7 +1882,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has } result = 0; - if ((has_set_exists != ZEND_PROPERTY_EXISTS) && zobj->ce->__isset) { + if ((has_set_exists != ZEND_PROPERTY_EXISTS) && OBJ_CE(zobj)->__isset) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_ISSET)) { @@ -1896,7 +1898,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has result = zend_is_true(&rv); zval_ptr_dtor(&rv); if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY && result) { - if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) { + if (EXPECTED(!EG(exception)) && OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { (*guard) |= IN_GET; zend_std_call_getter(zobj, name, &rv); (*guard) &= ~IN_GET; @@ -1919,7 +1921,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has ZEND_API zend_string *zend_std_get_class_name(const zend_object *zobj) /* {{{ */ { - return zend_string_copy(zobj->ce->name); + return zend_string_copy(OBJ_CE(zobj)->name); } /* }}} */ @@ -1927,7 +1929,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w { switch (type) { case IS_STRING: { - zend_class_entry *ce = readobj->ce; + zend_class_entry *ce = OBJ_CE(readobj); if (ce->__tostring) { zval retval; GC_ADDREF(readobj); @@ -1955,7 +1957,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zval *func = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE)); if (func == NULL) { diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 4c4b3cf30c13d..487380b4d78c0 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -20,30 +20,31 @@ #include "zend.h" #include "zend_globals.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_API.h" #include "zend_interfaces.h" #include "zend_exceptions.h" #include "zend_weakrefs.h" -static zend_always_inline void _zend_object_std_init(zend_object *object, zend_class_entry *ce) +static zend_always_inline void _zend_object_std_init(zend_object *object, zend_class_reference *class_ref) { GC_SET_REFCOUNT(object, 1); GC_TYPE_INFO(object) = GC_OBJECT; - object->ce = ce; - object->handlers = ce->default_object_handlers; + object->cr = class_ref; + object->handlers = class_ref->ce->default_object_handlers; object->properties = NULL; zend_objects_store_put(object); - if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) { - zval *guard_value = object->properties_table + object->ce->default_properties_count; + if (UNEXPECTED(class_ref->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + zval *guard_value = object->properties_table + class_ref->ce->default_properties_count; ZVAL_UNDEF(guard_value); Z_GUARD_P(guard_value) = 0; } } -ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce) +ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *class_type) { - _zend_object_std_init(object, ce); + _zend_object_std_init(object, ZEND_CE_TO_REF(class_type)); } ZEND_API void zend_object_std_dtor(zend_object *object) @@ -59,8 +60,8 @@ ZEND_API void zend_object_std_dtor(zend_object *object) } } p = object->properties_table; - if (EXPECTED(object->ce->default_properties_count)) { - end = p + object->ce->default_properties_count; + if (EXPECTED(OBJ_CE(object)->default_properties_count)) { + end = p + OBJ_CE(object)->default_properties_count; do { if (Z_REFCOUNTED_P(p)) { if (UNEXPECTED(Z_ISREF_P(p)) && @@ -76,7 +77,7 @@ ZEND_API void zend_object_std_dtor(zend_object *object) } while (p != end); } - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_USE_GUARDS)) { if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) { zval_ptr_dtor_str(p); } else if (Z_TYPE_P(p) == IS_ARRAY) { @@ -96,7 +97,7 @@ ZEND_API void zend_object_std_dtor(zend_object *object) ZEND_API void zend_objects_destroy_object(zend_object *object) { - zend_function *destructor = object->ce->destructor; + zend_function *destructor = OBJ_CE(object)->destructor; if (destructor) { zend_object *old_exception; @@ -109,10 +110,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (EG(current_execute_data)) { zend_class_entry *scope = zend_get_executed_scope(); - if (object->ce != scope) { + if (OBJ_CE(object) != scope) { zend_throw_error(NULL, "Call to private %s::__destruct() from %s%s", - ZSTR_VAL(object->ce->name), + ZSTR_VAL(OBJ_NAME(object)), scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "" ); @@ -121,7 +122,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } else { zend_error(E_WARNING, "Call to private %s::__destruct() from global scope during shutdown ignored", - ZSTR_VAL(object->ce->name)); + ZSTR_VAL(OBJ_NAME(object))); return; } } else { @@ -133,7 +134,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) { zend_throw_error(NULL, "Call to protected %s::__destruct() from %s%s", - ZSTR_VAL(object->ce->name), + ZSTR_VAL(OBJ_NAME(object)), scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "" ); @@ -142,7 +143,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } else { zend_error(E_WARNING, "Call to protected %s::__destruct() from global scope during shutdown ignored", - ZSTR_VAL(object->ce->name)); + ZSTR_VAL(OBJ_NAME(object))); return; } } @@ -184,22 +185,27 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } } -ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new_ref(zend_class_reference *class_ref) { - zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce)); + zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(class_ref->ce)); - _zend_object_std_init(object, ce); + _zend_object_std_init(object, class_ref); return object; } +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) +{ + return zend_objects_new_ref(ZEND_CE_TO_REF(ce)); +} + ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object) { - bool has_clone_method = old_object->ce->clone != NULL; + bool has_clone_method = OBJ_CE(old_object)->clone != NULL; - if (old_object->ce->default_properties_count) { + if (OBJ_CE(old_object)->default_properties_count) { zval *src = old_object->properties_table; zval *dst = new_object->properties_table; - zval *end = src + old_object->ce->default_properties_count; + zval *end = src + OBJ_CE(old_object)->default_properties_count; do { i_zval_ptr_dtor(dst); @@ -268,10 +274,10 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, if (has_clone_method) { GC_ADDREF(new_object); - zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(new_object)->clone, new_object, NULL); - if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { - for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { + if (ZEND_CLASS_HAS_READONLY_PROPS(OBJ_CE(new_object))) { + for (uint32_t i = 0; i < OBJ_CE(new_object)->default_properties_count; i++) { zval* prop = OBJ_PROP_NUM(new_object, i); /* Unconditionally remove the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */ Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; @@ -288,12 +294,12 @@ ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object) /* assume that create isn't overwritten, so when clone depends on the * overwritten one then it must itself be overwritten */ - new_object = zend_objects_new(old_object->ce); + new_object = zend_objects_new_ref(old_object->cr); /* zend_objects_clone_members() expect the properties to be initialized. */ - if (new_object->ce->default_properties_count) { + if (OBJ_CE(new_object)->default_properties_count) { zval *p = new_object->properties_table; - zval *end = p + new_object->ce->default_properties_count; + zval *end = p + OBJ_CE(new_object)->default_properties_count; do { ZVAL_UNDEF(p); p++; diff --git a/Zend/zend_objects.h b/Zend/zend_objects.h index 91d388154dd13..970ff92fb60a9 100644 --- a/Zend/zend_objects.h +++ b/Zend/zend_objects.h @@ -21,9 +21,11 @@ #define ZEND_OBJECTS_H #include "zend.h" +#include "zend_types.h" BEGIN_EXTERN_C() ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce); +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new_ref(zend_class_reference *class_ref); ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce); ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object); diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 80f5b747db710..72fd04233d1c2 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -54,7 +54,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); if (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor) { + || OBJ_CE(obj)->destructor) { GC_ADDREF(obj); obj->handlers->dtor_obj(obj); GC_DELREF(obj); @@ -178,7 +178,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ * GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); if (object->handlers->dtor_obj != zend_objects_destroy_object - || object->ce->destructor) { + || OBJ_CE(object)->destructor) { zend_fiber_switch_block(); GC_SET_REFCOUNT(object, 1); object->handlers->dtor_obj(object); diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 95fa5acf62fb7..24f7b427e63b2 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -98,9 +98,9 @@ static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_en static inline zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot) { - zend_property_info **table = obj->ce->properties_info_table; + zend_property_info **table = OBJ_CE(obj)->properties_info_table; intptr_t prop_num = slot - obj->properties_table; - ZEND_ASSERT(prop_num >= 0 && prop_num < obj->ce->default_properties_count); + ZEND_ASSERT(prop_num >= 0 && prop_num < OBJ_CE(obj)->default_properties_count); return table[prop_num]; } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 93195a1be4c54..3789634b0b173 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -29,6 +29,7 @@ #include "zend_constants.h" #include "zend_observer.h" +#include "zend_types.h" #include "zend_vm.h" static void zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array) @@ -108,17 +109,41 @@ ZEND_API void destroy_zend_function(zend_function *function) zend_function_dtor(&tmp); } +void zend_pnr_release(zend_packed_name_reference pnr, bool uses_arena, bool persistent); + +void zend_name_reference_release( + zend_name_reference *name_ref, bool uses_arena, bool persistent) { + zend_type *arg_type; + zend_string_release(name_ref->name); + ZEND_TYPE_LIST_FOREACH(&name_ref->args, arg_type) { + zend_type_release(*arg_type, persistent); + } ZEND_TYPE_LIST_FOREACH_END(); + if (!uses_arena) { + pefree(name_ref, persistent); + } +} + +void zend_pnr_release(zend_packed_name_reference pnr, bool uses_arena, bool persistent) { + if (ZEND_PNR_IS_COMPLEX(pnr)) { + zend_name_reference_release(ZEND_PNR_COMPLEX_GET_REF(pnr), uses_arena, persistent); + } else { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } +} + + ZEND_API void zend_type_release(zend_type type, bool persistent) { + bool uses_arena = ZEND_TYPE_USES_ARENA(type); if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { zend_type_release(*list_type, persistent); } ZEND_TYPE_LIST_FOREACH_END(); - if (!ZEND_TYPE_USES_ARENA(type)) { + if (!uses_arena) { pefree(ZEND_TYPE_LIST(type), persistent); } - } else if (ZEND_TYPE_HAS_NAME(type)) { - zend_string_release(ZEND_TYPE_NAME(type)); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_pnr_release(ZEND_TYPE_PNR(type), uses_arena, persistent); } } @@ -283,6 +308,15 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) } } +ZEND_API void destroy_zend_class_reference(zval *zv) +{ + zend_class_reference *class_ref = Z_CR_P(zv); + + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(class_ref)); + + efree(class_ref); +} + ZEND_API void destroy_zend_class(zval *zv) { zend_property_info *prop_info; @@ -327,11 +361,15 @@ ZEND_API void destroy_zend_class(zval *zv) return; } + zend_string_release(ZEND_CE_TO_REF(ce)->key); + switch (ce->type) { case ZEND_USER_CLASS: if (!(ce->ce_flags & ZEND_ACC_CACHED)) { - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { - zend_string_release_ex(ce->parent_name, 0); + if (ce->num_parents) { + if (!(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { + zend_pnr_release(ce->parent_name, /* uses_arena */ 1, /* persistent */ 0); + } } zend_string_release_ex(ce->name, 0); @@ -358,6 +396,36 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->num_traits > 0) { _destroy_zend_class_traits_info(ce); } + + if (ce->num_generic_params > 0) { + for (uint32_t i = 0; i < ce->num_generic_params; i++) { + zend_generic_param *param = &ce->generic_params[i]; + zend_string_release(param->name); + zend_type_release(param->bound_type, /* persistent */ 0); + zend_type_release(param->default_type, /* persistent */ 0); + } + efree(ce->generic_params); + } + if (ce->num_bound_generic_args > 0) { + for (uint32_t i = 0; i < ce->num_bound_generic_args; i++) { + zend_type *type = &ce->bound_generic_args[i]; + zend_type_release(*type, /* persistent */ 0); + } + efree(ce->bound_generic_args); + } + } + + if (ce->num_parents && (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(ref)) { + /* The type arguments are owned by bound_generic_args, + * and will be destroyed there. */ + zend_string_release(ref->key); + efree(ref); + } + } + efree(ce->parents); } if (ce->default_properties_table) { @@ -422,6 +490,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->backed_enum_table) { zend_hash_release(ce->backed_enum_table); } + if (ce->num_parents) { + free(ce->parents); + } if (ce->default_properties_table) { zval *p = ce->default_properties_table; zval *end = p + ce->default_properties_count; @@ -510,7 +581,7 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->attributes) { zend_hash_release(ce->attributes); } - free(ce); + free((char *) ce - ZEND_CLASS_ENTRY_HEADER_SIZE); break; } } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 8853d24549fb2..028756dc127d2 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -21,7 +21,11 @@ #include #include "zend.h" +#include "zend_execute.h" +#include "zend_extensions.h" #include "zend_operators.h" +#include "zend_portability.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_globals.h" #include "zend_list.h" @@ -2476,19 +2480,83 @@ ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *ins } return 0; } else { - while (1) { - instance_ce = instance_ce->parent; - if (instance_ce == ce) { - return 1; - } - if (instance_ce == NULL) { - return 0; + if (instance_ce->num_parents) { + for (uint32_t i = 0; i < instance_ce->num_parents; i++) { + zend_class_reference *parent_ref = instance_ce->parents[i]; + if (parent_ref->ce == ce) { + return 1; + } } } + return 0; } } /* }}} */ +static zend_always_inline bool zend_type_lists_compatible( + const zend_type_list *list1, + const zend_class_reference *list1_scope, + const zend_type_list *list2, + const zend_class_reference *list2_scope, + const zend_class_entry *ce) { + /* list1 is complete, while list2 may have defaulted arguments. */ + uint32_t i = 0; + for (; i < list1->num_types; i++) { + const zend_type *type1 = &list1->types[i]; + const zend_type *type2; + if (i < list2->num_types) { + type2 = &list2->types[i]; + } else { + type2 = &ce->generic_params[i].default_type; + } + switch (ce->generic_params[i].variance) { + case ZEND_GENERIC_PARAM_OUT: + if (!zend_type_accepts(type2, list2_scope, type1, list1_scope, NULL)) { + return 0; + } + break; + case ZEND_GENERIC_PARAM_IN: + if (!zend_type_accepts(type1, list1_scope, type2, list2_scope, NULL)) { + return 0; + } + break; + case ZEND_GENERIC_PARAM_INVARIANT: + // TODO: variance-aware zend_types_compatible() + if (!zend_type_accepts(type1, list1_scope, type2, list2_scope, NULL) + || !zend_type_accepts(type2, list2_scope, type1, list1_scope, NULL)) { + return 0; + } + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + } + + return 1; +} + +ZEND_API bool ZEND_FASTCALL instanceof_ref_slow( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope) { + zend_class_entry *instance_ce = instance_ref->ce; + zend_class_entry *ce = ref->ce; + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + return instanceof_function(instance_ce, ce); + } else { + if (instance_ce == ce && zend_type_lists_compatible(&instance_ref->args, instance_scope, &ref->args, ref_scope, ce)) { + return 1; + } + for (uint32_t i = 0; i < instance_ce->num_parents; i++) { + zend_class_reference *parent_ref = instance_ce->parents[i]; + if (parent_ref->ce == ce && zend_type_lists_compatible(&parent_ref->args, instance_scope, &ref->args, ref_scope, ce)) { + return 1; + } + } + return 0; + } +} + #define LOWER_CASE 1 #define UPPER_CASE 2 #define NUMERIC 3 @@ -2817,7 +2885,7 @@ ZEND_API bool ZEND_FASTCALL zend_object_is_true(const zval *op) /* {{{ */ if (zobj->handlers->cast_object(zobj, &tmp, _IS_BOOL) == SUCCESS) { return Z_TYPE(tmp) == IS_TRUE; } - zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name)); + zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(OBJ_NAME(zobj))); return false; } /* }}} */ diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 899567a953c63..e1f13e54c2262 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -21,6 +21,7 @@ #ifndef ZEND_OPERATORS_H #define ZEND_OPERATORS_H +#include "zend_types.h" #include #include #include @@ -66,12 +67,25 @@ ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zv ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce); ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce); +ZEND_API bool ZEND_FASTCALL instanceof_ref_slow( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope); static zend_always_inline bool instanceof_function( const zend_class_entry *instance_ce, const zend_class_entry *ce) { return instance_ce == ce || instanceof_function_slow(instance_ce, ce); } +static zend_always_inline bool instanceof_ref( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope) { + return instance_ref == ref || instanceof_ref_slow(instance_ref, instance_scope, ref, ref_scope); +} + ZEND_API bool zend_string_only_has_ascii_alphanumeric(const zend_string *str); /** diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 910e2eed250fe..ac062b1b77e15 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -634,7 +634,6 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ - typedef enum _zend_known_string_id { #define _ZEND_STR_ID(id, str) id, ZEND_KNOWN_STRINGS(_ZEND_STR_ID) diff --git a/Zend/zend_symbolic_inference.c b/Zend/zend_symbolic_inference.c new file mode 100644 index 0000000000000..dbc2fa0593743 --- /dev/null +++ b/Zend/zend_symbolic_inference.c @@ -0,0 +1,2040 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/* This implements type inference for constructor arguments. + * + * The algorithm is unsound by design. Most notably, it ignores references. + * + * This is implemented as a forward data flow analysis based on zend_inference.c + * + * Types that can not be resolved at compile time, such as the return type of + * function calls (for functions declared in other compilation units) are + * represented as symbols. E.g. the type of the expression `f(1)` is represented + * as `fcall`. Such types can then be resolved at runtime. + * + * Symbolic types may represent an infinity of types and never converge. E.g. + * the variable $a in the following code will successively have the types + * fcall, fcall|int>, and so on: + * + * $a = 1; + * while ($i--) { $a = f($a); } + * + * We handle these by using a variation of the algorithm decribed in: + * Weiss G, Schonberg E. Typefinding recursive structures: A data-flow analysis + * in the presence of infinite type sets. New York University. Courant Institute + * of Mathematical Sciences. Computer Science Department; 1986. + * + * This can be summarized as follows: Uses appearing in the same SCC as an + * opcode defs are considered recursive, and represented with a placeholder. + * + * After a fixpoint is reached we resolve the placeholders. Based on the + * assumption that operations are indempotent (type wise), we can eliminate + * recursivity: fcall with $v1=fcall|int is equivalent to + * fcall|int>. + */ + +#include "zend_arena.h" +#include "zend_closures.h" +#include "zend_compile.h" +#include "zend_operators.h" +#include "zend_portability.h" +#include "zend_string.h" +#include "zend_types.h" +#include "zend_type_tools.h" +#include "zend_type_info.h" +#include "zend_bitset.h" +#include "zend_vm_opcodes.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_constants.h" +#include +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_dump.h" +#include "Optimizer/zend_optimizer.h" +#include "Optimizer/zend_cfg.h" +#include "Optimizer/zend_ssa.h" +#include "Optimizer/zend_scc.h" + +static const zend_op *zend_find_fcall_init_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + +static zend_always_inline zend_type _const_op_type(const zval *zv) { + uint32_t mask; + if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { + // TODO + mask = MAY_BE_ANY; + } else if (Z_TYPE_P(zv) == IS_PNR) { + mask = MAY_BE_STRING; + } else if (Z_TYPE_P(zv) == IS_ARRAY) { + mask = MAY_BE_ARRAY; + } else { + mask = 1 << Z_TYPE_P(zv); + } + + return (zend_type) ZEND_TYPE_INIT_MASK(mask); +} + +#define _ZEND_TYPE_SSA_VAR_BIT (1 << _ZEND_TYPE_EXTRA_FLAGS_SHIFT) +#define _ZEND_TYPE_SYMBOLIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT+1)) + +#define ZEND_TYPE_IS_SSA_VAR(t) (((t).type_mask & _ZEND_TYPE_SSA_VAR_BIT) != 0) +#define ZEND_TYPE_SSA_VAR(t) ((int)(uintptr_t)(t).ptr) + +#define ZEND_TYPE_IS_SYMBOLIC(t) (((t).type_mask & _ZEND_TYPE_SYMBOLIC_BIT) != 0) + +#define ZEND_TYPE_INIT_SYMBOLIC(list, extra_flags) \ + ZEND_TYPE_INIT_PTR(list, _ZEND_TYPE_SYMBOLIC_BIT | _ZEND_TYPE_LIST_BIT, 0, extra_flags) + +#define SYMTYPE_BINOP 1 +#define SYMTYPE_FCALL 2 +#define SYMTYPE_NS_FCALL 3 +#define SYMTYPE_METHOD_CALL 4 +#define SYMTYPE_STATIC_METHOD_CALL 5 +#define SYMTYPE_NEW 6 +#define SYMTYPE_NOT_NULL 7 +#define SYMTYPE_PROP 8 +#define SYMTYPE_CLASS_CONSTANT 9 +#define SYMTYPE_CONSTANT 10 +#define SYMTYPE_CLASS 11 + +#define BINOP_NUMBER_ARRAY 1 +#define BINOP_NUMBER 2 +#define BINOP_LONG_STRING 3 +#define BINOP_STRING 4 +#define BINOP_LONG 5 + +static zend_type_list *zend_symbolic_alloc(zend_arena **arena, int symtype, uint32_t num_types) +{ + zend_type_list *list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); + list->num_types = num_types; + ZEND_TYPE_FULL_MASK(list->types[0]) = symtype; + return list; +} + +static zend_type zend_symbolic_binop(zend_arena **arena, zend_type op1, zend_type op2, uint32_t type_mask) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_BINOP, 4); + ZEND_TYPE_FULL_MASK(list->types[1]) = type_mask; + list->types[2] = op1; + list->types[3] = op2; + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +static zend_type zend_symbolic_binop_auto(zend_arena **arena, size_t opcode, zend_type op1, zend_type op2) +{ + switch (opcode) { + case ZEND_ADD: + return zend_symbolic_binop(arena, op1, op2, BINOP_NUMBER_ARRAY); + case ZEND_MUL: + case ZEND_DIV: + case ZEND_SUB: + case ZEND_POW: + return zend_symbolic_binop(arena, op1, op2, BINOP_NUMBER); + case ZEND_BW_AND: + case ZEND_BW_OR: + case ZEND_BW_XOR: + case ZEND_BW_NOT: + return zend_symbolic_binop(arena, op1, op2, BINOP_LONG_STRING); + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + return zend_symbolic_binop(arena, op1, op2, BINOP_LONG); + case ZEND_CONCAT: + return zend_symbolic_binop(arena, op1, op2, BINOP_STRING); + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + +#define SYMBOLIC_BINOP_TYPE_MASK(list) ZEND_TYPE_FULL_MASK((list)->types[1]) +#define SYMBOLIC_BINOP_OP1(list) (list)->types[2] +#define SYMBOLIC_BINOP_OP2(list) (list)->types[3] + +static zend_type zend_symbolic_fcall(zend_arena **arena, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_FCALL, nargs + 2); + zend_string_addref(fname); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_FCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_FCALL_ARGS(list) &(list)->types[2] +#define SYMBOLIC_FCALL_NARGS(list) ((list)->num_types-2) + +static zend_type zend_symbolic_ns_fcall(zend_arena **arena, zend_string *fname, zend_string *ns_fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NS_FCALL, nargs + 3); + zend_string_addref(fname); + zend_string_addref(ns_fname); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ns_fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NS_FCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_NS_FCALL_NS_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_NS_FCALL_ARGS(list) &(list)->types[3] +#define SYMBOLIC_NS_FCALL_NARGS(list) ((list)->num_types-3) + +static zend_type zend_symbolic_mcall(zend_arena **arena, zend_type object, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_METHOD_CALL, nargs + 3); + ZEND_TYPE_SET_PTR(list->types[0], 0); + zend_string_addref(fname); + list->types[1] = object; + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_MCALL_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_MCALL_OBJECT(list) (list)->types[1] +#define SYMBOLIC_MCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_MCALL_ARGS(list) &(list)->types[3] +#define SYMBOLIC_MCALL_NARGS(list) ((list)->num_types-3) + +static zend_type zend_symbolic_scall(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_STATIC_METHOD_CALL, nargs + 4); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + zend_string_addref(fname); + list->types[1] = object; + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + if (lcname) { + zend_string_addref(lcname); + list->types[4] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_SCALL_LCNAME(list) (list)->types[4] +#define SYMBOLIC_SCALL_NARGS(list) ((list)->num_types-4) + +static zend_type zend_symbolic_new(zend_arena **arena, zend_type class, zend_type *args, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NEW, nargs + 2); + list->types[1] = class; + for (size_t i = 0; i < nargs; i++) { + list->types[i+2] = args[i]; + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NEW_OBJECT(list) (list)->types[1] +#define SYMBOLIC_NEW_ARGS(list) &(list)->types[2] +#define SYMBOLIC_NEW_NARGS(list) ((list)->num_types-2) + +static zend_type zend_symbolic_coalesce(zend_arena **arena, zend_type type) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NOT_NULL, 2); + list->types[1] = type; + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NOT_NULL_OP(list) (list)->types[1] + +static zend_type zend_symbolic_prop(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *pname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_PROP, 4); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + zend_string_addref(pname); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + list->types[3] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(pname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_PROP_OBJECT(list) (list)->types[1] +#define SYMBOLIC_PROP_LCNAME(list) (list)->types[2] +#define SYMBOLIC_PROP_PNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[3]) + +static zend_type zend_symbolic_class_const(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *cname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CLASS_CONSTANT, 3); + zend_string_addref(cname); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + list->types[3] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CCONST_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_CCONST_OBJECT(list) (list)->types[1] +#define SYMBOLIC_CCONST_LCNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_CCONST_CNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[3]) + +static zend_type zend_symbolic_const(zend_arena **arena, zend_string *key1, zend_string *key2) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CONSTANT, 3); + zend_string_addref(key1); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(key1), 0, 0); + if (key2) { + zend_string_addref(key2); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(key2), 0, 0); + } else { + list->types[2] = (zend_type) ZEND_TYPE_INIT_MASK(0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CONST_KEY1(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_CONST_KEY2(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) + +static zend_type zend_symbolic_class(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CLASS, 3); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } else { + list->types[2] = (zend_type) ZEND_TYPE_INIT_MASK(0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CLASS_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_CLASS_OBJECT(list) (list)->types[1] +#define SYMBOLIC_CLASS_LCNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) + +static zend_always_inline zend_type get_ssa_var_info(zend_type *ssa_var_info, int ssa_var_num) +{ + if (ssa_var_num >= 0) { + return ssa_var_info[ssa_var_num]; + } else { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } +} + +static zend_always_inline zend_type get_ssa_recursive_var_info(zend_ssa *ssa, zend_type *ssa_var_info, int embedding_var, int embedded_var) +{ + ZEND_ASSERT(embedding_var >= 0); + + if (embedded_var >= 0) { + zend_ssa_var *var = &ssa->vars[embedded_var]; + if (var->scc && ssa->vars[embedding_var].scc == var->scc) { + return (zend_type) ZEND_TYPE_INIT_PTR((void*)(uintptr_t)embedded_var, _ZEND_TYPE_SSA_VAR_BIT, 0, 0); + } else { + return ssa_var_info[embedded_var]; + } + } else { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } +} + +#define DEFINE_SSA_OP_INFO(opN) \ + static ZEND_ATTRIBUTE_UNUSED zend_always_inline zend_type _ssa_##opN##_info(const zend_op_array *op_array, zend_type *ssa_var_info, const zend_op *opline, const zend_ssa_op *ssa_op) \ + { \ + if (opline->opN##_type == IS_CONST) { \ + return _const_op_type(CRT_CONSTANT(opline->opN)); \ + } else { \ + return get_ssa_var_info(ssa_var_info, ssa_op->opN##_use); \ + } \ + } \ + +#define DEFINE_SSA_OP_REC_INFO(opN) \ + static ZEND_ATTRIBUTE_UNUSED zend_always_inline zend_type _ssa_##opN##_rec_info(const zend_op_array *op_array, zend_ssa *ssa, zend_type *ssa_var_info, int var, const zend_op *opline, const zend_ssa_op *ssa_op) \ + { \ + if (opline->opN##_type == IS_CONST) { \ + return _const_op_type(CRT_CONSTANT(opline->opN)); \ + } else { \ + return get_ssa_recursive_var_info(ssa, ssa_var_info, var, ssa_op->opN##_use); \ + } \ + } \ + +DEFINE_SSA_OP_INFO(op1) +DEFINE_SSA_OP_INFO(op2) +DEFINE_SSA_OP_INFO(result) + +DEFINE_SSA_OP_REC_INFO(op1) +DEFINE_SSA_OP_REC_INFO(op2) +DEFINE_SSA_OP_REC_INFO(result) + +#define OP1_INFO_EX(opline, ssa_op) (_ssa_op1_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP2_INFO_EX(opline, ssa_op) (_ssa_op2_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP1_INFO() (_ssa_op1_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP2_INFO() (_ssa_op2_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa_var_info, (opline+1), (ssa_op+1))) +#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa_var_info, (opline+1), (ssa_op+1))) +#define RES_USE_INFO() (_ssa_result_info(op_array, ssa_var_info, opline, ssa_op)) + +/* Recursive-aware variants: Return a placeholder if the variable is in the same + * SCC as result_var */ +#define OP1_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP2_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op2_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP1_DATA_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP1_REC_INFO() (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, opline, ssa_op)) +#define OP2_REC_INFO() (_ssa_op2_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, opline, ssa_op)) +#define OP1_DATA_REC_INFO() (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, (opline+1), (ssa_op+1))) + +#define UPDATE_SSA_TYPE(_type, _var) \ + do { \ + uint32_t __type = (_type); \ + int __var = (_var); \ + if (__var >= 0) { \ + if (ZEND_TYPE_FULL_MASK(ssa_var_info[__var]) != __type) { \ + ZEND_TYPE_FULL_MASK(ssa_var_info[__var]) = __type; \ + add_usages(op_array, ssa, worklist, __var); \ + } \ + /*zend_bitset_excl(worklist, var);*/ \ + } \ + } while (0) + +#define UPDATE_SSA_TYPE_EX(_type, _var) \ + do { \ + zend_type __type = (_type); \ + int __var = (_var); \ + if (__var >= 0) { \ + if (!zend_type_equals(ssa_var_info[__var], __type)) { \ + ssa_var_info[__var] = __type; \ + add_usages(op_array, ssa, worklist, __var); \ + } \ + /*zend_bitset_excl(worklist, var);*/ \ + } \ + } while (0) + +static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var) +{ + if (ssa->vars[var].phi_use_chain) { + zend_ssa_phi *p = ssa->vars[var].phi_use_chain; + do { + zend_bitset_incl(worklist, p->ssa_var); + p = zend_ssa_next_use_phi(ssa, var, p); + } while (p); + } + if (ssa->vars[var].use_chain >= 0) { + int use = ssa->vars[var].use_chain; + zend_ssa_op *op; + + do { + op = ssa->ops + use; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + op--; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + } else if (use + 1 < op_array->last + && op_array->opcodes[use + 1].opcode == ZEND_OP_DATA) { + op++; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + } + use = zend_ssa_next_use(ssa->ops, var, use); + } while (use >= 0); + } +} + +static void zend_get_fcall_arg_types(zend_arena **arena, zend_ssa *ssa, zend_type *ssa_var_info, const zend_op_array *op_array, const zend_op *opline, int result_var, zend_type *args, int nargs) +{ + int depth = 1; + int arg = 0; + + ZEND_ASSERT(result_var >= 0); + + for (int i = opline - op_array->opcodes + 1; i < op_array->last; i++) { + zend_op *opline = &op_array->opcodes[i]; + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth++; + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth--; + break; + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_USER: + if (depth == 1) { + zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + args[arg] = OP1_REC_INFO_EX(opline, ssa_op, result_var); + arg++; + } + break; + } + if (depth == 0) { + break; + } + } + + ZEND_ASSERT(arg == nargs); +} + +static bool zend_type_equals(zend_type a, zend_type b) +{ + if (ZEND_TYPE_FULL_MASK(a) != ZEND_TYPE_FULL_MASK(b)) { + return false; + } + + if (!ZEND_TYPE_IS_COMPLEX(a) && !ZEND_TYPE_IS_SSA_VAR(a)) { + return true; + } + + if (a.ptr == b.ptr) { + return true; + } + + if (ZEND_TYPE_IS_SSA_VAR(a)) { + return false; + } + + if (ZEND_TYPE_HAS_PNR(a)) { + zend_packed_name_reference pnr_a = ZEND_TYPE_PNR(a); + zend_packed_name_reference pnr_b = ZEND_TYPE_PNR(b); + if (ZEND_PNR_IS_COMPLEX(pnr_a)) { + if (!ZEND_PNR_IS_COMPLEX(pnr_b)) { + return false; + } + zend_name_reference *ref_a = ZEND_PNR_COMPLEX_GET_REF(pnr_a); + zend_name_reference *ref_b = ZEND_PNR_COMPLEX_GET_REF(pnr_b); + if (!zend_string_equals(ref_a->name, ref_b->name)) { + return false; + } + if (ref_a->args.num_types != ref_b->args.num_types) { + return false; + } + for (uint32_t i = 0; i < ref_a->args.num_types; i++) { + if (!zend_type_equals(ref_a->args.types[i], ref_b->args.types[i])) { + return false; + } + } + return true; + } else { + if (ZEND_PNR_IS_COMPLEX(pnr_b)) { + return false; + } + zend_string *name_a = ZEND_PNR_SIMPLE_GET_NAME(pnr_a); + zend_string *name_b = ZEND_PNR_SIMPLE_GET_NAME(pnr_b); + return zend_string_equals(name_a, name_b); + } + } + + ZEND_ASSERT(ZEND_TYPE_HAS_LIST(a)); + + zend_type_list *a_list = ZEND_TYPE_LIST(a); + zend_type_list *b_list = ZEND_TYPE_LIST(b); + if (a_list->num_types != b_list->num_types) { + return false; + } + for (uint32_t i = 0; i < a_list->num_types; i++) { + if (!zend_type_equals(a_list->types[i], b_list->types[i])) { + return false; + } + } + + return true; +} + +static bool zend_union_type_contains(zend_type container, zend_type t) +{ + if (ZEND_TYPE_IS_UNION(container)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(container), single_type) { + if (zend_type_equals(*single_type, t)) { + return true; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return false; + } else { + return zend_type_equals(container, t); + } +} + +static zend_result zend_resolve_relative_class_base(zend_arena **arena, + uint32_t *fetch_type, zend_type *object, zend_string **lcname, + const zend_op_array *op_array, const znode_op *op) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + return FAILURE; + } + zend_packed_name_reference pnr; + if (op_array->scope->num_generic_params) { + zend_name_reference *name_ref = zend_arena_alloc( + arena, ZEND_CLASS_REF_SIZE(op_array->scope->num_generic_params)); + name_ref->name = op_array->scope->name; + zend_string_addref(name_ref->name); + for (uint32_t i = 0; i < op_array->scope->num_generic_params; i++) { + name_ref->args.types[i] = (zend_type) ZEND_TYPE_INIT_GENERIC_PARAM(i, 0); + } + name_ref->args.num_types = op_array->scope->num_generic_params; + zend_compile_name_reference_key(name_ref, NULL); + pnr = ZEND_PNR_ENCODE_REF(name_ref); + } else { + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + pnr = ZEND_PNR_ENCODE_NAME(cname); + } + *lcname = ZEND_CE_TO_REF(op_array->scope->name)->key; + *object = (zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0); + *fetch_type = op->num & ZEND_FETCH_CLASS_MASK; + return SUCCESS; +} + +static zend_result zend_update_symbolic_var_type(zend_arena **arena, zend_ssa *ssa, zend_bitset worklist, zend_type *ssa_var_info, const zend_op_array *op_array, const zend_op *opline) +{ + zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + + if (opline->opcode == ZEND_OP_DATA) { + opline--; + ssa_op--; + } + + zend_type t1 = OP1_INFO(); + zend_type t2 = OP2_INFO(); + +label: + if (0) goto label; + + /* If one of the operands cannot have any type, this means the operand derives from + * unreachable code. Propagate the empty result early, so that that the following + * code may assume that operands have at least one type. */ + if (!t1.type_mask + || !t2.type_mask + || (ssa_op->result_use >= 0 && !RES_USE_INFO().type_mask) + || ((opline->opcode == ZEND_ASSIGN_DIM_OP + || opline->opcode == ZEND_ASSIGN_OBJ_OP + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP + || opline->opcode == ZEND_ASSIGN_DIM + || opline->opcode == ZEND_ASSIGN_OBJ) + && !OP1_DATA_INFO().type_mask)) { + if (ssa_op->result_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->result_def); + } + if (ssa_op->op1_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->op1_def); + } + if (ssa_op->op2_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->op2_def); + } + if (opline->opcode == ZEND_ASSIGN_DIM_OP + || opline->opcode == ZEND_ASSIGN_OBJ_OP + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP + || opline->opcode == ZEND_ASSIGN_DIM + || opline->opcode == ZEND_ASSIGN_OBJ) { + if ((ssa_op+1)->op1_def >= 0) { + UPDATE_SSA_TYPE(0, (ssa_op+1)->op1_def); + } + } + + return SUCCESS; + } + + switch (opline->opcode) { + case ZEND_ASSIGN: + UPDATE_SSA_TYPE_EX(t2, ssa_op->op1_def); + UPDATE_SSA_TYPE_EX(t2, ssa_op->result_def); + break; + case ZEND_QM_ASSIGN: + case ZEND_COPY_TMP: + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + break; + case ZEND_ASSIGN_DIM: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + zend_type d1 = OP1_DATA_INFO(); + UPDATE_SSA_TYPE_EX(d1, ssa_op->result_def); + break; + } + case ZEND_UNSET_CV: + UPDATE_SSA_TYPE(MAY_BE_NULL, ssa_op->op1_def); + break; + case ZEND_UNSET_DIM: + case ZEND_UNSET_OBJ: + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + break; + case ZEND_INIT_ARRAY: + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ADD_ARRAY_UNPACK: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_ASSIGN_DIM_OP: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + // TODO: ArrayAccess (if op1 is an object, we can do better) + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY), + OP1_DATA_REC_INFO_EX(opline+1, ssa_op+1, ssa_op->op1_def)); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_RW: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_FETCH_GLOBALS: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_INCLUDE_OR_EVAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_FETCH_DIM_R: + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_IS: + case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_LIST_R: + case ZEND_FETCH_LIST_W: + /* TODO: generic arrays */ + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + zend_type d1 = OP1_DATA_INFO(); + UPDATE_SSA_TYPE_EX(d1, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_OBJ_OP: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_type d1 = OP1_DATA_REC_INFO(); + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + zend_symbolic_prop(arena, 0, OP1_REC_INFO(), NULL, + Z_STR_P(CRT_CONSTANT(opline->op2))), + d1); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_OP: + case ZEND_ASSIGN_STATIC_PROP_REF: { + // TODO: intersect prop type and op2 + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_BEGIN_SILENCE: + UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); + break; + case ZEND_BIND_GLOBAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_BIND_INIT_STATIC_OR_JMP: + /* TODO */ + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_BIND_LEXICAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op2_def); + break; + case ZEND_BIND_STATIC: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_CALLABLE_CONVERT: { + zend_string *cname = zend_ce_closure->name; + zend_type t = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_CAST: + /* TODO: preserve object type */ + UPDATE_SSA_TYPE(1 << opline->extended_value, ssa_op->result_def); + break; + case ZEND_TYPE_CHECK: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_VERIFY_RETURN_TYPE: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + } else { + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + } + break; + case ZEND_CLONE: + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + break; + case ZEND_COUNT: + case ZEND_STRLEN: + case ZEND_FUNC_NUM_ARGS: + UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); + break; + case ZEND_DECLARE_ANON_CLASS: + /* TODO: intersect parent and interfaces */ + UPDATE_SSA_TYPE(MAY_BE_OBJECT, ssa_op->result_def); + break; + case ZEND_DEFINED: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_FETCH_CONSTANT: { + zend_type t = zend_symbolic_const(arena, + Z_STR_P(CRT_CONSTANT(opline->op2) + 1), + (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) + ? Z_STR_P(CRT_CONSTANT(opline->op2) + 2) : NULL); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_OP: { + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + OP1_REC_INFO_EX(opline, ssa_op, ssa_op->op1_def), + OP2_REC_INFO_EX(opline, ssa_op, ssa_op->op1_def)); + UPDATE_SSA_TYPE_EX(t, ssa_op->op1_def); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_FETCH_OBJ_FUNC_ARG: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op1_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, &opline->op1) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_prop(arena, fetch_type, OP1_REC_INFO(), + lcname, Z_STR_P(CRT_CONSTANT(opline->op2))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_STATIC_PROP_R: + case ZEND_FETCH_STATIC_PROP_W: + case ZEND_FETCH_STATIC_PROP_RW: + case ZEND_FETCH_STATIC_PROP_IS: + case ZEND_FETCH_STATIC_PROP_UNSET: + case ZEND_FETCH_STATIC_PROP_FUNC_ARG: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op2_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, &opline->op2) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else if (opline->op2_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op2); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_prop(arena, fetch_type, object, + lcname, Z_STR_P(CRT_CONSTANT(opline->op1))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_CLASS_CONSTANT: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_type object = {0}; + zend_string *lcname = NULL; + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + if (opline->op1_type == IS_UNUSED) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + fetch_type = opline->op1.num & ZEND_FETCH_CLASS_MASK; + } else if (opline->op1_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_class_const(arena, fetch_type, object, + lcname, Z_STR_P(CRT_CONSTANT(opline->op2))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_BOOL_NOT: + case ZEND_BOOL_XOR: + case ZEND_BOOL: + case ZEND_IS_IDENTICAL: + case ZEND_IS_NOT_IDENTICAL: + case ZEND_IS_EQUAL: + case ZEND_IS_NOT_EQUAL: + case ZEND_IS_SMALLER: + case ZEND_IS_SMALLER_OR_EQUAL: + case ZEND_INSTANCEOF: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_CASE: + case ZEND_CASE_STRICT: + case ZEND_ISSET_ISEMPTY_CV: + case ZEND_ISSET_ISEMPTY_VAR: + case ZEND_ISSET_ISEMPTY_DIM_OBJ: + case ZEND_ISSET_ISEMPTY_PROP_OBJ: + case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_ASSERT_CHECK: + case ZEND_IN_ARRAY: + case ZEND_ARRAY_KEY_EXISTS: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_ADD: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_SUB: + case ZEND_POW: + case ZEND_BW_AND: + case ZEND_BW_OR: + case ZEND_BW_XOR: + case ZEND_BW_NOT: + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + case ZEND_CONCAT: { + zend_type t = zend_symbolic_binop_auto(arena, opline->opcode, + OP1_REC_INFO(), OP2_REC_INFO()); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: { + UPDATE_SSA_TYPE(MAY_BE_LONG | MAY_BE_DOUBLE, ssa_op->result_def); + UPDATE_SSA_TYPE(MAY_BE_LONG | MAY_BE_DOUBLE, ssa_op->op1_def); + break; + } + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: { + zend_type_list *list = zend_arena_calloc(arena, 1, ZEND_TYPE_LIST_SIZE(opline->extended_value + 1)); + list->num_types = opline->extended_value + 1; + list->types[0] = t1; + zend_type result = ZEND_TYPE_INIT_UNION(list, _ZEND_TYPE_ARENA_BIT); + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: { + const zend_op *init = zend_find_fcall_init_op(op_array, opline); + + if (init->op2_type != IS_CONST) { + // TODO: invokable + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + + uint32_t nargs = init->extended_value; + zend_type *args; + + zend_type result; + + switch (init->opcode) { + case ZEND_INIT_FCALL_BY_NAME: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + result = zend_symbolic_fcall(arena, fname, nargs); + args = SYMBOLIC_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_FCALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)); + result = zend_symbolic_fcall(arena, fname, nargs); + args = SYMBOLIC_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_NS_FCALL_BY_NAME: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + zend_string *ns_fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+2); + result = zend_symbolic_ns_fcall(arena, fname, ns_fname, nargs); + args = SYMBOLIC_NS_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_METHOD_CALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + zend_type object = OP1_REC_INFO_EX(init, &ssa->ops[init-op_array->opcodes], ssa_op->result_def); + result = zend_symbolic_mcall(arena, object, fname, nargs); + args = SYMBOLIC_MCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_STATIC_METHOD_CALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op1_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, + &init->op1) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else if (opline->op1_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + result = zend_symbolic_scall(arena, fetch_type, object, + lcname, fname, nargs); + args = SYMBOLIC_MCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } + + zend_get_fcall_arg_types(arena, ssa, ssa_var_info, op_array, init, + ssa_op->result_def, args, nargs); + + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + + break; + } + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_UNPACK: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + zend_type result = RES_USE_INFO(); + if (!ZEND_TYPE_IS_UNION(result)) { + const zend_op *init_op = zend_find_fcall_init_op(op_array, opline); + zend_type_list *list = zend_arena_calloc(arena, 1, ZEND_TYPE_LIST_SIZE(init_op->extended_value + 1)); + list->num_types = opline->extended_value + 1; + list->types[opline->extended_value+1] = t1; + result = (zend_type) ZEND_TYPE_INIT_UNION(list, _ZEND_TYPE_ARENA_BIT); + } else { + zend_type_list *list = ZEND_TYPE_LIST(result); + // TODO: wasting arena space + zend_type_list *new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_ASSERT(opline->extended_value+1 < list->num_types); + new_list->types[opline->extended_value+1] = t1; + ZEND_TYPE_SET_PTR(result, new_list); + } + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_FETCH_THIS: { + if (!op_array->scope) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + // TODO: generic scope + zend_packed_name_reference pnr = ZEND_PNR_ENCODE_NAME(op_array->scope->name); + UPDATE_SSA_TYPE_EX((zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0), ssa_op->result_def); + break; + } + case ZEND_FETCH_CLASS: { + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op2_type == IS_UNUSED) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK; + } else if (opline->op2_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP2_REC_INFO(); + } + zend_type t = zend_symbolic_class(arena, fetch_type, object, + lcname); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_NEW: { + if (opline->op1_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + + zval *zclass = CRT_CONSTANT(opline->op1); + zend_packed_name_reference pnr = Z_PNR_P(zclass); + if (ZEND_PNR_IS_COMPLEX(pnr)) { + UPDATE_SSA_TYPE_EX((zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0), ssa_op->result_def); + break; + } + + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + zend_type_list *args; + if (Z_TYPE_P(ztypes) == IS_NULL) { + int nargs = opline->extended_value; + args = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(nargs)); + args->num_types = nargs; + ZVAL_PTR(ztypes, args); + } else { + args = Z_PTR_P(ztypes); + } + + zend_get_fcall_arg_types(arena, ssa, ssa_var_info, op_array, opline, ssa_op->result_def, args->types, args->num_types); + + zend_string *cname = Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1)); + zend_string_addref(cname); + zend_type class = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + zend_type result = zend_symbolic_new(arena, class, args->types, args->num_types); + + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + break; + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: { + /* TODO: symbolic type should be "iter_key" or "iter_value" */ + /* TODO: generic arrays */ + /* TODO: generic iterable and Iterator */ + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_JMP_SET: + case ZEND_COALESCE: { + zend_type t = zend_symbolic_coalesce(arena, OP1_REC_INFO()); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FAST_CONCAT: + case ZEND_ROPE_INIT: + case ZEND_ROPE_ADD: + case ZEND_ROPE_END: + UPDATE_SSA_TYPE(MAY_BE_STRING, ssa_op->result_def); + break; + case ZEND_RECV: + case ZEND_RECV_INIT: { + zend_arg_info *arg_info = &op_array->arg_info[opline->op1.num-1]; + zend_type t = arg_info->type; + if (ZEND_TYPE_IS_SET(t)) { + ZEND_TYPE_FULL_MASK(t) &= _ZEND_TYPE_MASK; + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_RECV_VARIADIC: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_DECLARE_LAMBDA_FUNCTION: { + zend_string *cname = zend_ce_closure->name; + zend_type t = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + default: + fprintf(stderr, "Unhandled opcode: %d\n", opline->opcode); + break; + } + + return SUCCESS; +} + +static void zend_type_list_copy(zend_type_list *dest, zend_type_list *src, uint32_t num_types) +{ + zend_type *cur = src->types; + zend_type *end = cur+num_types; + zend_type *cur_dest = dest->types; + for (; cur < end; cur++, cur_dest++) { + *cur_dest = *cur; + } + dest->num_types = num_types; +} + +static zend_result zend_resolve_recursive_type_placeholders(zend_arena **arena, zend_ssa *ssa, zend_type *ssa_var_info, zend_type *t, zend_bitset seen) +{ + if (ZEND_TYPE_IS_SSA_VAR(*t)) { + int var = ZEND_TYPE_SSA_VAR(*t); + if (zend_bitset_in(seen, var)) { + return FAILURE; + } + zend_bitset_incl(seen, var); + zend_type tmp = ssa_var_info[var]; + zend_result result = zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen); + zend_bitset_excl(seen, var); + *t = tmp; + return result; + } + + if (ZEND_TYPE_IS_UNION(*t) || ZEND_TYPE_IS_INTERSECTION(*t)) { + zend_type *list_type; + zend_type_list *list = ZEND_TYPE_LIST(*t); + zend_type_list *new_list = NULL; + ZEND_TYPE_LIST_FOREACH(list, list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types-1)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_LIST(*t, new_list); + } + continue; + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_LIST(*t, new_list); + } + } + if (new_list) { + new_list->types[new_list->num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + if (new_list && new_list->num_types == 0) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } + } else if (ZEND_TYPE_HAS_COMPLEX_PNR(*t)) { + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(*t)); + zend_name_reference *new_ref = NULL; + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(&ref->args, list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_ref == NULL) { + new_ref = zend_arena_alloc(arena, ZEND_CLASS_REF_SIZE(ref->args.num_types)); + zend_type_list_copy(&new_ref->args, &ref->args, list_type-ref->args.types); + new_ref->name = ref->name; + ZEND_TYPE_SET_PNR(*t, ZEND_PNR_ENCODE_REF(new_ref)); + } + } + if (new_ref) { + new_ref->args.types[new_ref->args.num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_IS_SYMBOLIC(*t)) { + zend_type_list *new_list = NULL; + zend_type_list *list = ZEND_TYPE_LIST(*t); + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*t), list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_PTR(*t, new_list); + } + } + if (new_list) { + new_list->types[new_list->num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + } + + return SUCCESS; +} + +static void zend_resolve_recursive_placeholders(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa, zend_type *ssa_var_info) +{ + ALLOCA_FLAG(use_heap); + uint32_t seen_len = zend_bitset_len(ssa->vars_count); + zend_bitset seen = ZEND_BITSET_ALLOCA(seen_len, use_heap); + zend_bitset_clear(seen, seen_len); + + for (int i = 0; i < op_array->last; i++) { + const zend_op *opline = &op_array->opcodes[i]; + if (opline->opcode == ZEND_NEW && opline->op1_type == IS_CONST && + ZEND_PNR_IS_SIMPLE(Z_PNR_P(CRT_CONSTANT(opline->op1)))) { + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + if (Z_TYPE_P(ztypes) != IS_NULL) { + zend_type_list *args = Z_PTR_P(ztypes); + for (uintptr_t j = 0; j < args->num_types; j++) { + zend_type tmp = args->types[j]; + if (ZEND_TYPE_IS_SSA_VAR(tmp)) { + tmp = ssa_var_info[ZEND_TYPE_SSA_VAR(tmp)]; + } + zend_result result = zend_resolve_recursive_type_placeholders(arena, + ssa, ssa_var_info, &tmp, seen); + //ZEND_ASSERT(tmp.type_mask != 0); + args->types[j] = tmp; + ZEND_ASSERT(result == SUCCESS); + } + } + } + } + + free_alloca(seen, use_heap); +} + +static zend_result zend_build_type_pnr_keys(zend_type type) +{ + zend_type *type_elem; + ZEND_TYPE_FOREACH(type, type_elem) { + if (ZEND_TYPE_HAS_COMPLEX_PNR(*type_elem)) { + if (ZEND_TYPE_IS_SYMBOLIC(*type_elem)) { + return FAILURE; + } + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(*type_elem)); + for (uint32_t i = 0; i < ref->args.num_types; i++) { + if (zend_build_type_pnr_keys(ref->args.types[i]) == FAILURE) { + return FAILURE; + } + } + zend_string *lcname = zend_string_tolower(ref->name); + zend_compile_name_reference_key(ref, lcname); + zend_string_release(lcname); + } else if (ZEND_TYPE_HAS_LIST(*type_elem)) { + zend_build_type_pnr_keys(*type_elem); + } + } ZEND_TYPE_FOREACH_END(); + + return SUCCESS; +} + +static void zend_build_pnr_keys(const zend_op_array *op_array) +{ + for (int i = 0; i < op_array->last; i++) { + const zend_op *opline = &op_array->opcodes[i]; + if (opline->opcode == ZEND_NEW && opline->op1_type == IS_CONST && + ZEND_PNR_IS_SIMPLE(Z_PNR_P(CRT_CONSTANT(opline->op1)))) { + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + if (Z_TYPE_P(ztypes) != IS_NULL) { + zend_type_list *args = Z_PTR_P(ztypes); + for (uintptr_t j = 0; j < args->num_types; j++) { + zend_build_type_pnr_keys(args->types[j]); + } + } + } + } +} + +void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra); + +/* Infers the symbolic type of ZEND_NEW arguments, at compile time. */ +ZEND_API zend_result zend_symbolic_inference(const zend_op_array *op_array, zend_arena **arena) +{ + zend_ssa *ssa = zend_arena_alloc(arena, sizeof(zend_ssa)); + memset(ssa, 0, sizeof(zend_ssa)); + + zend_build_cfg(arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg); + + zend_cfg_build_predecessors(arena, &ssa->cfg); + + /* Compute Dominators Tree */ + zend_cfg_compute_dominators_tree(op_array, &ssa->cfg); + + /* Identify reducible and irreducible loops */ + zend_cfg_identify_loops(op_array, &ssa->cfg); + +#if 0 + if (zend_string_equals_cstr(op_array->function_name, "main", 4)) { + zend_dump_op_array(op_array, ZEND_DUMP_CFG, "symbolic inference (CFG)", &ssa->cfg); + } +#endif + + zend_script script = {0}; + uint32_t build_flags = ZEND_SSA_CALL_CHAINS; + if (zend_build_ssa(arena, &script, op_array, build_flags, ssa) == FAILURE) { + return FAILURE; + } + + zend_ssa_compute_use_def_chains(arena, op_array, ssa); + zend_ssa_find_sccs(op_array, ssa); + +#if 0 + if (!op_array->function_name || zend_string_equals_cstr(op_array->function_name, "test", 4)) { + //ssa_verify_integrity((zend_op_array*)op_array, ssa, "symbolic inference"); + zend_dump_op_array(op_array, ZEND_DUMP_SSA, "symbolic inference", ssa); + zend_dump_ssa_variables(op_array, ssa, 0); + } +#endif + + ALLOCA_FLAG(use_heap); + uint32_t worklist_len = zend_bitset_len(ssa->vars_count); + zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap); + memset(worklist, 0, sizeof(zend_ulong) * worklist_len); + for (int i = op_array->last_var; i < ssa->vars_count; i++) { + zend_bitset_incl(worklist, i); + } + + zend_ssa_var *ssa_vars = ssa->vars; + zend_type *ssa_var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(*ssa_var_info)); + for (int i = 0; i < op_array->last_var; i++) { + ZEND_TYPE_FULL_MASK(ssa_var_info[i]) = MAY_BE_UNDEF; + } + + zend_basic_block *blocks = ssa->cfg.blocks; + while (!zend_bitset_empty(worklist, worklist_len)) { + int j = zend_bitset_first(worklist, worklist_len); + zend_bitset_excl(worklist, j); + if (ssa_vars[j].definition_phi) { + zend_ssa_phi *p = ssa_vars[j].definition_phi; + if (p->pi >= 0) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[0]); + UPDATE_SSA_TYPE_EX(tmp, j); + } else { + zend_type tmp; + ALLOCA_FLAG(use_heap); + zend_type_list *new_list = NULL; + + for (int i = 0; i < blocks[p->block].predecessors_count; i++) { + if (i == 0) { + tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + } else if (!ZEND_TYPE_IS_COMPLEX(tmp) && !ZEND_TYPE_IS_SSA_VAR(tmp)) { + uint32_t mask = ZEND_TYPE_PURE_MASK(tmp); + tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + ZEND_TYPE_FULL_MASK(tmp) |= mask; + } else { + zend_type tmp2 = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (tmp2.type_mask == 0) { + continue; + } + + if (ZEND_TYPE_IS_INTERSECTION(tmp2)) { + if (zend_type_equals(tmp, tmp2)) { + continue; + } + if (!new_list) { + uint32_t num_types = ZEND_TYPE_IS_UNION(tmp) ? ZEND_TYPE_LIST(tmp)->num_types : 1; + for (int j = i; j < blocks[p->block].predecessors_count; j++) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (ZEND_TYPE_IS_UNION(tmp)) { + num_types += ZEND_TYPE_LIST(tmp)->num_types; + } else if (ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) { + num_types++; + } + } + + new_list = do_alloca(ZEND_TYPE_LIST_SIZE(num_types), use_heap); + if (ZEND_TYPE_IS_UNION(tmp)) { + memcpy(new_list, ZEND_TYPE_LIST(tmp), ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(tmp)->num_types)); + } else { + new_list->types[0] = tmp; + new_list->num_types = 1; + } + + ZEND_TYPE_SET_LIST(tmp, new_list); + ZEND_TYPE_FULL_MASK(tmp) = ZEND_TYPE_PURE_MASK(tmp) | _ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT; + } + new_list->types[new_list->num_types++] = tmp2; + continue; + } + + if (!ZEND_TYPE_IS_COMPLEX(tmp2) && !ZEND_TYPE_IS_SSA_VAR(tmp2)) { + ZEND_TYPE_FULL_MASK(tmp) |= ZEND_TYPE_PURE_MASK(tmp2); + continue; + } + + zend_type *tmp2_elem; + ZEND_TYPE_FOREACH_EX(tmp2, tmp2_elem, ~_ZEND_TYPE_SYMBOLIC_BIT) { + if (zend_union_type_contains(tmp, *tmp2_elem)) { + continue; + } + if (!new_list) { + uint32_t num_types = ZEND_TYPE_IS_UNION(tmp) ? ZEND_TYPE_LIST(tmp)->num_types : 1; + for (int j = i; j < blocks[p->block].predecessors_count; j++) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (ZEND_TYPE_IS_UNION(tmp)) { + num_types += ZEND_TYPE_LIST(tmp)->num_types; + } else if (ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) { + num_types++; + } + } + + new_list = do_alloca(ZEND_TYPE_LIST_SIZE(num_types), use_heap); + if (ZEND_TYPE_IS_UNION(tmp)) { + memcpy(new_list, ZEND_TYPE_LIST(tmp), ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(tmp)->num_types)); + } else { + new_list->types[0] = tmp; + new_list->num_types = 1; + } + + ZEND_TYPE_SET_LIST(tmp, new_list); + ZEND_TYPE_FULL_MASK(tmp) = ZEND_TYPE_PURE_MASK(tmp) | _ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT; + } + new_list->types[new_list->num_types++] = *tmp2_elem; + } ZEND_TYPE_FOREACH_END(); + } + } + + if (new_list) { + size_t size = ZEND_TYPE_LIST_SIZE(new_list->num_types); + zend_type_list *list = zend_arena_alloc(arena, size); + memcpy(list, new_list, size); + ZEND_TYPE_SET_LIST(tmp, list); + free_alloca(new_list, use_heap); + } + + UPDATE_SSA_TYPE_EX(tmp, j); + } + } else if (ssa_vars[j].definition >= 0) { + int i = ssa_vars[j].definition; + if (zend_update_symbolic_var_type(arena, ssa, worklist, ssa_var_info, op_array, op_array->opcodes + i) == FAILURE) { + return FAILURE; + } + } + } + + free_alloca(worklist, use_heap); + + zend_resolve_recursive_placeholders(arena, op_array, ssa, ssa_var_info); + zend_build_pnr_keys(op_array); + + return SUCCESS; +} + +static zend_type zend_resolve_overloaded_type(zend_type type) +{ + zend_type result = ZEND_TYPE_INIT_MASK(0); + zend_type *single_type; + + ZEND_ASSERT(!ZEND_TYPE_IS_SYMBOLIC(type)); + + ZEND_TYPE_FOREACH(type, single_type) { + if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(type); + zend_class_entry *ce = zend_fetch_class(name, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + if (!ce) { + continue; + } + if (ce->default_object_handlers->do_operation) { + // TODO + if (zend_string_equals_cstr(ce->name, "GMP", 3)) { + result = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ce->name), 0, 0); + } else { + ZEND_TYPE_FULL_MASK(result) = IS_OBJECT; + } + // TODO: join with existing result + return result; + } + } else if (ZEND_TYPE_IS_INTERSECTION(type)) { + zend_type inner = zend_resolve_overloaded_type(*single_type); + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(inner)); /* TODO */ + ZEND_TYPE_FULL_MASK(result) |= ZEND_TYPE_FULL_MASK(inner); + } + } ZEND_TYPE_FOREACH_END(); + + return result; +} + +zend_type zend_materialize_generic_types(zend_arena **arena, zend_type t, zend_class_reference *scope); + +static zend_result zend_resolve_relative_class(zend_arena **arena, + uint32_t fetch_type, zend_type *object) { + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*object)); + switch (fetch_type) { + case ZEND_FETCH_CLASS_PARENT: { + zend_class_reference *class_ref = zend_lookup_class_by_pnr( + ZEND_TYPE_PNR(*object), NULL, ZEND_FETCH_CLASS_SILENT); + if (!class_ref || !class_ref->ce->num_parents) { + return FAILURE; + } + zend_class_reference *parent = class_ref->ce->parents[0]; + if (parent->args.num_types == 0) { + ZEND_TYPE_SET_PNR(*object, ZEND_PNR_ENCODE_NAME(parent->ce->name)); + } else { + zend_name_reference *name_ref = zend_arena_alloc( + arena, ZEND_CLASS_REF_SIZE(parent->args.num_types)); + memcpy(name_ref, parent, ZEND_CLASS_REF_SIZE(parent->args.num_types)); + name_ref->name = parent->ce->name; + ZEND_TYPE_SET_PNR(*object, ZEND_PNR_ENCODE_REF(name_ref)); + *object = zend_materialize_generic_types(arena, *object, class_ref); + } + break; + } + case ZEND_FETCH_CLASS_STATIC: + return FAILURE; + case ZEND_FETCH_CLASS_DEFAULT: + case ZEND_FETCH_CLASS_SELF: + break; + default: + ZEND_UNREACHABLE(); + } + + return SUCCESS; +} + +zend_type zend_resolve_symbolic_type_symbolic(zend_arena **arena, zend_type type); + +/* Resolve a symbolic type in the form fcall to a concrete + * zend_type at runtime. Result is cacheable. */ +ZEND_API zend_type zend_resolve_symbolic_type(zend_arena **arena, zend_type type) +{ + if (ZEND_TYPE_IS_UNION(type)) { + zend_type_list *list = ZEND_TYPE_LIST(type); + zend_type *single_type; + uint32_t num_types = 0; + ZEND_TYPE_LIST_FOREACH(list, single_type) { + zend_type t = zend_resolve_symbolic_type(arena, *single_type); + if (!ZEND_TYPE_IS_COMPLEX(t)) { + ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(t); + } else { + list->types[num_types++] = t; + } + } ZEND_TYPE_LIST_FOREACH_END(); + list->num_types = num_types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(type) = ZEND_TYPE_PURE_MASK(type); + } + return type; + } + + if (ZEND_TYPE_IS_SYMBOLIC(type)) { + uint32_t mask = ZEND_TYPE_PURE_MASK(type); + type = zend_resolve_symbolic_type_symbolic(arena, type); + ZEND_TYPE_FULL_MASK(type) |= mask; + } + + return type; +} + +zend_type zend_resolve_symbolic_type_symbolic(zend_arena **arena, zend_type type) +{ + zend_type_list *list = ZEND_TYPE_LIST(type); + uint32_t symtype = ZEND_TYPE_FULL_MASK(list->types[0]); + switch (symtype) { + case SYMTYPE_BINOP: { + zend_type a = zend_resolve_symbolic_type(arena, SYMBOLIC_BINOP_OP1(list)); + zend_type b = zend_resolve_symbolic_type(arena, SYMBOLIC_BINOP_OP2(list)); + uint32_t mask = ZEND_TYPE_FULL_MASK(a) | ZEND_TYPE_FULL_MASK(b); + switch (SYMBOLIC_BINOP_TYPE_MASK(list)) { + case BINOP_NUMBER: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + } + break; + } + case BINOP_LONG: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + } + break; + } + case BINOP_NUMBER_ARRAY: { + switch (mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL)) { + case 0: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + case MAY_BE_ARRAY: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY); + } + break; + } + case BINOP_LONG_STRING: { + switch (mask & + ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_BOOL | MAY_BE_NULL)) { + case 0: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + case MAY_BE_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + } + break; + } + case BINOP_STRING: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + } + break; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } + + /* Operator may be overloaded */ + a = zend_resolve_overloaded_type(a); + if (ZEND_TYPE_FULL_MASK(a)) { + return a; + } + b = zend_resolve_overloaded_type(b); + if (ZEND_TYPE_FULL_MASK(b)) { + return b; + } + + /* We could not prove the operator is overloaded: fallback to + * optimistic defaults */ + switch (SYMBOLIC_BINOP_TYPE_MASK(list)) { + case BINOP_NUMBER: + case BINOP_NUMBER_ARRAY: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + case BINOP_LONG: + case BINOP_LONG_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + case BINOP_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + EMPTY_SWITCH_DEFAULT_CASE(); + } + + ZEND_UNREACHABLE(); + } + case SYMTYPE_FCALL: { + // TODO: generic functions + zend_string *fname = SYMBOLIC_FCALL_FNAME(list); + zval *zfunc = zend_hash_find_known_hash(EG(function_table), fname); + if (!zfunc) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_function *func = Z_FUNC_P(zfunc); + if (!func->common.arg_info) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + return (func->common.arg_info - 1)->type; + } + case SYMTYPE_METHOD_CALL: + case SYMTYPE_STATIC_METHOD_CALL: { + zend_type object = SYMBOLIC_MCALL_OBJECT(list); + if (ZEND_TYPE_HAS_PNR(object) && zend_resolve_relative_class( + arena, SYMBOLIC_MCALL_FETCH_TYPE(list), + &object) == FAILURE) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_type result = {0}; + zend_type *object_elem; + ZEND_TYPE_FOREACH_EX(SYMBOLIC_MCALL_OBJECT(list), object_elem, ~(_ZEND_TYPE_INTERSECTION_BIT|_ZEND_TYPE_SYMBOLIC_BIT)) { + zend_type resolved = zend_resolve_symbolic_type(arena, *object_elem); + zend_type inner = {0}; + ZEND_TYPE_FOREACH(resolved, object_elem) { + if (!ZEND_TYPE_HAS_PNR(*object_elem)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_class_reference *class_ref; + + if (ZEND_TYPE_HAS_COMPLEX_PNR(resolved)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(resolved)), NULL, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + } else { + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(resolved); + zend_class_entry *ce = zend_lookup_class_ex( + cname, NULL, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + if (!ce) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + class_ref = ZEND_CE_TO_REF(ce); + } + if (!class_ref) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_string *fname = SYMBOLIC_MCALL_FNAME(list); + zend_function *func = zend_hash_find_ptr(&class_ref->ce->function_table, fname); + if (!func) { + if (symtype == SYMTYPE_METHOD_CALL && class_ref->ce->__call) { + func = class_ref->ce->__call; + } else if (symtype == SYMTYPE_STATIC_METHOD_CALL && class_ref->ce->__callstatic) { + func = class_ref->ce->__callstatic; + } + } + zend_type t; + if (!func || !func->common.arg_info) { + t = (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } else { + // TODO: scope/visibility? + t = (func->common.arg_info - 1)->type; + } + if (class_ref->ce->num_generic_params) { + t = zend_materialize_generic_types(arena, t, class_ref); + } + if (ZEND_TYPE_IS_INTERSECTION(resolved)) { + inner = zend_type_intersect(arena, inner, t); + } else { + inner = zend_type_union(arena, inner, t); + } + } ZEND_TYPE_FOREACH_END(); + if (ZEND_TYPE_IS_INTERSECTION(SYMBOLIC_MCALL_OBJECT(list))) { + result = zend_type_intersect(arena, result, inner); + } else { + result = zend_type_union(arena, result, inner); + } + } ZEND_TYPE_FOREACH_END(); + + return result; + } + case SYMTYPE_NEW: { + // TODO: generic classes + // TODO: relative class + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_NEW_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + return object; + } + case SYMTYPE_PROP: { + zend_type object = SYMBOLIC_PROP_OBJECT(list); + if (ZEND_TYPE_HAS_PNR(object) && zend_resolve_relative_class( + arena, SYMBOLIC_MCALL_FETCH_TYPE(list), + &object) == FAILURE) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + object = zend_resolve_symbolic_type(arena, object); + // TODO compound types + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_class_reference *class_ref = zend_lookup_class_by_pnr( + ZEND_TYPE_PNR(object), NULL, ZEND_FETCH_CLASS_SILENT); + if (!class_ref) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_string *pname = SYMBOLIC_PROP_PNAME(list); + zend_class_entry *prev_scope = EG(fake_scope); + EG(fake_scope) = class_ref->ce; // TODO + zend_property_info *prop_info = zend_get_property_info( + class_ref->ce, pname, 1); + EG(fake_scope) = prev_scope; + if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) { + if (ZEND_TYPE_IS_SET(prop_info->type)) { + // TODO: materialize is not always required + // (e.g. for $this->prop) + return zend_materialize_generic_types(arena, + prop_info->type, class_ref); + } + } + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + case SYMTYPE_CLASS: { + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_CLASS_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + switch (SYMBOLIC_CCONST_FETCH_TYPE(list)) { + case ZEND_FETCH_CLASS_DEFAULT: + case ZEND_FETCH_CLASS_SELF: + return object; + case ZEND_FETCH_CLASS_PARENT: { + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(object); + zend_string *lcname = SYMBOLIC_CLASS_LCNAME(list); + zend_class_entry *ce = zend_lookup_class_ex(cname, lcname, + ZEND_FETCH_CLASS_SILENT); + if (!ce || !ce->num_parents) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + ce = ce->parents[0]->ce; + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ce->name), 0, 0); + } + case ZEND_FETCH_CLASS_STATIC: + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + default: + ZEND_UNREACHABLE(); + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + } + case SYMTYPE_CLASS_CONSTANT: { + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_CCONST_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(object); + zend_string *lcname = SYMBOLIC_CCONST_LCNAME(list); + // TODO: $this call + // TODO: relative call + // TODO: ::class + // TODO: AST + // TODO: enum + zend_class_entry *ce = zend_lookup_class_ex(cname, lcname, + ZEND_FETCH_CLASS_SILENT); + if (!ce) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + switch (SYMBOLIC_CCONST_FETCH_TYPE(list)) { + case ZEND_FETCH_CLASS_DEFAULT: + break; + case ZEND_FETCH_CLASS_SELF: + break; + case ZEND_FETCH_CLASS_PARENT: { + + if (!ce->num_parents) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + ce = ce->parents[0]->ce; + break; + } + case ZEND_FETCH_CLASS_STATIC: + // TODO: maybe conservatively use highest parent + // declaring the constant, if typed? + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + EMPTY_SWITCH_DEFAULT_CASE(); + } + + zend_string *constant_name = SYMBOLIC_CCONST_CNAME(list); + zval *zv = zend_hash_find_known_hash(CE_CONSTANTS_TABLE(ce), + constant_name); + if (!zv) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_class_constant *constant = Z_PTR_P(zv); + if (ZEND_TYPE_IS_SET(constant->type)) { + return constant->type; + } + return _const_op_type(&constant->value); + } + case SYMTYPE_CONSTANT: { + zval *zv = zend_hash_find_known_hash(EG(zend_constants), + SYMBOLIC_CONST_KEY1(list)); + zend_constant *constant = NULL; + if (zv) { + constant = (zend_constant*)Z_PTR_P(zv); + } else if (SYMBOLIC_CONST_KEY2(list)) { + zv = zend_hash_find_known_hash(EG(zend_constants), SYMBOLIC_CONST_KEY2(list)); + if (zv) { + constant = (zend_constant*)Z_PTR_P(zv); + } + } + + if (!constant) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + return _const_op_type(&constant->value); + } + case SYMTYPE_NOT_NULL: { + type = zend_resolve_symbolic_type(arena, SYMBOLIC_NOT_NULL_OP(list)); + ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; + return type; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } +} diff --git a/Zend/zend_symbolic_inference.h b/Zend/zend_symbolic_inference.h new file mode 100644 index 0000000000000..8a5961977cd68 --- /dev/null +++ b/Zend/zend_symbolic_inference.h @@ -0,0 +1,13 @@ + +#ifndef ZEND_SYMBOLIC_INFERENCE_H +#define ZEND_SYMBOLIC_INFERENCE_H + +#include +#include +#include +#include + +ZEND_API zend_result zend_symbolic_inference(const zend_op_array *op_array, zend_arena **arena); +ZEND_API zend_type zend_resolve_symbolic_type(zend_arena **arena, zend_type type); + +#endif /* ZEND_SYMBOLIC_INFERENCE_H */ diff --git a/Zend/zend_type_tools.c b/Zend/zend_type_tools.c new file mode 100644 index 0000000000000..4061a182bf563 --- /dev/null +++ b/Zend/zend_type_tools.c @@ -0,0 +1,334 @@ + +#include "zend.h" +#include "zend_API.h" +#include "zend_alloc.h" +#include "zend_arena.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_string.h" +#include "zend_types.h" +#include "zend_type_info.h" +#include "zend_bitset.h" + +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); + +// TODO: dedup this function +static void zend_type_list_copy_ctor( + zend_type *const parent_type, + bool use_arena, + bool persistent +) { + const zend_type_list *const old_list = ZEND_TYPE_LIST(*parent_type); + size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); + zend_type_list *new_list = use_arena + ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); + + memcpy(new_list, old_list, size); + ZEND_TYPE_SET_LIST(*parent_type, new_list); + if (use_arena) { + ZEND_TYPE_FULL_MASK(*parent_type) |= _ZEND_TYPE_ARENA_BIT; + } + + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + zend_type_copy_ctor(list_type, use_arena, persistent); + } ZEND_TYPE_LIST_FOREACH_END(); +} + +// TODO: dedup this function +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { + if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type_list_copy_ctor(type, use_arena, persistent); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + // TODO: Duplicate name reference... + zend_string_addref(ZEND_TYPE_PNR_NAME(*type)); + } +} + +#define REALLOC_INTERSECT_LIST() do { \ + if (!new_list) { \ + zend_type_list *old_list = ZEND_TYPE_IS_INTERSECTION(dest) ? ZEND_TYPE_LIST(dest) : NULL; \ + /* Worse case */ \ + size_t num_types = (old_list ? old_list->num_types : 1) \ + + (ZEND_TYPE_IS_INTERSECTION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); \ + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); \ + if (old_list) { \ + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); \ + } else { \ + new_list->types[0] = dest; \ + new_list->num_types = 1; \ + } \ + dest.ptr = new_list; \ + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_INTERSECTION_BIT|_ZEND_TYPE_ARENA_BIT; \ + dest_elems = new_list->types; \ + } \ +} while (0) + +zend_type zend_type_intersect(zend_arena **arena, zend_type dest, zend_type src) +{ + if (ZEND_TYPE_PURE_MASK(dest) || !ZEND_TYPE_IS_COMPLEX(src)) { + return dest; + } + + if (ZEND_TYPE_IS_UNION(dest) || ZEND_TYPE_IS_UNION(src)) { + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH_EX(src, src_elem, ~_ZEND_TYPE_INTERSECTION_BIT) { + zend_type *dest_elems = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + zend_type intersection = zend_type_intersect(arena, *dest_elem, *src_elem); + if (dest_elem->type_mask != intersection.type_mask + || (ZEND_TYPE_IS_COMPLEX(*dest_elem) && dest_elem->ptr != intersection.ptr)) { + bool add = true; + for (uint32_t j = 0; j < i; j++) { + if (zend_type_accepts(&intersection, NULL, &dest_elems[j], NULL, NULL)) { + add = false; + break; + } + } + if (add) { + if (!new_list) { + zend_type_list *old_list = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest) : NULL; + /* Worse case */ + size_t num_types = (old_list ? old_list->num_types : 1) + * (ZEND_TYPE_IS_UNION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); + if (old_list) { + memcpy(new_list, old_list, sizeof(zend_type)*i); + new_list->num_types = i; + } else { + new_list->num_types = 0; + } + dest.ptr = new_list; + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_UNION_BIT|_ZEND_TYPE_ARENA_BIT; + } + new_list->types[new_list->num_types++] = intersection; + } + } + } + } ZEND_TYPE_FOREACH_END(); + + return dest; + } + + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH(src, src_elem) { + bool add = true; + zend_type *dest_elems = ZEND_TYPE_HAS_LIST(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_HAS_LIST(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + if (zend_type_accepts(src_elem, NULL, dest_elem, NULL, NULL)) { + REALLOC_INTERSECT_LIST(); + *dest_elem = *src_elem; + add = false; + } else if (zend_type_accepts(dest_elem, NULL, src_elem, NULL, NULL)) { + add = false; + break; + } + } + if (add) { + REALLOC_INTERSECT_LIST(); + new_list->types[new_list->num_types++] = *src_elem; + } + } ZEND_TYPE_FOREACH_END(); + + return dest; +} + +#define REALLOC_UNION_LIST() do { \ + if (!new_list) { \ + zend_type_list *old_list = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest) : NULL; \ + /* Worse case */ \ + size_t num_types = (old_list ? old_list->num_types : 1) \ + + (ZEND_TYPE_IS_UNION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); \ + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); \ + if (old_list) { \ + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); \ + } else { \ + new_list->types[0] = dest; \ + new_list->num_types = 1; \ + } \ + dest.ptr = new_list; \ + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_UNION_BIT|_ZEND_TYPE_ARENA_BIT; \ + dest_elems = new_list->types; \ + } \ +} while (0) + +zend_type zend_type_union(zend_arena **arena, zend_type dest, zend_type src) +{ + if (!ZEND_TYPE_IS_COMPLEX(dest) && !ZEND_TYPE_IS_COMPLEX(src)) { + ZEND_TYPE_FULL_MASK(dest) |= ZEND_TYPE_PURE_MASK(src); + return dest; + } + + if (!ZEND_TYPE_IS_COMPLEX(dest)) { + ZEND_ASSERT(ZEND_TYPE_IS_COMPLEX(src)); + uint32_t mask = ZEND_TYPE_PURE_MASK(dest); + dest = src; + ZEND_TYPE_FULL_MASK(dest) |= mask; + return dest; + } + + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH_EX(src, src_elem, ~_ZEND_TYPE_INTERSECTION_BIT) { + bool add = true; + zend_type *dest_elems = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + if (zend_type_accepts(src_elem, NULL, dest_elem, NULL, NULL)) { + REALLOC_UNION_LIST(); + *dest_elem = *src_elem; + add = false; + } else if (zend_type_accepts(dest_elem, NULL, src_elem, NULL, NULL)) { + add = false; + break; + } + } + if (add) { + REALLOC_UNION_LIST(); + if (!ZEND_TYPE_IS_COMPLEX(*src_elem)) { + ZEND_TYPE_FULL_MASK(dest) |= ZEND_TYPE_PURE_MASK(*src_elem); + } else { + new_list->types[new_list->num_types++] = *src_elem; + } + } + } ZEND_TYPE_FOREACH_END(); + + if (new_list && new_list->num_types == 0) { + ZEND_TYPE_SET_PTR(dest, NULL); + ZEND_TYPE_FULL_MASK(dest) = ZEND_TYPE_PURE_MASK(dest); + } + + return dest; +} + +zend_type zend_type_normalize_union_in_place(zend_type t) +{ + ZEND_ASSERT(ZEND_TYPE_IS_UNION(t)); + + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type *a = list->types; + zend_type *b; + zend_type *last = a + list->num_types - 1; + + while (a <= last) { +restart: + if (!ZEND_TYPE_IS_COMPLEX(*a)) { + ZEND_TYPE_FULL_MASK(t) |= ZEND_TYPE_PURE_MASK(*a); + // Remove a + if (a != last) { + *a = *last; + } + last--; + continue; + } + b = &list->types[0]; + while (b <= last && b < a) { + if (zend_type_accepts(b, NULL, a, NULL, NULL)) { + // Remove a + if (a != last) { + *a = *last; + last--; + goto restart; + } else { + last--; + break; + } + } else if (zend_type_accepts(a, NULL, b, NULL, NULL)) { + // Remove b + if (b != last) { + *b = *last; + } + if (a == last) { + b++; + } + last--; + continue; + } + b++; + } + a++; + } + + uint32_t num_types = last+1-list->types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(t) &= ~(_ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT | _ZEND_TYPE_ARENA_BIT); + } else if (num_types == 1) { + uint32_t mask = ZEND_TYPE_PURE_MASK(t); + t = list->types[0]; + ZEND_TYPE_FULL_MASK(t) |= mask; + } else { + list->num_types = num_types; + } + + return t; +} + +zend_type zend_type_normalize_intersection_in_place(zend_type t) +{ + ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(t)); + + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type *a = list->types; + zend_type *b; + zend_type *last = a + list->num_types - 1; + + while (a <= last) { +restart: + if (!ZEND_TYPE_IS_COMPLEX(*a)) { + ZEND_TYPE_FULL_MASK(t) |= ZEND_TYPE_PURE_MASK(*a); + // Remove a + if (a != last) { + *a = *last; + } + last--; + continue; + } + b = &list->types[0]; + while (b <= last && b < a) { + if (zend_type_accepts(a, NULL, b, NULL, NULL)) { + // Remove a + if (a != last) { + *a = *last; + last--; + goto restart; + } else { + last--; + break; + } + } else if (zend_type_accepts(b, NULL, a, NULL, NULL)) { + // Remove b + if (b != last) { + *b = *last; + } + if (a == last) { + b++; + } + last--; + continue; + } + b++; + } + a++; + } + + uint32_t num_types = last+1-list->types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(t) &= ~(_ZEND_TYPE_LIST_BIT | _ZEND_TYPE_INTERSECTION_BIT | _ZEND_TYPE_ARENA_BIT); + } else if (num_types == 1) { + uint32_t mask = ZEND_TYPE_PURE_MASK(t); + t = list->types[0]; + ZEND_TYPE_FULL_MASK(t) |= mask; + } else { + list->num_types = num_types; + } + + return t; +} + diff --git a/Zend/zend_type_tools.h b/Zend/zend_type_tools.h new file mode 100644 index 0000000000000..138b8fb139819 --- /dev/null +++ b/Zend/zend_type_tools.h @@ -0,0 +1,14 @@ +#ifndef _ZEND_TYPE_TOOLS_H_ +#define _ZEND_TYPE_TOOLS_H_ + +#include "zend.h" +#include "zend_arena.h" +#include "zend_types.h" + +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); +zend_type zend_type_intersect(zend_arena **arena, zend_type dest, zend_type src); +zend_type zend_type_union(zend_arena **arena, zend_type dest, zend_type src); +zend_type zend_type_normalize_union_in_place(zend_type t); +zend_type zend_type_normalize_intersection_in_place(zend_type t); + +#endif /* _ZEND_TYPE_TOOLS_H_ */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f2f538a2c2c88..340526a1b8501 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,16 +142,19 @@ typedef struct { zend_type types[1] ZEND_ELEMENT_COUNT(num_types); } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 -#define _ZEND_TYPE_MASK ((1u << 25) - 1) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 26 +#define _ZEND_TYPE_MASK ((1u << 26) - 1) + /* Only one of these bits may be set. */ -#define _ZEND_TYPE_NAME_BIT (1u << 24) -// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, -#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 23) -#define _ZEND_TYPE_LIST_BIT (1u << 22) -#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) +#define _ZEND_TYPE_PNR_BIT (1u << 25) +#define _ZEND_TYPE_CLASS_REF_BIT (1u << 24) +#define _ZEND_TYPE_LIST_BIT (1u << 23) /* For BC behaviour with iterable type */ -#define _ZEND_TYPE_ITERABLE_BIT (1u << 21) +#define _ZEND_TYPE_ITERABLE_BIT (1u << 22) +#define _ZEND_TYPE_GENERIC_PARAM_BIT (1u << 21) +#define _ZEND_TYPE_CLASS_MASK \ + (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CLASS_REF_BIT|_ZEND_TYPE_PNR_BIT) +#define _ZEND_TYPE_COMPLEX_MASK (_ZEND_TYPE_CLASS_MASK|_ZEND_TYPE_GENERIC_PARAM_BIT) /* Whether the type list is arena allocated */ #define _ZEND_TYPE_ARENA_BIT (1u << 20) /* Whether the type list is an intersection type */ @@ -163,19 +166,35 @@ typedef struct { /* Must have same value as MAY_BE_NULL */ #define _ZEND_TYPE_NULLABLE_BIT 0x2u +/* Both tag 0 and 1 represent a PNR, as it internally tags with the low bit. */ +#define _ZEND_TYPE_LIST_CLASS_REF_TAG ((uintptr_t) 2) +#define _ZEND_TYPE_LIST_GENERIC_PARAM_TAG ((uintptr_t) 3) +#define _ZEND_TYPE_LIST_TAG_MASK ((uintptr_t) 3) + #define ZEND_TYPE_IS_SET(t) \ (((t).type_mask & _ZEND_TYPE_MASK) != 0) -/* If a type is complex it means it's either a list with a union or intersection, - * or the void pointer is a class name */ +#define ZEND_TYPE_HAS_CLASS(t) \ + ((((t).type_mask) & _ZEND_TYPE_CLASS_MASK) != 0) + #define ZEND_TYPE_IS_COMPLEX(t) \ - ((((t).type_mask) & _ZEND_TYPE_KIND_MASK) != 0) + ((((t).type_mask) & _ZEND_TYPE_COMPLEX_MASK) != 0) -#define ZEND_TYPE_HAS_NAME(t) \ - ((((t).type_mask) & _ZEND_TYPE_NAME_BIT) != 0) +#define ZEND_TYPE_HAS_CLASS_REF(t) \ + ((((t).type_mask) & _ZEND_TYPE_CLASS_REF_BIT) != 0) +#define ZEND_TYPE_HAS_PNR(t) \ + ((((t).type_mask) & _ZEND_TYPE_PNR_BIT) != 0) + +/* Only used for arginfo, reuse bit. */ #define ZEND_TYPE_HAS_LITERAL_NAME(t) \ - ((((t).type_mask) & _ZEND_TYPE_LITERAL_NAME_BIT) != 0) + ZEND_TYPE_HAS_PNR(t) + +#define ZEND_TYPE_HAS_SIMPLE_PNR(t) \ + (ZEND_TYPE_HAS_PNR(t) && ZEND_PNR_IS_SIMPLE(ZEND_TYPE_PNR(t))) + +#define ZEND_TYPE_HAS_COMPLEX_PNR(t) \ + (ZEND_TYPE_HAS_PNR(t) && ZEND_PNR_IS_COMPLEX(ZEND_TYPE_PNR(t))) #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) @@ -189,23 +208,41 @@ typedef struct { #define ZEND_TYPE_IS_UNION(t) \ ((((t).type_mask) & _ZEND_TYPE_UNION_BIT) != 0) +#define ZEND_TYPE_HAS_GENERIC_PARAM(t) \ + ((((t).type_mask) & _ZEND_TYPE_GENERIC_PARAM_BIT) != 0) + #define ZEND_TYPE_USES_ARENA(t) \ ((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0) -#define ZEND_TYPE_IS_ONLY_MASK(t) \ - (ZEND_TYPE_IS_SET(t) && (t).ptr == NULL) +#define ZEND_TYPE_PNR(t) \ + ((zend_packed_name_reference) (t).ptr) + +#define ZEND_TYPE_PNR_SIMPLE_NAME(t) \ + ZEND_PNR_SIMPLE_GET_NAME(ZEND_TYPE_PNR(t)) + +#define ZEND_TYPE_PNR_NAME(t) \ + ZEND_PNR_GET_NAME(ZEND_TYPE_PNR(t)) -#define ZEND_TYPE_NAME(t) \ - ((zend_string *) (t).ptr) +#define ZEND_TYPE_PNR_KEY(t) \ + ZEND_PNR_GET_KEY(ZEND_TYPE_PNR(t)) #define ZEND_TYPE_LITERAL_NAME(t) \ ((const char *) (t).ptr) +#define ZEND_TYPE_CLASS_REF(t) \ + ((zend_class_reference *) (t).ptr) + +#define ZEND_TYPE_PNR(t) \ + ((zend_packed_name_reference) (t).ptr) + #define ZEND_TYPE_LIST(t) \ ((zend_type_list *) (t).ptr) +#define ZEND_TYPE_GENERIC_PARAM_ID(t) \ + ((uint32_t) (uintptr_t) (t).ptr) + #define ZEND_TYPE_LIST_SIZE(num_types) \ - (sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type)) + (sizeof(zend_type_list) + ((ssize_t)(num_types) - 1) * sizeof(zend_type)) /* This iterates over a zend_type_list. */ #define ZEND_TYPE_LIST_FOREACH(list, type_ptr) do { \ @@ -218,6 +255,17 @@ typedef struct { } \ } while (0) +#define ZEND_TYPE_LIST_FOREACH_CONST(list, type_ptr) do { \ + const zend_type *_list = (list)->types; \ + const zend_type *_end = _list + (list)->num_types; \ + for (; _list < _end; _list++) { \ + type_ptr = _list; + +#define ZEND_TYPE_LIST_FOREACH_CONST_END() \ + } \ +} while (0) + + /* This iterates over any zend_type. If it's a type list, all list elements will * be visited. If it's a single type, only the single type is visited. */ #define ZEND_TYPE_FOREACH(type, type_ptr) do { \ @@ -237,15 +285,41 @@ typedef struct { } while (++_cur < _end); \ } while (0) +/* Same as ZEND_TYPE_FOREACH, but visit the list only if + * (type.type_mask & mask) is non zero. */ +#define ZEND_TYPE_FOREACH_EX(type, type_ptr, mask) do { \ + zend_type *_cur, *_end; \ + uint32_t _mask = (mask); \ + zend_type _type = (type); \ + if (ZEND_TYPE_HAS_LIST(_type) && !(ZEND_TYPE_FULL_MASK(_type) & ~_mask)) { \ + zend_type_list *_list = ZEND_TYPE_LIST(_type); \ + _cur = _list->types; \ + _end = _cur + _list->num_types; \ + } else { \ + _cur = &_type; \ + _end = _cur + 1; \ + } \ + do { \ + type_ptr = _cur; + #define ZEND_TYPE_SET_PTR(t, _ptr) \ ((t).ptr = (_ptr)) +#define ZEND_TYPE_SET_PNR(t, _pnr) \ + ZEND_TYPE_SET_PTR((t), (void *) (uintptr_t) (_pnr)) + +#define ZEND_TYPE_SET_GENERIC_PARAM_ID(t, _id) \ + ((t).ptr = (void *) (uintptr_t) (_id)) + #define ZEND_TYPE_SET_PTR_AND_KIND(t, _ptr, kind_bit) do { \ (t).ptr = (_ptr); \ - (t).type_mask &= ~_ZEND_TYPE_KIND_MASK; \ + (t).type_mask &= ~_ZEND_TYPE_COMPLEX_MASK; \ (t).type_mask |= (kind_bit); \ } while (0) +#define ZEND_TYPE_SET_CLASS_REF(t, ce_ref) \ + ZEND_TYPE_SET_PTR_AND_KIND(t, ce_ref, _ZEND_TYPE_CLASS_REF_BIT) + #define ZEND_TYPE_SET_LIST(t, list) \ ZEND_TYPE_SET_PTR_AND_KIND(t, list, _ZEND_TYPE_LIST_BIT) @@ -300,17 +374,109 @@ typedef struct { #define ZEND_TYPE_INIT_INTERSECTION(ptr, extra_flags) \ _ZEND_TYPE_PREFIX { (void *) (ptr), (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_INTERSECTION_BIT) | (extra_flags) } -#define ZEND_TYPE_INIT_CLASS(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) +#define ZEND_TYPE_INIT_CLASS_REF(ce_ref, allow_null, extra_flags) \ + ZEND_TYPE_INIT_PTR(ce_ref, _ZEND_TYPE_CLASS_REF_BIT, allow_null, extra_flags) + +#define ZEND_TYPE_INIT_PNR(pnr, allow_null, extra_flags) \ + ZEND_TYPE_INIT_PTR((void *) pnr, _ZEND_TYPE_PNR_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_PNR_BIT | (type_mask)) #define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_LITERAL_NAME_BIT, allow_null, extra_flags) + ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_PNR_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_LITERAL_NAME_BIT | (type_mask))) + ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_PNR_BIT | (type_mask))) + +#define ZEND_TYPE_INIT_GENERIC_PARAM(param_id, extra_flags) \ + { (void *) (uintptr_t) param_id, _ZEND_TYPE_GENERIC_PARAM_BIT | (extra_flags) } + +/* Represents a class entry together with bound generic type arguments. Normal class entries + * are prefixed with a compatible header, such that any class can be cheaply reinterpreted as + * a zero-argument reference to itself. */ +typedef struct _zend_class_reference { + zend_class_entry *ce; + zend_string *key; + zend_type_list args; +} zend_class_reference; + +/* The same, but for unresolved cases where we only have the name available. + * This should be structurally the same zend_class_reference to permit in-place resolution. */ +typedef struct _zend_name_reference { + zend_string *name; + zend_string *key; + zend_type_list args; +} zend_name_reference; + +#define ZEND_CLASS_ENTRY_HEADER_SIZE (3 * sizeof(void*)) + +#define ZEND_CE_TO_REF(ce) \ + ((zend_class_reference *) ((char *) (ce) - ZEND_CLASS_ENTRY_HEADER_SIZE)) + +#define ZEND_REF_IS_TRIVIAL(ref) \ + ((ref)->ce == (zend_class_entry *) ((char *) (ref) + ZEND_CLASS_ENTRY_HEADER_SIZE)) + +#define ZEND_CLASS_REF_SIZE(num_types) \ + (sizeof(zend_class_reference) - sizeof(zend_type) + (num_types) * sizeof(zend_type)) + +/* A packed name reference is used to either store a simple name string, + * or a full zend_name_reference structure. The low bit is reserved for the tag. */ +typedef uintptr_t zend_packed_name_reference; + +// TODO: Make this non-static? +static const zend_type_list zend_empty_type_list = {0}; + +#define ZEND_PNR_IS_COMPLEX(pnr) ((pnr) & 1) +#define ZEND_PNR_IS_SIMPLE(pnr) !ZEND_PNR_IS_COMPLEX(pnr) +#define ZEND_PNR_SIMPLE_GET_NAME(pnr) ((zend_string *) (pnr)) +#define ZEND_PNR_COMPLEX_GET_REF(pnr) ((zend_name_reference *) (pnr - 1)) +#define ZEND_PNR_COMPLEX_GET_NAME(pnr) ZEND_PNR_COMPLEX_GET_REF(pnr)->name +#define ZEND_PNR_COMPLEX_GET_KEY(pnr) ZEND_PNR_COMPLEX_GET_REF(pnr)->key + +#define ZEND_PNR_ENCODE_NAME(name) ((uintptr_t) (name)) +#define ZEND_PNR_ENCODE_REF(ref) ((uintptr_t) (ref) + 1) + +#define ZEND_PNR_GET_NAME(pnr) \ + (ZEND_PNR_IS_COMPLEX(pnr) ? ZEND_PNR_COMPLEX_GET_NAME(pnr) : ZEND_PNR_SIMPLE_GET_NAME(pnr)) + +#define ZEND_PNR_GET_ARGS(pnr) \ + (ZEND_PNR_IS_COMPLEX(pnr) ? &ZEND_PNR_COMPLEX_GET_REF(pnr)->args : &zend_empty_type_list) + +#define ZEND_PNR_UNPACK(pnr, name_var, args_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (args_var) = &__ref->args; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (args_var) = &zend_empty_type_list; \ + } \ +} while (0) + +#define ZEND_PNR_UNPACK_NAME_KEY(pnr, name_var, key_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (key_var) = __ref->key; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (key_var) = NULL; \ + } \ +} while (0) + +#define ZEND_PNR_UNPACK_NAME_KEY_ARGS(pnr, name_var, key_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (key_var) = __ref->key; \ + (args_var) = &__ref->args; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (key_var) = NULL; \ + (args_var) = &zend_empty_type_list; \ + } \ +} while (0) typedef union _zend_value { zend_long lval; /* long value */ @@ -325,6 +491,8 @@ typedef union _zend_value { zval *zv; void *ptr; zend_class_entry *ce; + zend_class_reference *cr; + zend_packed_name_reference pnr; zend_function *func; struct { uint32_t w1; @@ -556,7 +724,7 @@ typedef struct _HashTableIterator { struct _zend_object { zend_refcounted_h gc; uint32_t handle; // TODO: may be removed ??? - zend_class_entry *ce; + zend_class_reference *cr; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; @@ -622,7 +790,8 @@ struct _zend_ast_ref { #define IS_INDIRECT 12 #define IS_PTR 13 #define IS_ALIAS_PTR 14 -#define _IS_ERROR 15 +#define IS_PNR 15 /* TODO: reuse same bit as _ZEND_TYPE_PNR_BIT? */ +#define _IS_ERROR 16 /* used for casts */ #define _IS_BOOL 18 @@ -833,6 +1002,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define OBJ_FLAGS(obj) GC_FLAGS(obj) +#define OBJ_CE(obj) (obj)->cr->ce +#define OBJ_NAME(obj) OBJ_CE(obj)->name + /* Fast class cache */ #define ZSTR_HAS_CE_CACHE(s) (GC_FLAGS(s) & IS_STR_CLASS_NAME_MAP_PTR) #define ZSTR_GET_CE_CACHE(s) ZSTR_GET_CE_CACHE_EX(s, 1) @@ -851,10 +1023,10 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { } while (0) #define GET_CE_CACHE(ce_cache) \ - (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) + (*(zend_class_reference **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) #define SET_CE_CACHE(ce_cache, ce) do { \ - *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \ + *((zend_class_reference **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \ } while (0) /* Recursion protection macros must be used only for arrays and objects */ @@ -994,9 +1166,12 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_OBJ_HANDLE(zval) (Z_OBJ((zval)))->handle #define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*(zval_p)) -#define Z_OBJCE(zval) (Z_OBJ(zval)->ce) +#define Z_OBJCE(zval) (Z_OBJ(zval)->cr->ce) #define Z_OBJCE_P(zval_p) Z_OBJCE(*(zval_p)) +#define Z_OBJCR(zval) (Z_OBJ(zval)->cr) +#define Z_OBJCR_P(zval_p) Z_OBJCR(*(zval_p)) + #define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(Z_OBJ(zval)) #define Z_OBJPROP_P(zval_p) Z_OBJPROP(*(zval_p)) @@ -1032,6 +1207,12 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_CE(zval) (zval).value.ce #define Z_CE_P(zval_p) Z_CE(*(zval_p)) +#define Z_CR(zval) (zval).value.cr +#define Z_CR_P(zval_p) Z_CR(*(zval_p)) + +#define Z_PNR(zval) (zval).value.pnr +#define Z_PNR_P(zval_p) Z_PNR(*(zval_p)) + #define Z_FUNC(zval) (zval).value.func #define Z_FUNC_P(zval_p) Z_FUNC(*(zval_p)) @@ -1249,6 +1430,11 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { Z_TYPE_INFO_P(z) = IS_PTR; \ } while (0) +#define ZVAL_PNR(z, c) do { \ + Z_PNR_P(z) = (c); \ + Z_TYPE_INFO_P(z) = IS_PNR; \ + } while (0) + #define ZVAL_ALIAS_PTR(z, p) do { \ Z_PTR_P(z) = (p); \ Z_TYPE_INFO_P(z) = IS_ALIAS_PTR; \ diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c index 27e09d7db22b1..4fa8c9265784e 100644 --- a/Zend/zend_variables.c +++ b/Zend/zend_variables.c @@ -48,7 +48,8 @@ static const zend_rc_dtor_func_t zend_rc_dtor_func[] = { [IS_OBJECT] = (zend_rc_dtor_func_t)zend_objects_store_del, [IS_RESOURCE] = (zend_rc_dtor_func_t)zend_list_free, [IS_REFERENCE] = (zend_rc_dtor_func_t)zend_reference_destroy, - [IS_CONSTANT_AST] = (zend_rc_dtor_func_t)zend_ast_ref_destroy + [IS_CONSTANT_AST] = (zend_rc_dtor_func_t)zend_ast_ref_destroy, + [IS_PNR] = (zend_rc_dtor_func_t)zend_empty_destroy, }; ZEND_API void ZEND_FASTCALL rc_dtor_func(zend_refcounted *p) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f731653a24813..0eca57c0e141c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -23,6 +23,7 @@ * php zend_vm_gen.php */ +#include "zend_types.h" ZEND_VM_HELPER(zend_add_helper, ANY, ANY, zval *op_1, zval *op_2) { USE_OPLINE @@ -1076,7 +1077,8 @@ ZEND_VM_C_LABEL(assign_op_object): } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -1133,7 +1135,8 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_STATIC_PROP_OP, ANY, ANY, OP) if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + NULL, prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(prop, prop, value OPLINE_CC); } @@ -1331,9 +1334,9 @@ ZEND_VM_C_LABEL(pre_incdec_object): if (OP2_TYPE == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -1402,10 +1405,10 @@ ZEND_VM_C_LABEL(post_incdec_object): if (OP2_TYPE == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -1439,7 +1442,7 @@ ZEND_VM_HANDLER(38, ZEND_PRE_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT) HANDLE_EXCEPTION(); } - zend_pre_incdec_property_zval(prop, + zend_pre_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -1465,7 +1468,7 @@ ZEND_VM_HANDLER(40, ZEND_POST_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT) HANDLE_EXCEPTION(); } - zend_post_incdec_property_zval(prop, + zend_post_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -2071,7 +2074,7 @@ ZEND_VM_HOT_OBJ_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMPVAR|UNUSED|THIS|CV, CONST if (OP2_TYPE == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -2133,7 +2136,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy): * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -2141,7 +2144,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy): if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -2236,7 +2239,7 @@ ZEND_VM_COLD_CONST_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, C if (OP2_TYPE == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -2407,7 +2410,7 @@ ZEND_VM_HANDLER(24, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_ ZEND_VM_C_LABEL(assign_object): zobj = Z_OBJ_P(object); if (OP2_TYPE == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -2418,7 +2421,7 @@ ZEND_VM_C_LABEL(assign_object): zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); ZEND_VM_C_GOTO(free_and_exit_assign_obj); } else { ZEND_VM_C_LABEL(fast_assign_obj): @@ -2444,7 +2447,7 @@ ZEND_VM_C_LABEL(fast_assign_obj): } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -2534,7 +2537,7 @@ ZEND_VM_HANDLER(25, ZEND_ASSIGN_STATIC_PROP, ANY, ANY, CACHE_SLOT, SPEC(OP_DATA= value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); FREE_OP_DATA(); } else { value = zend_assign_to_variable_ex(prop, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES(), &garbage); @@ -2832,7 +2835,8 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_STATIC_PROP_REF, ANY, ANY, CACHE_SLOT|SRC) prop = &EG(uninitialized_zval); } } else if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - prop = zend_assign_to_typed_property_reference(prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); + prop = zend_assign_to_typed_property_reference( + NULL, prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(prop, value_ptr, &garbage); } @@ -3550,7 +3554,7 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (OP2_TYPE == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -3566,7 +3570,7 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((OP2_TYPE == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } FREE_OP2(); if ((OP1_TYPE & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -3714,7 +3718,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -4316,7 +4320,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -4654,7 +4658,7 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT) CACHE_PTR(opline->extended_value & ~ZEND_LAST_CATCH, catch_ce); } - ce = EG(exception)->ce; + ce = OBJ_CE(EG(exception)); #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_CAUGHT_ENABLED()) { @@ -5765,32 +5769,70 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (OP1_TYPE == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); - if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference( + ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + types, Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJCR(EX(This)) : NULL); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + efree(ref); /* members are owned by class_ref */ + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); + } + } else { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); } } else if (OP1_TYPE == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -5864,7 +5906,7 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -6729,7 +6771,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR) ZEND_VM_NEXT_OPCODE(); } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -8751,14 +8793,14 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, CV|TMPVAR|UNUSED|CLASS_FETCH, ANY) ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -9323,10 +9365,10 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED) } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index fbec492f7d70a..9f8c5b7790771 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -793,7 +793,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_OP_SPEC_HAN if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + NULL, prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(prop, prop, value OPLINE_CC); } @@ -821,7 +822,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_STATIC_PROP_SPEC_HANDL HANDLE_EXCEPTION(); } - zend_pre_incdec_property_zval(prop, + zend_pre_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -841,7 +842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND HANDLE_EXCEPTION(); } - zend_post_incdec_property_zval(prop, + zend_post_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -949,7 +950,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = RT_CONSTANT((opline+1), (opline+1)->op1); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); } else { value = zend_assign_to_variable_ex(prop, value, IS_CONST, EX_USES_STRICT_TYPES(), &garbage); @@ -985,7 +986,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } else { value = zend_assign_to_variable_ex(prop, value, IS_TMP_VAR, EX_USES_STRICT_TYPES(), &garbage); @@ -1021,7 +1022,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } else { value = zend_assign_to_variable_ex(prop, value, IS_VAR, EX_USES_STRICT_TYPES(), &garbage); @@ -1057,7 +1058,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_cv_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); } else { value = zend_assign_to_variable_ex(prop, value, IS_CV, EX_USES_STRICT_TYPES(), &garbage); @@ -1097,7 +1098,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_REF_SPEC_HA prop = &EG(uninitialized_zval); } } else if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - prop = zend_assign_to_typed_property_reference(prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); + prop = zend_assign_to_typed_property_reference( + NULL, prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(prop, value_ptr, &garbage); } @@ -4748,7 +4750,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_ CACHE_PTR(opline->extended_value & ~ZEND_LAST_CATCH, catch_ce); } - ce = EG(exception)->ce; + ce = OBJ_CE(EG(exception)); #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_CAUGHT_ENABLED()) { @@ -4862,7 +4864,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -5168,7 +5170,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER( ZEND_VM_NEXT_OPCODE(); } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -6424,7 +6426,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -6486,7 +6488,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -6494,7 +6496,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -6551,7 +6553,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -6866,7 +6868,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -6882,7 +6884,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -7030,7 +7032,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -8944,7 +8946,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -9006,7 +9008,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -9014,7 +9016,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -9071,7 +9073,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -9386,7 +9388,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -9402,7 +9404,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -9550,7 +9552,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -10303,7 +10305,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -10402,7 +10404,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -10518,32 +10520,68 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_CONST == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); - if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + efree(ref); /* members are owned by class_ref */ + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); + } + } else { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); } } else if (IS_CONST == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -10944,10 +10982,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_ } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -11331,7 +11369,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -11393,7 +11431,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -11401,7 +11439,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -11458,7 +11496,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -11773,7 +11811,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -11789,7 +11827,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -11937,7 +11975,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -14770,7 +14808,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -15126,14 +15164,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_TMPVAR_H ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -15713,7 +15751,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -15775,7 +15813,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -15783,7 +15821,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -15840,7 +15878,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -16126,7 +16164,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -16142,7 +16180,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -17158,7 +17196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -17220,7 +17258,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -17228,7 +17266,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -17285,7 +17323,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -17571,7 +17609,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -17587,7 +17625,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -18221,10 +18259,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -18518,7 +18556,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -18580,7 +18618,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -18588,7 +18626,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -18645,7 +18683,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HA if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -18931,7 +18969,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -18947,7 +18985,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -19535,7 +19573,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE ZEND_VM_NEXT_OPCODE(); } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -20888,7 +20926,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -22205,7 +22243,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE ZEND_VM_NEXT_OPCODE(); } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -22928,7 +22966,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CONST_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -23135,9 +23174,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HAN if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -23200,10 +23239,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HA if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -23400,7 +23439,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23411,7 +23450,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23437,7 +23476,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23534,7 +23573,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23545,7 +23584,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23571,7 +23610,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23668,7 +23707,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23679,7 +23718,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23705,7 +23744,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23802,7 +23841,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23813,7 +23852,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23839,7 +23878,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -24746,7 +24785,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -25849,7 +25888,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_ } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -26058,9 +26098,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -26124,10 +26164,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_H if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -26326,7 +26366,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26337,7 +26377,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26363,7 +26403,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26460,7 +26500,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26471,7 +26511,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26497,7 +26537,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26594,7 +26634,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26605,7 +26645,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26631,7 +26671,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26728,7 +26768,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26739,7 +26779,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26765,7 +26805,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -27614,7 +27654,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -29128,7 +29168,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -29227,7 +29267,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -29668,32 +29708,68 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_VAR == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); - if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + efree(ref); /* members are owned by class_ref */ + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); + } + } else { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); } } else if (IS_VAR == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -30146,7 +30222,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HAND } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -30353,9 +30430,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLE if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -30418,10 +30495,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDL if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -30618,7 +30695,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30629,7 +30706,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -30655,7 +30732,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -30752,7 +30829,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30763,7 +30840,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -30789,7 +30866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -30886,7 +30963,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30897,7 +30974,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -30923,7 +31000,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -31020,7 +31097,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -31031,7 +31108,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -31057,7 +31134,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -32002,7 +32079,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -32589,7 +32666,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -32657,14 +32734,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_H ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -32750,7 +32827,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONS } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -32827,9 +32905,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_ if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -32892,10 +32970,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -32945,7 +33023,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -33007,7 +33085,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -33015,7 +33093,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -33115,7 +33193,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -33251,7 +33329,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33262,7 +33340,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33288,7 +33366,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33385,7 +33463,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33396,7 +33474,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33422,7 +33500,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33519,7 +33597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33530,7 +33608,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33556,7 +33634,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33653,7 +33731,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33664,7 +33742,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33690,7 +33768,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33995,7 +34073,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -34011,7 +34089,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -34159,7 +34237,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -34807,7 +34885,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_TMPV } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -34884,9 +34963,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -34950,10 +35029,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -35004,7 +35083,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -35066,7 +35145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -35074,7 +35153,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -35169,7 +35248,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -35305,7 +35384,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35316,7 +35395,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35342,7 +35421,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35439,7 +35518,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35450,7 +35529,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35476,7 +35555,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35573,7 +35652,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35584,7 +35663,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35610,7 +35689,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35707,7 +35786,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35718,7 +35797,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35744,7 +35823,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -36050,7 +36129,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -36066,7 +36145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -36214,7 +36293,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -36629,7 +36708,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -36728,7 +36807,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -36827,32 +36906,68 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_UNUSED == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); - if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + efree(ref); /* members are owned by class_ref */ + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); + } + } else { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); } } else if (IS_UNUSED == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -37305,7 +37420,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -37382,9 +37498,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HAN if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -37447,10 +37563,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HA if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -37500,7 +37616,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -37562,7 +37678,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -37570,7 +37686,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -37665,7 +37781,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HA if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -37801,7 +37917,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -37812,7 +37928,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -37838,7 +37954,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -37935,7 +38051,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -37946,7 +38062,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -37972,7 +38088,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38069,7 +38185,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -38080,7 +38196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -38106,7 +38222,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38203,7 +38319,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -38214,7 +38330,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -38240,7 +38356,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38545,7 +38661,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -38561,7 +38677,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -38709,7 +38825,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -39713,7 +39829,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -39936,7 +40052,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN ZEND_VM_NEXT_OPCODE(); } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -40484,14 +40600,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_CV_HANDL ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -41333,7 +41449,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CONST_HA } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -41540,9 +41657,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HAND if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -41605,10 +41722,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HAN if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -41770,7 +41887,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -41832,7 +41949,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -41840,7 +41957,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -41940,7 +42057,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HAN if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -42076,7 +42193,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42087,7 +42204,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42113,7 +42230,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42210,7 +42327,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42221,7 +42338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42247,7 +42364,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42344,7 +42461,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42355,7 +42472,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42381,7 +42498,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42478,7 +42595,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42489,7 +42606,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42515,7 +42632,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -43536,7 +43653,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -43552,7 +43669,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -45173,7 +45290,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_TMPVAR_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -45382,9 +45500,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HAN if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -45448,10 +45566,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -45614,7 +45732,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -45676,7 +45794,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -45684,7 +45802,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -45779,7 +45897,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -45915,7 +46033,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -45926,7 +46044,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -45952,7 +46070,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46049,7 +46167,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46060,7 +46178,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46086,7 +46204,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46183,7 +46301,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46194,7 +46312,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46220,7 +46338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46317,7 +46435,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46328,7 +46446,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46354,7 +46472,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -47317,7 +47435,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -47333,7 +47451,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -49142,7 +49260,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -49861,10 +49979,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -50568,7 +50686,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CV_HANDL } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -50775,9 +50894,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -50840,10 +50959,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLE if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -51005,7 +51124,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -51067,7 +51186,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -51075,7 +51194,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -51170,7 +51289,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLE if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -51306,7 +51425,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51317,7 +51436,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51343,7 +51462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51440,7 +51559,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51451,7 +51570,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51477,7 +51596,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51574,7 +51693,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51585,7 +51704,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51611,7 +51730,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51708,7 +51827,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51719,7 +51838,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51745,7 +51864,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -52804,7 +52923,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -52820,7 +52939,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 442b19fb62adb..26e9d201d79f7 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -341,7 +341,7 @@ static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int if (zv == NULL) { if (type != BP_VAR_IS) { zend_throw_error(NULL, - "Object %s#%d not contained in WeakMap", ZSTR_VAL(obj_addr->ce->name), obj_addr->handle); + "Object %s#%d not contained in WeakMap", ZSTR_VAL(OBJ_NAME(obj_addr)), obj_addr->handle); return NULL; } return NULL; diff --git a/build/gen_stub.php b/build/gen_stub.php index d597d30a72f3f..02c352987aa7b 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2294,7 +2294,7 @@ protected function getTypeCode(string $variableLikeName, string &$code): string foreach ($arginfoType->classTypes as $k => $classType) { $escapedClassName = $classType->toEscapedName(); - $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$escapedClassName}, 0, 0);\n"; + $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST({$variableLikeType}_{$variableLikeName}_class_{$escapedClassName}, 0, 0);\n"; } $typeMaskCode = $this->type->toArginfoType()->toTypeMask(); @@ -2310,7 +2310,7 @@ protected function getTypeCode(string $variableLikeName, string &$code): string $varEscapedClassName = $arginfoType->classTypes[0]->toVarEscapedName(); $code .= "\tzend_string *{$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName} = zend_string_init(\"{$escapedClassName}\", sizeof(\"{$escapedClassName}\")-1, 1);\n"; - $typeCode = "(zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName}, 0, " . $arginfoType->toTypeMask() . ")"; + $typeCode = "(zend_type) ZEND_TYPE_INIT_CLASS_CONST({$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName}, 0, " . $arginfoType->toTypeMask() . ")"; } } else { $typeCode = "(zend_type) ZEND_TYPE_INIT_MASK(" . $arginfoType->toTypeMask() . ")"; diff --git a/configure.ac b/configure.ac index ed765780448e1..b4027157e8f24 100644 --- a/configure.ac +++ b/configure.ac @@ -1746,6 +1746,8 @@ PHP_ADD_SOURCES(Zend, \ zend_observer.c zend_system_id.c zend_enum.c zend_fibers.c zend_atomic.c \ zend_max_execution_timer.c \ zend_hrtime.c \ + zend_type_tools.c \ + zend_symbolic_inference.c \ Optimizer/zend_optimizer.c \ Optimizer/pass1.c \ Optimizer/pass3.c \ @@ -1767,6 +1769,8 @@ PHP_ADD_SOURCES(Zend, \ Optimizer/escape_analysis.c \ Optimizer/compact_vars.c \ Optimizer/zend_dump.c \ + Optimizer/zend_symbolic_inference.c \ + Optimizer/zend_scc.c \ , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_BUILD_DIR(main main/streams) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 02236d6676ea7..602abd6d78719 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -314,10 +314,7 @@ static void date_throw_uninitialized_error(zend_class_entry *ce) if (ce->type == ZEND_INTERNAL_CLASS) { zend_throw_error(date_ce_date_object_error, "Object of type %s has not been correctly initialized by calling parent::__construct() in its constructor", ZSTR_VAL(ce->name)); } else { - zend_class_entry *ce_ptr = ce; - while (ce_ptr && ce_ptr->parent && ce_ptr->type == ZEND_USER_CLASS) { - ce_ptr = ce_ptr->parent; - } + zend_class_entry *ce_ptr = zend_class_entry_get_root(ce); if (ce_ptr->type != ZEND_INTERNAL_CLASS) { zend_throw_error(date_ce_date_object_error, "Object of type %s not been correctly initialized by calling parent::__construct() in its constructor", ZSTR_VAL(ce->name)); } @@ -603,14 +600,14 @@ static void update_property(zend_object *object, zend_string *key, zval *prop_va zend_string_release_ex(cname, 0); } else { // protected - zend_update_property(object->ce, object, prop_name, prop_name_len, prop_val); + zend_update_property(OBJ_CE(object), object, prop_name, prop_name_len, prop_val); } } return; } // public - zend_update_property(object->ce, object, ZSTR_VAL(key), ZSTR_LEN(key), prop_val); + zend_update_property(OBJ_CE(object), object, ZSTR_VAL(key), ZSTR_LEN(key), prop_val); } /* }}} */ @@ -1596,8 +1593,8 @@ static zend_class_entry *get_base_date_class(zend_class_entry *start_ce) { zend_class_entry *tmp = start_ce; - while (tmp != date_ce_date && tmp != date_ce_immutable && tmp->parent) { - tmp = tmp->parent; + while (tmp != date_ce_date && tmp != date_ce_immutable && tmp->num_parents) { + tmp = tmp->parents[0]->ce; } return tmp; @@ -1861,7 +1858,7 @@ static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */ static zend_object *date_object_clone_date(zend_object *this_ptr) /* {{{ */ { php_date_obj *old_obj = php_date_obj_from_obj(this_ptr); - php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(old_obj->std.ce)); + php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); if (!old_obj->time) { @@ -2001,7 +1998,7 @@ static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_timezone(zend_object *this_ptr) /* {{{ */ { php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr); - php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(old_obj->std.ce)); + php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); if (!old_obj->initialized) { @@ -2165,7 +2162,7 @@ static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */ { php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr); - php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce)); + php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); new_obj->civil_or_wall = old_obj->civil_or_wall; @@ -2256,7 +2253,7 @@ static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ static zend_object *date_object_clone_period(zend_object *this_ptr) /* {{{ */ { php_period_obj *old_obj = php_period_obj_from_obj(this_ptr); - php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(old_obj->std.ce)); + php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); new_obj->initialized = old_obj->initialized; diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 027bec67cb08d..f71f34a6dc8ea 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1104,28 +1104,28 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry ZVAL_UNDEF(&property_start_default_value); zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1); zend_string *property_start_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_start_name); zval property_current_default_value; ZVAL_UNDEF(&property_current_default_value); zend_string *property_current_name = zend_string_init("current", sizeof("current") - 1, 1); zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_current_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_current_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_current_name); zval property_end_default_value; ZVAL_UNDEF(&property_end_default_value); zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1); zend_string *property_end_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_end_name); zval property_interval_default_value; ZVAL_UNDEF(&property_interval_default_value); zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1); zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1); - zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_interval_class_DateInterval, 0, MAY_BE_NULL)); zend_string_release(property_interval_name); zval property_recurrences_default_value; diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 632da61c59cd2..6f93c24b853d5 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -342,15 +342,15 @@ zval *dom_write_property(zend_object *object, zend_string *name, zval *value, vo if (hnd) { if (!hnd->write_func) { - zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(OBJ_NAME(object)), ZSTR_VAL(name)); return &EG(error_zval); } - zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true); + zend_property_info *prop = zend_get_property_info(OBJ_CE(object), name, /* silent */ true); if (prop && ZEND_TYPE_IS_SET(prop->type)) { zval tmp; ZVAL_COPY(&tmp, value); - if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { + if (!zend_verify_property_type(object, prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { zval_ptr_dtor(&tmp); return &EG(error_zval); } @@ -510,11 +510,11 @@ static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr ori static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ { dom_object *intern = php_dom_obj_from_obj(zobject); - dom_object *clone = dom_objects_set_class(intern->std.ce); + dom_object *clone = dom_objects_set_class(OBJ_CE(&intern->std)); clone->std.handlers = dom_get_obj_handlers(); - if (instanceof_function(intern->std.ce, dom_node_class_entry)) { + if (instanceof_function(OBJ_CE(&intern->std), dom_node_class_entry)) { xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern); if (node != NULL) { xmlNodePtr cloned_node = xmlDocCopyNode(node, node->doc, 1); @@ -534,7 +534,7 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); - zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce); + zend_object *clone = dom_objects_namespace_node_new(OBJ_CE(&intern->dom.std)); dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone); xmlNodePtr original_node = dom_object_get_node(&intern->dom); @@ -1087,8 +1087,8 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern) { zend_class_entry *base_class = class_type; - while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) { - base_class = base_class->parent; + while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->num_parents != 0) { + base_class = base_class->parents[0]->ce; } intern->prop_handler = zend_hash_find_ptr(&classes, base_class->name); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index bb778e7ab9d41..c85d59f58a383 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -194,7 +194,7 @@ void dom_mark_namespaces_for_copy_based_on_copy(xmlNodePtr copy, const xmlNode * #define DOM_GET_INTERN(__id, __intern) { \ __intern = Z_DOMOBJ_P(__id); \ if (UNEXPECTED(__intern->ptr == NULL)) { \ - zend_throw_error(NULL, "Couldn't fetch %s", ZSTR_VAL(__intern->std.ce->name));\ + zend_throw_error(NULL, "Couldn't fetch %s", ZSTR_VAL(OBJ_NAME(&__intern->std)));\ RETURN_THROWS();\ } \ } diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 3a2ec9eddc034..ab2e0a643c63b 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1169,14 +1169,14 @@ static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_ ZVAL_UNDEF(&property_entities_default_value); zend_string *property_entities_name = zend_string_init("entities", sizeof("entities") - 1, 1); zend_string *property_entities_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_entities_name, &property_entities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_entities_class_DOMNamedNodeMap, 0, 0)); + zend_declare_typed_property(class_entry, property_entities_name, &property_entities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_entities_class_DOMNamedNodeMap, 0, 0)); zend_string_release(property_entities_name); zval property_notations_default_value; ZVAL_UNDEF(&property_notations_default_value); zend_string *property_notations_name = zend_string_init("notations", sizeof("notations") - 1, 1); zend_string *property_notations_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_notations_name, &property_notations_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_notations_class_DOMNamedNodeMap, 0, 0)); + zend_declare_typed_property(class_entry, property_notations_name, &property_notations_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_notations_class_DOMNamedNodeMap, 0, 0)); zend_string_release(property_notations_name); zval property_publicId_default_value; @@ -1310,56 +1310,56 @@ static zend_class_entry *register_class_DOMNode(void) ZVAL_UNDEF(&property_parentNode_default_value); zend_string *property_parentNode_name = zend_string_init("parentNode", sizeof("parentNode") - 1, 1); zend_string *property_parentNode_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_parentNode_name); zval property_parentElement_default_value; ZVAL_UNDEF(&property_parentElement_default_value); zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1); zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_parentElement_name); zval property_childNodes_default_value; ZVAL_UNDEF(&property_childNodes_default_value); zend_string *property_childNodes_name = zend_string_init("childNodes", sizeof("childNodes") - 1, 1); zend_string *property_childNodes_class_DOMNodeList = zend_string_init("DOMNodeList", sizeof("DOMNodeList")-1, 1); - zend_declare_typed_property(class_entry, property_childNodes_name, &property_childNodes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_childNodes_class_DOMNodeList, 0, 0)); + zend_declare_typed_property(class_entry, property_childNodes_name, &property_childNodes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_childNodes_class_DOMNodeList, 0, 0)); zend_string_release(property_childNodes_name); zval property_firstChild_default_value; ZVAL_UNDEF(&property_firstChild_default_value); zend_string *property_firstChild_name = zend_string_init("firstChild", sizeof("firstChild") - 1, 1); zend_string *property_firstChild_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_firstChild_name, &property_firstChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstChild_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstChild_name, &property_firstChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstChild_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_firstChild_name); zval property_lastChild_default_value; ZVAL_UNDEF(&property_lastChild_default_value); zend_string *property_lastChild_name = zend_string_init("lastChild", sizeof("lastChild") - 1, 1); zend_string *property_lastChild_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_lastChild_name, &property_lastChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastChild_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastChild_name, &property_lastChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastChild_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_lastChild_name); zval property_previousSibling_default_value; ZVAL_UNDEF(&property_previousSibling_default_value); zend_string *property_previousSibling_name = zend_string_init("previousSibling", sizeof("previousSibling") - 1, 1); zend_string *property_previousSibling_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_previousSibling_name, &property_previousSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousSibling_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousSibling_name, &property_previousSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousSibling_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_previousSibling_name); zval property_nextSibling_default_value; ZVAL_UNDEF(&property_nextSibling_default_value); zend_string *property_nextSibling_name = zend_string_init("nextSibling", sizeof("nextSibling") - 1, 1); zend_string *property_nextSibling_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_nextSibling_name, &property_nextSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextSibling_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextSibling_name, &property_nextSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextSibling_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_nextSibling_name); zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); zend_string *property_attributes_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_DOMNamedNodeMap, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_attributes_class_DOMNamedNodeMap, 0, MAY_BE_NULL)); zend_string_release(property_attributes_name); zval property_isConnected_default_value; @@ -1372,7 +1372,7 @@ static zend_class_entry *register_class_DOMNode(void) ZVAL_UNDEF(&property_ownerDocument_default_value); zend_string *property_ownerDocument_name = zend_string_init("ownerDocument", sizeof("ownerDocument") - 1, 1); zend_string *property_ownerDocument_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1); - zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOM_Document, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerDocument_class_DOM_Document, 0, MAY_BE_NULL)); zend_string_release(property_ownerDocument_name); zval property_namespaceURI_default_value; @@ -1462,21 +1462,21 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void) ZVAL_UNDEF(&property_ownerDocument_default_value); zend_string *property_ownerDocument_name = zend_string_init("ownerDocument", sizeof("ownerDocument") - 1, 1); zend_string *property_ownerDocument_class_DOMDocument = zend_string_init("DOMDocument", sizeof("DOMDocument")-1, 1); - zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOMDocument, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerDocument_class_DOMDocument, 0, MAY_BE_NULL)); zend_string_release(property_ownerDocument_name); zval property_parentNode_default_value; ZVAL_UNDEF(&property_parentNode_default_value); zend_string *property_parentNode_name = zend_string_init("parentNode", sizeof("parentNode") - 1, 1); zend_string *property_parentNode_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_parentNode_name); zval property_parentElement_default_value; ZVAL_UNDEF(&property_parentElement_default_value); zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1); zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_parentElement_name); return class_entry; @@ -1505,14 +1505,14 @@ static zend_class_entry *register_class_DOMDocumentFragment(zend_class_entry *cl ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; @@ -1567,14 +1567,14 @@ static zend_class_entry *register_class_DOMCharacterData(zend_class_entry *class ZVAL_UNDEF(&property_previousElementSibling_default_value); zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); zend_string *property_previousElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_previousElementSibling_name); zval property_nextElementSibling_default_value; ZVAL_UNDEF(&property_nextElementSibling_default_value); zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); zend_string *property_nextElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); return class_entry; @@ -1610,7 +1610,7 @@ static zend_class_entry *register_class_DOMAttr(zend_class_entry *class_entry_DO ZVAL_UNDEF(&property_ownerElement_default_value); zend_string *property_ownerElement_name = zend_string_init("ownerElement", sizeof("ownerElement") - 1, 1); zend_string *property_ownerElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_ownerElement_name, &property_ownerElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerElement_name, &property_ownerElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_ownerElement_name); zval property_schemaTypeInfo_default_value; @@ -1659,14 +1659,14 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; @@ -1679,14 +1679,14 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry ZVAL_UNDEF(&property_previousElementSibling_default_value); zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); zend_string *property_previousElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_previousElementSibling_name); zval property_nextElementSibling_default_value; ZVAL_UNDEF(&property_nextElementSibling_default_value); zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); zend_string *property_nextElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); return class_entry; @@ -1703,7 +1703,7 @@ static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entr ZVAL_UNDEF(&property_implementation_default_value); zend_string *property_implementation_name = zend_string_init("implementation", sizeof("implementation") - 1, 1); zend_string *property_implementation_class_DOMImplementation = zend_string_init("DOMImplementation", sizeof("DOMImplementation")-1, 1); - zend_declare_typed_property(class_entry, property_implementation_name, &property_implementation_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_implementation_class_DOMImplementation, 0, 0)); + zend_declare_typed_property(class_entry, property_implementation_name, &property_implementation_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_implementation_class_DOMImplementation, 0, 0)); zend_string_release(property_implementation_name); zval property_actualEncoding_default_value; @@ -1958,7 +1958,7 @@ static zend_class_entry *register_class_DOMXPath(void) ZVAL_UNDEF(&property_document_default_value); zend_string *property_document_name = zend_string_init("document", sizeof("document") - 1, 1); zend_string *property_document_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1); - zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_document_class_DOM_Document, 0, 0)); + zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_document_class_DOM_Document, 0, 0)); zend_string_release(property_document_name); zval property_registerNodeNamespaces_default_value; @@ -1984,14 +1984,14 @@ static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_ent ZVAL_UNDEF(&property_doctype_default_value); zend_string *property_doctype_name = zend_string_init("doctype", sizeof("doctype") - 1, 1); zend_string *property_doctype_class_DOM_DocumentType = zend_string_init("DOM\\DocumentType", sizeof("DOM\\DocumentType")-1, 1); - zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_doctype_class_DOM_DocumentType, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_doctype_class_DOM_DocumentType, 0, MAY_BE_NULL)); zend_string_release(property_doctype_name); zval property_documentElement_default_value; ZVAL_UNDEF(&property_documentElement_default_value); zend_string *property_documentElement_name = zend_string_init("documentElement", sizeof("documentElement") - 1, 1); zend_string *property_documentElement_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_documentElement_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_documentElement_name); zval property_encoding_default_value; @@ -2016,14 +2016,14 @@ static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_ent ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index ff2b37ae09f0b..de9d2d435ede8 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -323,7 +323,7 @@ static HashTable *gmp_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */ { gmp_object *old_object = GET_GMP_OBJECT_FROM_OBJ(obj); - gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(obj->ce)); + gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(OBJ_CE(obj))); zend_objects_clone_members( &new_object->std, &old_object->std); diff --git a/ext/hash/hash.c b/ext/hash/hash.c index e0024daf90d77..d5ff4f215a4df 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -1396,7 +1396,7 @@ static void php_hashcontext_free(zend_object *obj) { /* {{{ php_hashcontext_clone */ static zend_object *php_hashcontext_clone(zend_object *zobj) { php_hashcontext_object *oldobj = php_hashcontext_from_object(zobj); - zend_object *znew = php_hashcontext_create(zobj->ce); + zend_object *znew = php_hashcontext_create(OBJ_CE(zobj)); php_hashcontext_object *newobj = php_hashcontext_from_object(znew); if (!oldobj->context) { diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 4709c0e2be4a7..8db6984767382 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -124,7 +124,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) { /* Optimized version without rebuilding properties HashTable */ zend_object *obj = Z_OBJ_P(val); - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_property_info *prop_info; zval *prop; int i; diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 6d7f567b95994..bd6093fd963ba 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -15,6 +15,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_API.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -1258,10 +1259,7 @@ PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) php_libxml_func_handler *export_hnd; if (Z_TYPE_P(object) == IS_OBJECT) { - ce = Z_OBJCE_P(object); - while (ce->parent != NULL) { - ce = ce->parent; - } + ce = zend_class_entry_get_root(Z_OBJCE_P(object)); if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) { node = export_hnd->export_func(object); } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index b9149fa1fa12a..9d3d809b6a871 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -31,6 +31,7 @@ #include "zend_accelerator_blacklist.h" #include "zend_list.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_inheritance.h" #include "zend_exceptions.h" @@ -618,8 +619,18 @@ static inline void accel_copy_permanent_list_types( ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(*single_type)); accel_copy_permanent_list_types(new_interned_string, *single_type); } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_TYPE_NAME(*single_type))); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_packed_name_reference pnr = ZEND_TYPE_PNR(*single_type); + if (ZEND_PNR_IS_SIMPLE(pnr)) { + ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_PNR_SIMPLE_GET_NAME(pnr))); + } else { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + name_ref->name = new_interned_string(name_ref->name); + name_ref->key = new_interned_string(name_ref->key); + for (uint32_t i = 0; i < name_ref->args.num_types; i++) { + accel_copy_permanent_list_types(new_interned_string, name_ref->args.types[i]); + } + } } } ZEND_TYPE_FOREACH_END(); } @@ -666,8 +677,10 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int /* class table hash keys, class names, properties, methods, constants, etc */ ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) { zend_class_entry *ce; + zend_class_reference *class_ref; ce = (zend_class_entry*)Z_PTR(p->val); + class_ref = ZEND_CE_TO_REF(ce); if (p->key) { p->key = new_interned_string(p->key); @@ -678,6 +691,8 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int ZEND_ASSERT(ZSTR_HAS_CE_CACHE(ce->name)); } + class_ref->key = new_interned_string(class_ref->key); + ZEND_HASH_MAP_FOREACH_BUCKET(&ce->properties_info, q) { zend_property_info *info; @@ -2290,7 +2305,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, } ce = entry->ce; if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); + ZSTR_SET_CE_CACHE_EX(ce->name, ZEND_CE_TO_REF(ce), 0); } return ce; } @@ -3646,8 +3661,8 @@ static void preload_sort_classes(void *base, size_t count, size_t siz, compare_f while (b1 < end) { try_again: ce = (zend_class_entry*)Z_PTR(b1->val); - if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { - p = ce->parent; + if (ce->num_parents && (ce->ce_flags & ZEND_ACC_LINKED)) { + p = ce->parents[0]->ce; if (p->type == ZEND_USER_CLASS) { b2 = b1 + 1; while (b2 < end) { @@ -3693,12 +3708,12 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e memset(error, 0, sizeof(preload_error)); if (ce->parent_name) { - zend_string *key = zend_string_tolower(ce->parent_name); + zend_string *key = zend_string_tolower(ZEND_PNR_GET_NAME(ce->parent_name)); zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key); zend_string_release(key); if (!parent) { error->kind = "Unknown parent "; - error->name = ZSTR_VAL(ce->parent_name); + error->name = ZSTR_VAL(ZEND_PNR_GET_NAME(ce->parent_name)); return FAILURE; } } @@ -3772,7 +3787,7 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) } } if (ce->default_static_members_count) { - uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count; + uint32_t count = ce->num_parents ? ce->default_static_members_count - ce->parents[0]->ce->default_static_members_count : ce->default_static_members_count; bool resolved = true; val = ce->default_static_members_table + ce->default_static_members_count - 1; @@ -3915,7 +3930,7 @@ static void preload_link(void) uint32_t temporary_flags = ZEND_ACC_FILE_CACHED|ZEND_ACC_CACHED; ce->ce_flags |= temporary_flags; if (ce->parent_name) { - zend_string_addref(ce->parent_name); + zend_string_addref(ZEND_PNR_GET_NAME(ce->parent_name)); } /* Record and suppress errors during inheritance. */ diff --git a/ext/opcache/tests/opt/dce_006.phpt b/ext/opcache/tests/opt/dce_006.phpt index 97d2988793907..6d65c8302e1a9 100644 --- a/ext/opcache/tests/opt/dce_006.phpt +++ b/ext/opcache/tests/opt/dce_006.phpt @@ -30,7 +30,7 @@ foo: ; (after optimizer) ; %sdce_006.php:5-8 0000 CV0($x) = RECV 1 -0001 V2 = NEW 0 string("A") +0001 V2 = NEW 0 pnr("A") 0002 DO_FCALL 0003 CV1($a) = QM_ASSIGN V2 0004 ASSIGN_OBJ CV1($a) string("foo") diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 74161688ea443..a6aa5c2ded674 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -26,6 +26,7 @@ #include "zend_persist.h" #include "zend_shared_alloc.h" #include "zend_observer.h" +#include "zend_types.h" #ifdef __SSE2__ /* For SSE2 adler32 */ @@ -239,7 +240,7 @@ static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, Ha _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); if ((ce->ce_flags & ZEND_ACC_LINKED) && ZSTR_VAL(p->key)[0]) { if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); + ZSTR_SET_CE_CACHE_EX(ce->name, ZEND_CE_TO_REF(ce), 0); } if (UNEXPECTED(call_observers)) { _zend_observer_class_linked_notify(ce, p->key); diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index edcaf689ab7f1..32ceb89ef2402 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -17,6 +17,7 @@ */ #include "zend.h" +#include "zend_types.h" #include "zend_virtual_cwd.h" #include "zend_compile.h" #include "zend_vm.h" @@ -186,6 +187,66 @@ static int zend_file_cache_flock(int fd, int type) } \ } while (0) +# define SERIALIZE_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + SERIALIZE_STR(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + SERIALIZE_STR(_name_ref->name); \ + SERIALIZE_STR(_name_ref->key); \ + zend_file_cache_serialize_type_list(&_name_ref->args, script, info, buf); \ + SERIALIZE_PTR(_name_ref); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } while (0) + +# define UNSERIALIZE_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + UNSERIALIZE_STR(_name); \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + UNSERIALIZE_PTR(_name_ref); \ + UNSERIALIZE_STR(_name_ref->name); \ + UNSERIALIZE_STR(_name_ref->key); \ + zend_file_cache_unserialize_type_list(&_name_ref->args, script, buf); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } while (0) + +# define SERIALIZE_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + SERIALIZE_PNR(_pnr); \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } while (0) + +# define UNSERIALIZE_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + UNSERIALIZE_PNR(_pnr); \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } while (0) + +# define SERIALIZE_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + SERIALIZE_PNR(_pnr); \ + Z_PNR_P(zv) = _pnr; \ + } while (0) + +# define UNSERIALIZE_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + UNSERIALIZE_PNR(_pnr); \ + Z_PNR_P(zv) = _pnr; \ + } while (0) + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -239,6 +300,13 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_persistent_script *script, void *buf); +static void zend_file_cache_serialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, zend_file_cache_metainfo *info, + void *buf); + +static void zend_file_cache_unserialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, void *buf); + static void *zend_file_cache_serialize_interned(zend_string *str, zend_file_cache_metainfo *info) { @@ -404,6 +472,9 @@ static void zend_file_cache_serialize_zval(zval *zv, zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf); } break; + case IS_PNR: + SERIALIZE_ZV_PNR(zv); + break; case IS_INDIRECT: /* Used by static properties. */ SERIALIZE_PTR(Z_INDIRECT_P(zv)); @@ -448,13 +519,20 @@ static void zend_file_cache_serialize_type( ZEND_TYPE_LIST_FOREACH(list, list_type) { zend_file_cache_serialize_type(list_type, script, info, buf); } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string *type_name = ZEND_TYPE_NAME(*type); - SERIALIZE_STR(type_name); - ZEND_TYPE_SET_PTR(*type, type_name); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + SERIALIZE_TYPE_PNR(*type); } } +static void zend_file_cache_serialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, zend_file_cache_metainfo *info, + void *buf) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_file_cache_serialize_type(single_type, script, info, buf); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_file_cache_serialize_op_array(zend_op_array *op_array, zend_persistent_script *script, zend_file_cache_metainfo *info, @@ -714,12 +792,25 @@ static void zend_file_cache_serialize_class(zval *zv, ce = Z_PTR_P(zv); UNSERIALIZE_PTR(ce); + zend_class_reference *class_ref = ZEND_CE_TO_REF(ce); + SERIALIZE_PTR(class_ref->ce); + SERIALIZE_STR(class_ref->key); + SERIALIZE_STR(ce->name); - if (ce->parent) { + if (ce->num_parents) { if (!(ce->ce_flags & ZEND_ACC_LINKED)) { - SERIALIZE_STR(ce->parent_name); + SERIALIZE_PNR(ce->parent_name); } else { - SERIALIZE_PTR(ce->parent); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(parent_ref)) { + SERIALIZE_PTR(parent_ref->ce); + SERIALIZE_STR(parent_ref->key); + zend_file_cache_serialize_type_list(&parent_ref->args, script, info, buf); + } + SERIALIZE_PTR(ce->parents[i]); + } + SERIALIZE_PTR(ce->parents); } } zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func); @@ -1252,6 +1343,9 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf); } break; + case IS_PNR: + UNSERIALIZE_ZV_PNR(zv); + break; case IS_INDIRECT: /* Used by static properties. */ UNSERIALIZE_PTR(Z_INDIRECT_P(zv)); @@ -1280,7 +1374,7 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri } static void zend_file_cache_unserialize_type( - zend_type *type, zend_class_entry *scope, zend_persistent_script *script, void *buf) + zend_type *type, zend_persistent_script *script, void *buf) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); @@ -1289,20 +1383,26 @@ static void zend_file_cache_unserialize_type( zend_type *list_type; ZEND_TYPE_LIST_FOREACH(list, list_type) { - zend_file_cache_unserialize_type(list_type, scope, script, buf); + zend_file_cache_unserialize_type(list_type, script, buf); } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string *type_name = ZEND_TYPE_NAME(*type); - UNSERIALIZE_STR(type_name); - ZEND_TYPE_SET_PTR(*type, type_name); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + UNSERIALIZE_TYPE_PNR(*type); if (!script->corrupted) { - zend_accel_get_class_name_map_ptr(type_name); + zend_accel_get_class_name_map_ptr(ZEND_TYPE_PNR_NAME(*type)); } else { - zend_alloc_ce_cache(type_name); + zend_alloc_ce_cache(ZEND_TYPE_PNR_NAME(*type)); } } } +static void zend_file_cache_unserialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, void *buf) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_file_cache_unserialize_type(single_type, script, buf); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_file_cache_unserialize_op_array(zend_op_array *op_array, zend_persistent_script *script, void *buf) @@ -1444,7 +1544,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr if (!IS_UNSERIALIZED(p->name)) { UNSERIALIZE_STR(p->name); } - zend_file_cache_unserialize_type(&p->type, (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf); + zend_file_cache_unserialize_type(&p->type, script, buf); p++; } } @@ -1510,7 +1610,7 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, UNSERIALIZE_STR(prop->doc_comment); } UNSERIALIZE_ATTRIBUTES(prop->attributes); - zend_file_cache_unserialize_type(&prop->type, prop->ce, script, buf); + zend_file_cache_unserialize_type(&prop->type, script, buf); } } } @@ -1535,7 +1635,7 @@ static void zend_file_cache_unserialize_class_constant(zval * UNSERIALIZE_STR(c->doc_comment); } UNSERIALIZE_ATTRIBUTES(c->attributes); - zend_file_cache_unserialize_type(&c->type, c->ce, script, buf); + zend_file_cache_unserialize_type(&c->type, script, buf); } } } @@ -1557,11 +1657,20 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_alloc_ce_cache(ce->name); } } - if (ce->parent) { + if (ce->num_parents) { if (!(ce->ce_flags & ZEND_ACC_LINKED)) { - UNSERIALIZE_STR(ce->parent_name); + UNSERIALIZE_PNR(ce->parent_name); } else { - UNSERIALIZE_PTR(ce->parent); + UNSERIALIZE_PTR(ce->parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + UNSERIALIZE_PTR(ce->parents[i]); + zend_class_reference *parent_ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(parent_ref)) { + UNSERIALIZE_PTR(parent_ref->ce); + UNSERIALIZE_STR(parent_ref->key); + zend_file_cache_unserialize_type_list(&parent_ref->args, script, buf); + } + } } } zend_file_cache_unserialize_hash(&ce->function_table, diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index b10e5a8046ab1..d84581efbf6fb 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -24,6 +24,7 @@ #include "zend_persist.h" #include "zend_extensions.h" #include "zend_shared_alloc.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_constants.h" #include "zend_operators.h" @@ -81,10 +82,52 @@ } \ } while (0) +# define zend_accel_store_pnr(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + zend_accel_store_interned_string(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + zend_string *_new_name_ref = zend_shared_alloc_get_xlat_entry(_name_ref); \ + if (_new_name_ref) { \ + (pnr) = ZEND_PNR_ENCODE_REF(_new_name_ref); \ + } else { \ + _name_ref = zend_shared_memdup_put((void*)_name_ref, ZEND_CLASS_REF_SIZE(_name_ref->args.num_types)); \ + zend_accel_store_interned_string(_name_ref->name); \ + zend_accel_store_interned_string(_name_ref->key); \ + zend_persist_type_list(&_name_ref->args); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } \ + } while (0) + +# define zend_accel_store_zv_pnr(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + zend_accel_store_pnr(_pnr); \ + if (Z_PNR_P(zv) != _pnr) { \ + Z_PNR_P(zv) = _pnr; \ + } \ + } while (0) + +# define zend_accel_store_type_pnr(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + zend_accel_store_pnr(_pnr); \ + if (ZEND_TYPE_PNR(type) != _pnr) { \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } \ + } while (0) + typedef void (*zend_persist_func_t)(zval*); static void zend_persist_zval(zval *z); static void zend_persist_op_array(zval *zv); +static void zend_persist_type_list(zend_type_list *type_list); + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -265,6 +308,9 @@ static void zend_persist_zval(zval *z) efree(old_ref); } break; + case IS_PNR: + zend_accel_store_zv_pnr(z); + break; default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -357,17 +403,22 @@ static void zend_persist_type(zend_type *type) { zend_persist_type(single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *type_name = ZEND_TYPE_NAME(*single_type); - zend_accel_store_interned_string(type_name); - ZEND_TYPE_SET_PTR(*single_type, type_name); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_accel_store_type_pnr(*single_type); if (!ZCG(current_persistent_script)->corrupted) { - zend_accel_get_class_name_map_ptr(type_name); + zend_accel_get_class_name_map_ptr(ZEND_TYPE_PNR_NAME(*single_type)); } } } ZEND_TYPE_FOREACH_END(); } +static void zend_persist_type_list(zend_type_list *type_list) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_persist_type(single_type); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) { zend_op *persist_ptr; @@ -855,7 +906,14 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) if (new_ce) { return new_ce; } - ce = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); + + zend_class_reference *class_ref = zend_shared_memdup_put( + ZEND_CE_TO_REF(ce), sizeof(zend_class_entry_storage)); + ce = (zend_class_entry*)((char*)class_ref + ZEND_CLASS_ENTRY_HEADER_SIZE); + zend_shared_alloc_register_xlat_entry(orig_ce, ce); + class_ref->ce = ce; + zend_accel_store_interned_string(class_ref->key); + if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { ce->ce_flags |= ZEND_ACC_IMMUTABLE; if ((ce->ce_flags & ZEND_ACC_LINKED) @@ -867,6 +925,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) } else { ce->ce_flags |= ZEND_ACC_FILE_CACHED; } + ce->inheritance_cache = NULL; if (!(ce->ce_flags & ZEND_ACC_CACHED)) { @@ -879,7 +938,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) zend_accel_get_class_name_map_ptr(ce->name); } if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - zend_accel_store_interned_string(ce->parent_name); + zend_accel_store_pnr(ce->parent_name); } } @@ -904,7 +963,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) /* Persist only static properties in this class. * Static properties from parent classes will be handled in class_copy_ctor */ - i = (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) ? ce->parent->default_static_members_count : 0; + i = (ce->num_parents && (ce->ce_flags & ZEND_ACC_LINKED)) ? ce->parents[0]->ce->default_static_members_count : 0; for (; i < ce->default_static_members_count; i++) { zend_persist_zval(&ce->default_static_members_table[i]); } @@ -1066,24 +1125,36 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) void zend_update_parent_ce(zend_class_entry *ce) { if (ce->ce_flags & ZEND_ACC_LINKED) { - if (ce->parent) { - int i, end; - zend_class_entry *parent = ce->parent; - - if (parent->type == ZEND_USER_CLASS) { - zend_class_entry *p = zend_shared_alloc_get_xlat_entry(parent); - + if (ce->num_parents) { + ce->parents = zend_shared_memdup_free(ce->parents, + sizeof(zend_class_reference *) * ce->num_parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + zend_class_reference *p = zend_shared_alloc_get_xlat_entry(parent_ref); if (p) { - ce->parent = parent = p; + ce->parents[i] = parent_ref = p; + } else { + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(parent_ref)); + ce->parents[i] = parent_ref = zend_shared_memdup_free( + parent_ref, + ZEND_CLASS_REF_SIZE(parent_ref->args.num_types)); + zend_accel_store_interned_string(parent_ref->key); + zend_persist_type_list(&parent_ref->args); + zend_class_entry *ce = zend_shared_alloc_get_xlat_entry(parent_ref->ce); + if (ce) { + parent_ref->ce = ce; + } } } /* Create indirections to static properties from parent classes */ - i = parent->default_static_members_count - 1; - while (parent && parent->default_static_members_table) { - end = parent->parent ? parent->parent->default_static_members_count : 0; - for (; i >= end; i--) { - zval *p = &ce->default_static_members_table[i]; + zend_class_entry *parent = ce->parents[0]->ce; + int j = parent->default_static_members_count - 1; + int end; + while (parent->default_static_members_table) { + end = parent->num_parents ? parent->parents[0]->ce->default_static_members_count : 0; + for (; j >= end; j--) { + zval *p = &ce->default_static_members_table[j]; /* The static property may have been overridden by a trait * during inheritance. In that case, the property default * value is replaced by zend_declare_typed_property() at the @@ -1091,11 +1162,14 @@ void zend_update_parent_ce(zend_class_entry *ce) * point to the parent property value if the child value was * already indirect. */ if (Z_TYPE_P(p) == IS_INDIRECT) { - ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); + ZVAL_INDIRECT(p, &parent->default_static_members_table[j]); } } - parent = parent->parent; + if (!parent->num_parents) { + break; + } + parent = parent->parents[0]->ce; } } diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 2d4a3c92afa29..aa90ddbd8e03e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -26,6 +26,7 @@ #include "zend_shared_alloc.h" #include "zend_operators.h" #include "zend_attributes.h" +#include "zend_types.h" #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) @@ -45,8 +46,50 @@ } \ } while (0) +# define ADD_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + ADD_INTERNED_STRING(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + if (!ZCG(current_persistent_script)->corrupted \ + && zend_accel_in_shm(_name_ref)) { \ + break; \ + } \ + size_t _size = zend_shared_memdup_size(_name_ref, ZEND_CLASS_REF_SIZE(_name_ref->args.num_types)); \ + if (_size) { \ + ADD_SIZE(_size); \ + ADD_INTERNED_STRING(_name_ref->name); \ + ADD_INTERNED_STRING(_name_ref->key); \ + zend_persist_type_list_calc(&_name_ref->args); \ + } \ + } \ + } while (0) + +# define ADD_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference pnr = ZEND_TYPE_PNR(type); \ + ADD_PNR(pnr); \ + if (ZEND_TYPE_PNR(type) != pnr) { \ + ZEND_TYPE_SET_PNR((type), pnr); \ + } \ + } while (0) + +# define ADD_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference pnr = Z_PNR_P(zv); \ + ADD_PNR(pnr); \ + if (Z_PNR_P(zv) != pnr) { \ + Z_PNR_P(zv) = pnr; \ + } \ + } while (0) + static void zend_persist_zval_calc(zval *z); static void zend_persist_op_array_calc(zval *zv); +static void zend_persist_type_list_calc(zend_type_list *type_list); static void zend_hash_persist_calc(HashTable *ht) { @@ -146,6 +189,10 @@ static void zend_persist_zval_calc(zval *z) } } break; + case IS_PNR: { + ADD_ZV_PNR(z); + break; + } default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -191,14 +238,22 @@ static void zend_persist_type_calc(zend_type *type) zend_persist_type_calc(single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *type_name = ZEND_TYPE_NAME(*single_type); - ADD_INTERNED_STRING(type_name); - ZEND_TYPE_SET_PTR(*single_type, type_name); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + ADD_TYPE_PNR(*single_type); } } ZEND_TYPE_FOREACH_END(); } +static void zend_persist_type_list_calc(zend_type_list *type_list) +{ + ADD_SIZE(ZEND_TYPE_LIST_SIZE(type_list->num_types)); + + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_persist_type_calc(single_type); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_persist_op_array_calc_ex(zend_op_array *op_array) { if (op_array->function_name) { @@ -408,18 +463,32 @@ void zend_persist_class_entry_calc(zend_class_entry *ce) Bucket *p; if (ce->type == ZEND_USER_CLASS) { - /* The same zend_class_entry may be reused by class_alias */ - if (zend_shared_alloc_get_xlat_entry(ce)) { - return; - } + ZEND_ASSERT(!zend_shared_alloc_get_xlat_entry(ce)); zend_shared_alloc_register_xlat_entry(ce, ce); + zend_shared_alloc_register_xlat_entry(ZEND_CE_TO_REF(ce), ZEND_CE_TO_REF(ce)); - ADD_SIZE(sizeof(zend_class_entry)); + ADD_SIZE(sizeof(zend_class_entry_storage)); + ADD_INTERNED_STRING(ZEND_CE_TO_REF(ce)->key); if (!(ce->ce_flags & ZEND_ACC_CACHED)) { ADD_INTERNED_STRING(ce->name); if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - ADD_INTERNED_STRING(ce->parent_name); + ADD_PNR(ce->parent_name); + } + } + if (ce->ce_flags & ZEND_ACC_LINKED) { + if (ce->num_parents) { + ADD_SIZE(sizeof(zend_class_reference *) * ce->num_parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + if (zend_shared_alloc_get_xlat_entry(parent_ref)) { + continue; + } + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(parent_ref)); + ADD_SIZE(ZEND_CLASS_REF_SIZE(parent_ref->args.num_types)); + ADD_INTERNED_STRING(parent_ref->key); + zend_persist_type_list_calc(&parent_ref->args); + } } } @@ -563,7 +632,9 @@ static void zend_accel_persist_class_table_calc(HashTable *class_table) ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); ADD_INTERNED_STRING(p->key); - zend_persist_class_entry_calc(Z_CE(p->val)); + if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { + zend_persist_class_entry_calc(Z_CE(p->val)); + } } ZEND_HASH_FOREACH_END(); } diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c index e16c9f6722fc3..043c4ac6232fd 100644 --- a/ext/random/engine_mt19937.c +++ b/ext/random/engine_mt19937.c @@ -350,30 +350,30 @@ PHP_METHOD(Random_Engine_Mt19937, __unserialize) /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ if (zend_hash_num_elements(d) != 2) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } /* members */ t = zend_hash_index_find(d, 0); if (!t || Z_TYPE_P(t) != IS_ARRAY) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } object_properties_load(&engine->std, Z_ARRVAL_P(t)); if (EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } /* state */ t = zend_hash_index_find(d, 1); if (!t || Z_TYPE_P(t) != IS_ARRAY) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } if (!engine->algo->unserialize(engine->status, Z_ARRVAL_P(t))) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } } diff --git a/ext/random/random.c b/ext/random/random.c index f34d55f340727..d90b94216eca8 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -277,7 +277,7 @@ PHPAPI void php_random_engine_common_free_object(zend_object *object) PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object) { php_random_engine *old_engine = php_random_engine_from_obj(object); - php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce)); + php_random_engine *new_engine = php_random_engine_from_obj(OBJ_CE(&old_engine->std)->create_object(OBJ_CE(&old_engine->std))); new_engine->algo = old_engine->algo; if (old_engine->status) { diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index 2ba8ced481b7f..c808653320ed3 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -343,7 +343,7 @@ static zend_class_entry *register_class_Random_Randomizer(void) ZVAL_UNDEF(&property_engine_default_value); zend_string *property_engine_name = zend_string_init("engine", sizeof("engine") - 1, 1); zend_string *property_engine_class_Random_Engine = zend_string_init("Random\\Engine", sizeof("Random\\Engine")-1, 1); - zend_declare_typed_property(class_entry, property_engine_name, &property_engine_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_engine_class_Random_Engine, 0, 0)); + zend_declare_typed_property(class_entry, property_engine_name, &property_engine_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_engine_class_Random_Engine, 0, 0)); zend_string_release(property_engine_name); return class_entry; diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index fe9ad5fc35a9c..9dc6ec61848a9 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -28,7 +28,7 @@ #include "Zend/zend_exceptions.h" static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) { - if (engine_object->ce->type == ZEND_INTERNAL_CLASS) { + if (OBJ_CE(engine_object)->type == ZEND_INTERNAL_CLASS) { /* Internal classes always php_random_engine struct */ php_random_engine *engine = php_random_engine_from_obj(engine_object); @@ -43,7 +43,7 @@ static inline void randomizer_common_init(php_random_randomizer *randomizer, zen zend_function *generate_method; mname = ZSTR_INIT_LITERAL("generate", 0); - generate_method = zend_hash_find_ptr(&engine_object->ce->function_table, mname); + generate_method = zend_hash_find_ptr(&OBJ_CE(engine_object)->function_table, mname); zend_string_release(mname); /* Create compatible state */ @@ -503,7 +503,7 @@ PHP_METHOD(Random_Randomizer, __unserialize) RETURN_THROWS(); } - zengine = zend_read_property(randomizer->std.ce, &randomizer->std, "engine", strlen("engine"), 1, NULL); + zengine = zend_read_property(OBJ_CE(&randomizer->std), &randomizer->std, "engine", strlen("engine"), 1, NULL); if (Z_TYPE_P(zengine) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zengine), random_ce_Random_Engine)) { zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); RETURN_THROWS(); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 53e978604ad91..51284d7744c00 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_types.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -105,7 +106,7 @@ PHPAPI zend_class_entry *reflection_fiber_ptr; #define GET_REFLECTION_OBJECT() do { \ intern = Z_REFLECTION_P(ZEND_THIS); \ if (intern->ptr == NULL) { \ - if (EG(exception) && EG(exception)->ce == reflection_exception_ptr) { \ + if (EG(exception) && OBJ_CE(EG(exception)) == reflection_exception_ptr) { \ RETURN_THROWS(); \ } \ zend_throw_error(NULL, "Internal error: Failed to retrieve the reflection object"); \ @@ -233,8 +234,8 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ case REF_TYPE_TYPE: { type_reference *type_ref = intern->ptr; - if (ZEND_TYPE_HAS_NAME(type_ref->type)) { - zend_string_release(ZEND_TYPE_NAME(type_ref->type)); + if (ZEND_TYPE_HAS_SIMPLE_PNR(type_ref->type)) { + zend_string_release(ZEND_TYPE_PNR_SIMPLE_NAME(type_ref->type)); } efree(type_ref); break; @@ -350,8 +351,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char smart_str_append_printf(str, "class "); } smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name)); - if (ce->parent) { - smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parent->name)); + if (ce->num_parents) { + smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parents[0]->ce->name)); } if (ce->num_interfaces) { @@ -564,7 +565,7 @@ static void _class_const_string(smart_str *str, zend_string *name, zend_class_co const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c)); const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : ""; - zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type) : NULL; + zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type, NULL) : NULL; const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, ZSTR_VAL(name)); @@ -645,7 +646,7 @@ static int format_default_value(smart_str *str, zval *value) { } else if (Z_TYPE_P(value) == IS_OBJECT) { /* This branch may only be reached for default properties, which don't support arbitrary objects. */ zend_object *obj = Z_OBJ_P(value); - zend_class_entry *class = obj->ce; + zend_class_entry *class = OBJ_CE(obj); ZEND_ASSERT(class->ce_flags & ZEND_ACC_ENUM); smart_str_append(str, class->name); smart_str_appends(str, "::"); @@ -674,7 +675,7 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_ smart_str_append_printf(str, " "); } if (ZEND_TYPE_IS_SET(arg_info->type)) { - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, fptr->common.scope); smart_str_append(str, type_str); smart_str_appendc(str, ' '); zend_string_release(type_str); @@ -795,9 +796,9 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if (scope && fptr->common.scope) { if (fptr->common.scope != scope) { smart_str_append_printf(str, ", inherits %s", ZSTR_VAL(fptr->common.scope->name)); - } else if (fptr->common.scope->parent) { + } else if (fptr->common.scope->num_parents) { lc_name = zend_string_tolower(fptr->common.function_name); - if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) { + if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parents[0]->ce->function_table, lc_name)) != NULL) { if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) { smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name)); } @@ -865,7 +866,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { smart_str_append_printf(str, " %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return"); if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { - zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type); + zend_string *type_str = + zend_type_to_string(fptr->common.arg_info[-1].type, fptr->common.scope); smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str)); zend_string_release(type_str); } @@ -912,7 +914,7 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha smart_str_appends(str, "readonly "); } if (ZEND_TYPE_IS_SET(prop->type)) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); smart_str_append(str, type_str); smart_str_appendc(str, ' '); zend_string_release(type_str); @@ -1400,8 +1402,8 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be * do this for the top-level type, as resolutions inside type lists will be * fully visible to us (we'd have to do a fully copy of the type if we wanted * to prevent that). */ - if (ZEND_TYPE_HAS_NAME(type)) { - zend_string_addref(ZEND_TYPE_NAME(type)); + if (ZEND_TYPE_HAS_SIMPLE_PNR(type)) { + zend_string_addref(ZEND_TYPE_PNR_SIMPLE_NAME(type)); } } /* }}} */ @@ -2639,7 +2641,7 @@ ZEND_METHOD(ReflectionParameter, getClass) GET_REFLECTION_OBJECT_PTR(param); // TODO: This is going to return null for union types, which is rather odd. - if (ZEND_TYPE_HAS_NAME(param->arg_info->type)) { + if (ZEND_TYPE_HAS_SIMPLE_PNR(param->arg_info->type)) { /* Class name is stored as a string, we might also get "self" or "parent" * - For "self", simply use the function scope. If scope is NULL then * the function is global and thus self does not make any sense @@ -2654,7 +2656,7 @@ ZEND_METHOD(ReflectionParameter, getClass) */ zend_string *class_name; - class_name = ZEND_TYPE_NAME(param->arg_info->type); + class_name = ZEND_TYPE_PNR_SIMPLE_NAME(param->arg_info->type); if (zend_string_equals_literal_ci(class_name, "self")) { ce = param->fptr->common.scope; if (!ce) { @@ -2669,12 +2671,12 @@ ZEND_METHOD(ReflectionParameter, getClass) "Parameter uses \"parent\" as type but function is not a class member"); RETURN_THROWS(); } - if (!ce->parent) { + if (!ce->num_parents) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Parameter uses \"parent\" as type although class does not have a parent"); RETURN_THROWS(); } - ce = ce->parent; + ce = ce->parents[0]->ce; } else { ce = zend_lookup_class(class_name); if (!ce) { @@ -2961,7 +2963,9 @@ ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName) } else if (ast->kind == ZEND_AST_CONSTANT_CLASS) { RETVAL_STRINGL("__CLASS__", sizeof("__CLASS__")-1); } else if (ast->kind == ZEND_AST_CLASS_CONST) { - zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->child[1] == NULL && "Generic params not supported yet"); + zend_string *class_name = zend_ast_get_str(class_ast->child[0]); zend_string *const_name = zend_ast_get_str(ast->child[1]); RETVAL_NEW_STR(zend_string_concat3( ZSTR_VAL(class_name), ZSTR_LEN(class_name), @@ -3019,7 +3023,7 @@ ZEND_METHOD(ReflectionType, allowsNull) /* }}} */ /* For BC with iterable for named types */ -static zend_string *zend_named_reflection_type_to_string(zend_type type) { +static zend_string *zend_named_reflection_type_to_string(zend_type type, const zend_class_entry *scope) { if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { zend_string *iterable = ZSTR_KNOWN(ZEND_STR_ITERABLE); if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_NULL) { @@ -3027,12 +3031,12 @@ static zend_string *zend_named_reflection_type_to_string(zend_type type) { } return iterable; } - return zend_type_to_string(type); + return zend_type_to_string(type, scope); } static zend_string *zend_type_to_string_without_null(zend_type type) { ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; - return zend_named_reflection_type_to_string(type); + return zend_named_reflection_type_to_string(type, NULL); } /* {{{ Return the text of the type hint */ @@ -3046,7 +3050,7 @@ ZEND_METHOD(ReflectionType, __toString) } GET_REFLECTION_OBJECT_PTR(param); - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(param->type, NULL)); } /* }}} */ @@ -3064,7 +3068,7 @@ ZEND_METHOD(ReflectionNamedType, getName) if (param->legacy_behavior) { RETURN_STR(zend_type_to_string_without_null(param->type)); } - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(param->type, NULL)); } /* }}} */ @@ -3084,7 +3088,8 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin) } /* Treat "static" as a class type for the purposes of reflection. */ - RETVAL_BOOL(ZEND_TYPE_IS_ONLY_MASK(param->type) + RETVAL_BOOL(!ZEND_TYPE_IS_COMPLEX(param->type) + && ZEND_TYPE_FULL_MASK(param->type) && !(ZEND_TYPE_FULL_MASK(param->type) & MAY_BE_STATIC)); } /* }}} */ @@ -3117,14 +3122,17 @@ ZEND_METHOD(ReflectionUnionType, getTypes) GET_REFLECTION_OBJECT_PTR(param); array_init(return_value); - if (ZEND_TYPE_HAS_LIST(param->type)) { - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type); - } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(param->type)) { - zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0)); + if (ZEND_TYPE_IS_COMPLEX(param->type)) { + if (ZEND_TYPE_HAS_LIST(param->type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { + append_type(return_value, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else { + zend_type type = param->type; + ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_MAY_BE_MASK; + append_type(return_value, type); + } } type_mask = ZEND_TYPE_PURE_MASK(param->type); @@ -3230,7 +3238,7 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ } orig_obj = arg1_obj; - ce = arg1_obj->ce; + ce = OBJ_CE(arg1_obj); method_name = ZSTR_VAL(arg2_str); method_name_len = ZSTR_LEN(arg2_str); } else if (arg2_str) { @@ -3773,7 +3781,7 @@ ZEND_METHOD(ReflectionClassConstant, __construct) ZEND_PARSE_PARAMETERS_END(); if (classname_obj) { - ce = classname_obj->ce; + ce = OBJ_CE(classname_obj); } else { if ((ce = zend_lookup_class(classname_str)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str)); @@ -4041,8 +4049,8 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob intern = Z_REFLECTION_P(object); if (arg_obj) { - ZVAL_STR_COPY(reflection_prop_name(object), arg_obj->ce->name); - intern->ptr = arg_obj->ce; + ZVAL_STR_COPY(reflection_prop_name(object), OBJ_CE(arg_obj)->name); + intern->ptr = OBJ_CE(arg_obj); if (is_object) { ZVAL_OBJ_COPY(&intern->obj, arg_obj); } @@ -4232,7 +4240,8 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue) } } - if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, 0)) { + if (ZEND_TYPE_IS_SET(prop_info->type) + && !zend_verify_property_type(NULL, prop_info, value, 0)) { return; } @@ -5284,8 +5293,8 @@ ZEND_METHOD(ReflectionClass, getParentClass) } GET_REFLECTION_OBJECT_PTR(ce); - if (ce->parent) { - zend_reflection_class_factory(ce->parent, return_value); + if (ce->num_parents) { + zend_reflection_class_factory(ce->parents[0]->ce, return_value); } else { RETURN_FALSE; } @@ -5511,7 +5520,7 @@ ZEND_METHOD(ReflectionProperty, __construct) intern = Z_REFLECTION_P(object); if (classname_obj) { - ce = classname_obj->ce; + ce = OBJ_CE(classname_obj); } else { if ((ce = zend_lookup_class(classname_str)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str)); @@ -6712,7 +6721,7 @@ static int call_attribute_constructor( EG(current_execute_data) = call; } - zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params); + zend_call_known_function(ctor, obj, OBJ_CE(obj), NULL, argc, args, named_params); if (filename) { EG(current_execute_data) = call->prev_execute_data; @@ -7176,11 +7185,11 @@ ZEND_METHOD(ReflectionFiber, getCallable) /* {{{ _reflection_write_property */ static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { - if (zend_hash_exists(&object->ce->properties_info, name) + if (zend_hash_exists(&OBJ_CE(object)->properties_info, name) && (zend_string_equals(name, ZSTR_KNOWN(ZEND_STR_NAME)) || zend_string_equals(name, ZSTR_KNOWN(ZEND_STR_CLASS)))) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot set read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + "Cannot set read-only property %s::$%s", ZSTR_VAL(OBJ_CE(object)->name), ZSTR_VAL(name)); return &EG(uninitialized_zval); } else diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index 020b294052df3..deab0531f1603 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -489,7 +489,7 @@ static zend_class_entry *register_class_SoapServer(void) ZVAL_NULL(&property___soap_fault_default_value); zend_string *property___soap_fault_name = zend_string_init("__soap_fault", sizeof("__soap_fault") - 1, 1); zend_string *property___soap_fault_class_SoapFault = zend_string_init("SoapFault", sizeof("SoapFault")-1, 1); - zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); zend_string_release(property___soap_fault_name); return class_entry; @@ -692,7 +692,7 @@ static zend_class_entry *register_class_SoapClient(void) ZVAL_NULL(&property___soap_fault_default_value); zend_string *property___soap_fault_name = zend_string_init("__soap_fault", sizeof("__soap_fault") - 1, 1); zend_string *property___soap_fault_class_SoapFault = zend_string_init("SoapFault", sizeof("SoapFault")-1, 1); - zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); zend_string_release(property___soap_fault_name); zval property___last_request_default_value; diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index f3f2c7c50a41e..03b02b56cb495 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_types.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -71,7 +72,7 @@ static zend_class_entry * spl_find_ce_by_name(zend_string *name, bool autoload) PHP_FUNCTION(class_parents) { zval *obj; - zend_class_entry *parent_class, *ce; + zend_class_entry *ce; bool autoload = 1; /* We do not use Z_PARAM_OBJ_OR_STR here to be able to exclude int, float, and bool which are bogus class names */ @@ -93,10 +94,8 @@ PHP_FUNCTION(class_parents) } array_init(return_value); - parent_class = ce->parent; - while (parent_class) { - spl_add_class_name(return_value, parent_class, 0, 0); - parent_class = parent_class->parent; + for (uint32_t i = 0; i < ce->num_parents; i++) { + spl_add_class_name(return_value, ce->parents[i]->ce, 0, 0); } } /* }}} */ @@ -451,7 +450,8 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri } if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { - return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + return class_ref ? class_ref->ce : NULL; } else { zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); if (ce) { diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 4a9a5f59f5c13..97fbcd3df4be8 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -144,10 +144,10 @@ static void spl_array_object_free_storage(zend_object *object) static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) { spl_array_object *intern; - zend_class_entry *parent = class_type; + zend_class_entry *parent; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_array_object), parent); + intern = zend_object_alloc(sizeof(spl_array_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -181,13 +181,11 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o array_init(&intern->array); } - while (parent) { - if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator || parent == spl_ce_ArrayObject) { - break; - } - parent = parent->parent; - inherited = 1; - } + parent = zend_class_entry_get_root(class_type); + + inherited = class_type != spl_ce_ArrayIterator + && class_type != spl_ce_ArrayObject + && class_type != spl_ce_RecursiveArrayIterator; ZEND_ASSERT(parent); @@ -232,7 +230,7 @@ static zend_object *spl_array_object_clone(zend_object *old_object) { zend_object *new_object; - new_object = spl_array_object_new_ex(old_object->ce, old_object, 1); + new_object = spl_array_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -414,7 +412,7 @@ static zval *spl_array_read_dimension_ex(int check_inherited, zend_object *objec ZVAL_UNDEF(&tmp); offset = &tmp; } - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_get, "offsetGet", rv, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_get, "offsetGet", rv, offset); if (!Z_ISUNDEF_P(rv)) { return rv; @@ -423,7 +421,7 @@ static zval *spl_array_read_dimension_ex(int check_inherited, zend_object *objec } } - ret = spl_array_get_dimension_ptr(check_inherited, intern, object->ce->name, offset, type); + ret = spl_array_get_dimension_ptr(check_inherited, intern, OBJ_CE(object)->name, offset, type); /* When in a write context, * ZE has to be fooled into thinking this is in a reference set @@ -473,7 +471,7 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_method_with_2_params(object, object->ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value); + zend_call_method_with_2_params(object, OBJ_CE(object), &intern->fptr_offset_set, "offsetSet", NULL, offset, value); return; } @@ -497,7 +495,7 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec } if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_W); + zend_illegal_container_offset(OBJ_NAME(object), offset, BP_VAR_W); zval_ptr_dtor(value); return; } @@ -528,7 +526,7 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec spl_hash_key key; if (check_inherited && intern->fptr_offset_del) { - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_del, "offsetUnset", NULL, offset); return; } @@ -538,7 +536,7 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec } if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_UNSET); + zend_illegal_container_offset(OBJ_NAME(object), offset, BP_VAR_UNSET); return; } @@ -587,7 +585,7 @@ static bool spl_array_has_dimension_ex(bool check_inherited, zend_object *object zval rv, *value = NULL, *tmp; if (check_inherited && intern->fptr_offset_has) { - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_has, "offsetExists", &rv, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_has, "offsetExists", &rv, offset); if (!zend_is_true(&rv)) { zval_ptr_dtor(&rv); @@ -608,7 +606,7 @@ static bool spl_array_has_dimension_ex(bool check_inherited, zend_object *object spl_hash_key key; if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_IS); + zend_illegal_container_offset(OBJ_CE(object)->name, offset, BP_VAR_IS); return 0; } @@ -846,7 +844,7 @@ static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *na return NULL; } ZVAL_STR(&member, name); - return spl_array_get_dimension_ptr(1, intern, object->ce->name, &member, type); + return spl_array_get_dimension_ptr(1, intern, OBJ_CE(object)->name, &member, type); } return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } /* }}} */ @@ -972,7 +970,7 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar if (handler != zend_std_get_properties) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Overloaded object of type %s is not compatible with %s", - ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(intern->std.ce->name)); + ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(OBJ_CE(&intern->std)->name)); return; } zval_ptr_dtor(&intern->array); @@ -1138,7 +1136,7 @@ static zend_result spl_array_object_count_elements(zend_object *object, zend_lon if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (Z_TYPE(rv) != IS_UNDEF) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 528dc5ecc4720..330f3ac4fae70 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -379,7 +379,7 @@ static zend_result spl_filesystem_file_open(spl_filesystem_object *intern, bool intern->u.file.enclosure = '"'; intern->u.file.escape = (unsigned char) '\\'; - intern->u.file.func_getCurr = zend_hash_str_find_ptr(&intern->std.ce->function_table, "getcurrentline", sizeof("getcurrentline") - 1); + intern->u.file.func_getCurr = zend_hash_str_find_ptr(&OBJ_CE(&intern->std)->function_table, "getcurrentline", sizeof("getcurrentline") - 1); return SUCCESS; } /* }}} */ @@ -398,7 +398,7 @@ static zend_object *spl_filesystem_object_clone(zend_object *old_object) spl_filesystem_object *source; source = spl_filesystem_from_obj(old_object); - new_object = spl_filesystem_object_new_ex(old_object->ce); + new_object = spl_filesystem_object_new_ex(OBJ_CE(old_object)); intern = spl_filesystem_from_obj(new_object); intern->flags = source->flags; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 74dc3b8c99fa1..976c2fbf6340c 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -309,10 +309,9 @@ static void spl_dllist_object_free_storage(zend_object *object) /* {{{ */ static zend_object *spl_dllist_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) /* {{{ */ { spl_dllist_object *intern; - zend_class_entry *parent = class_type; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_dllist_object), parent); + intern = zend_object_alloc(sizeof(spl_dllist_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -342,43 +341,34 @@ static zend_object *spl_dllist_object_new_ex(zend_class_entry *class_type, zend_ SPL_LLIST_CHECK_ADDREF(intern->traverse_pointer); } - while (parent) { - if (parent == spl_ce_SplStack) { - intern->flags |= (SPL_DLLIST_IT_FIX | SPL_DLLIST_IT_LIFO); - } else if (parent == spl_ce_SplQueue) { - intern->flags |= SPL_DLLIST_IT_FIX; - } - - if (parent == spl_ce_SplDoublyLinkedList) { - break; - } - - parent = parent->parent; - inherited = 1; + inherited = class_type != spl_ce_SplDoublyLinkedList + && class_type != spl_ce_SplStack && class_type != spl_ce_SplQueue; + if (instanceof_function(class_type, spl_ce_SplStack)) { + intern->flags |= (SPL_DLLIST_IT_FIX | SPL_DLLIST_IT_LIFO); + } else if (instanceof_function(class_type, spl_ce_SplQueue)) { + intern->flags |= SPL_DLLIST_IT_FIX; } - ZEND_ASSERT(parent); - if (inherited) { intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1); - if (intern->fptr_offset_get->common.scope == parent) { + if (intern->fptr_offset_get->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_get = NULL; } intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1); - if (intern->fptr_offset_set->common.scope == parent) { + if (intern->fptr_offset_set->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_set = NULL; } intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1); - if (intern->fptr_offset_has->common.scope == parent) { + if (intern->fptr_offset_has->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_has = NULL; } intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1); - if (intern->fptr_offset_del->common.scope == parent) { + if (intern->fptr_offset_del->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_del = NULL; } /* Find count() method */ intern->fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); - if (intern->fptr_count->common.scope == parent) { + if (intern->fptr_count->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_count = NULL; } } @@ -395,7 +385,7 @@ static zend_object *spl_dllist_object_new(zend_class_entry *class_type) /* {{{ * static zend_object *spl_dllist_object_clone(zend_object *old_object) /* {{{ */ { - zend_object *new_object = spl_dllist_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_dllist_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -409,7 +399,7 @@ static zend_result spl_dllist_object_count_elements(zend_object *object, zend_lo if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (!Z_ISUNDEF(rv)) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index dbdc08c5a87ca..6a2ee7ce50b40 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -42,7 +42,7 @@ ZEND_GET_MODULE(spl_fixedarray) /* Check if the object is an instance of a subclass of SplFixedArray that overrides method's implementation. * Expect subclassing SplFixedArray to be rare and check that first. */ -#define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED((object)->ce != spl_ce_SplFixedArray && (object)->ce->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray) +#define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED(OBJ_CE(object) != spl_ce_SplFixedArray && OBJ_CE(object)->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray) typedef struct _spl_fixedarray { zend_long size; @@ -237,7 +237,7 @@ static HashTable* spl_fixedarray_object_get_properties_for(zend_object *obj, zen * SplFixedArray can be subclassed or have dynamic properties (With or without AllowDynamicProperties in subclasses). * Instances of subclasses with declared properties may have properties but not yet have a property table. */ - HashTable *source_properties = obj->properties ? obj->properties : (obj->ce->default_properties_count ? zend_std_get_properties(obj) : NULL); + HashTable *source_properties = obj->properties ? obj->properties : (OBJ_CE(obj)->default_properties_count ? zend_std_get_properties(obj) : NULL); const zend_long size = intern->array.size; if (size == 0 && (!source_properties || !zend_hash_num_elements(source_properties))) { @@ -277,10 +277,9 @@ static void spl_fixedarray_object_free_storage(zend_object *object) static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig) { spl_fixedarray_object *intern; - zend_class_entry *parent = class_type; bool inherited = false; - intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent); + intern = zend_object_alloc(sizeof(spl_fixedarray_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -290,21 +289,12 @@ static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, z spl_fixedarray_copy_ctor(&intern->array, &other->array); } - while (parent) { - if (parent == spl_ce_SplFixedArray) { - break; - } - - parent = parent->parent; - inherited = true; - } - - ZEND_ASSERT(parent); + inherited = class_type != spl_ce_SplFixedArray; if (UNEXPECTED(inherited)) { /* Find count() method */ zend_function *fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); - if (fptr_count->common.scope == parent) { + if (fptr_count->common.scope == spl_ce_SplFixedArray) { fptr_count = NULL; } intern->fptr_count = fptr_count; @@ -320,7 +310,7 @@ static zend_object *spl_fixedarray_new(zend_class_entry *class_type) static zend_object *spl_fixedarray_object_clone(zend_object *old_object) { - zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_fixedarray_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -397,7 +387,7 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset); if (!Z_ISUNDEF_P(rv)) { return rv; } @@ -445,7 +435,7 @@ static void spl_fixedarray_object_write_dimension(zend_object *object, zval *off ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_known_instance_method_with_2_params(object->ce->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value); + zend_call_known_instance_method_with_2_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value); return; } @@ -474,7 +464,7 @@ static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object * static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset) { if (UNEXPECTED(HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetunset))) { - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset); return; } @@ -507,7 +497,7 @@ static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetexists)) { zval rv; - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset); bool result = zend_is_true(&rv); zval_ptr_dtor(&rv); return result; diff --git a/ext/spl/spl_functions.c b/ext/spl/spl_functions.c index 0fcf389667355..25d81083584d0 100644 --- a/ext/spl/spl_functions.c +++ b/ext/spl/spl_functions.c @@ -70,9 +70,8 @@ void spl_add_classes(zend_class_entry *pce, zval *list, bool sub, int allow, int spl_add_class_name(list, pce, allow, ce_flags); if (sub) { spl_add_interfaces(list, pce, allow, ce_flags); - while (pce->parent) { - pce = pce->parent; - spl_add_classes(pce, list, sub, allow, ce_flags); + for (uint32_t i = 0; i < pce->num_parents; i++) { + spl_add_classes(pce->parents[i]->ce, list, sub, allow, ce_flags); } } } diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 5d61741425185..048980257628d 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -124,7 +124,7 @@ static void spl_ptr_heap_pqueue_elem_ctor(void *elem) { /* {{{ */ static zend_result spl_ptr_heap_cmp_cb_helper(zval *object, spl_heap_object *heap_object, zval *a, zval *b, zend_long *result) { /* {{{ */ zval zresult; - zend_call_method_with_2_params(Z_OBJ_P(object), heap_object->std.ce, &heap_object->fptr_cmp, "compare", &zresult, a, b); + zend_call_method_with_2_params(Z_OBJ_P(object), OBJ_CE(&heap_object->std), &heap_object->fptr_cmp, "compare", &zresult, a, b); if (EG(exception)) { return FAILURE; @@ -406,10 +406,10 @@ static void spl_heap_object_free_storage(zend_object *object) /* {{{ */ static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) /* {{{ */ { spl_heap_object *intern; - zend_class_entry *parent = class_type; + zend_class_entry *parent; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_heap_object), parent); + intern = zend_object_alloc(sizeof(spl_heap_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -430,23 +430,23 @@ static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zend_ob return &intern->std; } - while (parent) { - if (parent == spl_ce_SplPriorityQueue) { - intern->heap = spl_ptr_heap_init(spl_ptr_pqueue_elem_cmp, spl_ptr_heap_pqueue_elem_ctor, spl_ptr_heap_pqueue_elem_dtor, sizeof(spl_pqueue_elem)); - intern->flags = SPL_PQUEUE_EXTR_DATA; - break; - } - - if (parent == spl_ce_SplMinHeap || parent == spl_ce_SplMaxHeap - || parent == spl_ce_SplHeap) { - intern->heap = spl_ptr_heap_init( - parent == spl_ce_SplMinHeap ? spl_ptr_heap_zval_min_cmp : spl_ptr_heap_zval_max_cmp, - spl_ptr_heap_zval_ctor, spl_ptr_heap_zval_dtor, sizeof(zval)); - break; + parent = zend_class_entry_get_root(class_type); + inherited = class_type != spl_ce_SplPriorityQueue + && class_type != spl_ce_SplMinHeap && spl_ce_SplMaxHeap; + if (parent == spl_ce_SplPriorityQueue) { + intern->heap = spl_ptr_heap_init(spl_ptr_pqueue_elem_cmp, spl_ptr_heap_pqueue_elem_ctor, spl_ptr_heap_pqueue_elem_dtor, sizeof(spl_pqueue_elem)); + intern->flags = SPL_PQUEUE_EXTR_DATA; + } else { + ZEND_ASSERT(parent == spl_ce_SplHeap); + if (instanceof_function(class_type, spl_ce_SplMinHeap)) { + parent = spl_ce_SplMinHeap; + } else if (instanceof_function(class_type, spl_ce_SplMaxHeap)) { + parent = spl_ce_SplMaxHeap; } - parent = parent->parent; - inherited = 1; + intern->heap = spl_ptr_heap_init( + parent == spl_ce_SplMinHeap ? spl_ptr_heap_zval_min_cmp : spl_ptr_heap_zval_max_cmp, + spl_ptr_heap_zval_ctor, spl_ptr_heap_zval_dtor, sizeof(zval)); } ZEND_ASSERT(parent); @@ -475,7 +475,7 @@ static zend_object *spl_heap_object_new(zend_class_entry *class_type) /* {{{ */ static zend_object *spl_heap_object_clone(zend_object *old_object) /* {{{ */ { - zend_object *new_object = spl_heap_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_heap_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -489,7 +489,7 @@ static zend_result spl_heap_object_count_elements(zend_object *object, zend_long if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (!Z_ISUNDEF(rv)) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 482fb80acab8b..9eb57beabd62d 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -929,7 +929,7 @@ static zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_st zval *zobj; if (!object->iterators) { - zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name)); + zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL(OBJ_CE(*zobject)->name)); return NULL; } zobj = &object->iterators[level].zobject; @@ -1656,7 +1656,7 @@ static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern) zval retval; while (spl_dual_it_fetch(intern, 1) == SUCCESS) { - zend_call_method_with_0_params(Z_OBJ_P(zthis), intern->std.ce, NULL, "accept", &retval); + zend_call_method_with_0_params(Z_OBJ_P(zthis), OBJ_CE(&intern->std), NULL, "accept", &retval); if (Z_TYPE(retval) != IS_UNDEF) { if (zend_is_true(&retval)) { zval_ptr_dtor(&retval); @@ -1887,7 +1887,7 @@ PHP_METHOD(RegexIterator, accept) break; case REGIT_MODE_REPLACE: { - zval *replacement = zend_read_property(intern->std.ce, Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv); + zval *replacement = zend_read_property(OBJ_CE(&intern->std), Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv); zend_string *replacement_str = zval_try_get_string(replacement); if (UNEXPECTED(!replacement_str)) { diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index b996764c1aca1..75880046258c7 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -88,7 +88,7 @@ static zend_result spl_object_storage_get_hash(zend_hash_key *key, spl_SplObject zval rv; ZVAL_OBJ(¶m, obj); zend_call_method_with_1_params( - &intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, ¶m); + &intern->std, OBJ_CE(&intern->std), &intern->fptr_get_hash, "getHash", &rv, ¶m); if (!Z_ISUNDEF(rv)) { if (Z_TYPE(rv) == IS_STRING) { key->key = Z_STR(rv); @@ -254,9 +254,8 @@ static void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjec static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend_object *orig) /* {{{ */ { spl_SplObjectStorage *intern; - zend_class_entry *parent = class_type; - intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(parent)); + intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(class_type)); memset(intern, 0, sizeof(spl_SplObjectStorage) - sizeof(zval)); intern->pos = 0; @@ -265,35 +264,29 @@ static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend zend_hash_init(&intern->storage, 0, NULL, spl_object_storage_dtor, 0); - while (parent) { - if (parent == spl_ce_SplObjectStorage) { - /* Possible optimization: Cache these results with a map from class entry to IS_NULL/IS_PTR. - * Or maybe just a single item with the result for the most recently loaded subclass. */ - if (class_type != spl_ce_SplObjectStorage) { - zend_function *get_hash = zend_hash_str_find_ptr(&class_type->function_table, "gethash", sizeof("gethash") - 1); - if (get_hash->common.scope != spl_ce_SplObjectStorage) { - intern->fptr_get_hash = get_hash; - } - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetget) || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetexists)) { - intern->flags |= SOS_OVERRIDDEN_READ_DIMENSION; - } - - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetset)) { - intern->flags |= SOS_OVERRIDDEN_WRITE_DIMENSION; - } - - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetunset)) { - intern->flags |= SOS_OVERRIDDEN_UNSET_DIMENSION; - } - } - break; + if (class_type != spl_ce_SplObjectStorage + && zend_class_entry_get_root(class_type) == spl_ce_SplObjectStorage) { + /* Possible optimization: Cache these results with a map from class entry to IS_NULL/IS_PTR. + * Or maybe just a single item with the result for the most recently loaded subclass. */ + zend_function *get_hash = zend_hash_str_find_ptr(&class_type->function_table, "gethash", sizeof("gethash") - 1); + if (get_hash->common.scope != spl_ce_SplObjectStorage) { + intern->fptr_get_hash = get_hash; + } + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetget) || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetexists)) { + intern->flags |= SOS_OVERRIDDEN_READ_DIMENSION; + } + + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetset)) { + intern->flags |= SOS_OVERRIDDEN_WRITE_DIMENSION; } - parent = parent->parent; + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetunset)) { + intern->flags |= SOS_OVERRIDDEN_UNSET_DIMENSION; + } } if (orig) { @@ -310,7 +303,7 @@ static zend_object *spl_object_storage_clone(zend_object *old_object) { zend_object *new_object; - new_object = spl_object_storage_new_ex(old_object->ce, old_object); + new_object = spl_object_storage_new_ex(OBJ_CE(old_object), old_object); zend_objects_clone_members(new_object, old_object); @@ -382,15 +375,9 @@ static int spl_object_storage_compare_info(zval *e1, zval *e2) /* {{{ */ static int spl_object_storage_compare_objects(zval *o1, zval *o2) /* {{{ */ { - zend_object *zo1; - zend_object *zo2; - ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2); - zo1 = (zend_object *)Z_OBJ_P(o1); - zo2 = (zend_object *)Z_OBJ_P(o2); - - if (zo1->ce != spl_ce_SplObjectStorage || zo2->ce != spl_ce_SplObjectStorage) { + if (Z_OBJCE_P(o1) != spl_ce_SplObjectStorage || Z_OBJCE_P(o2) != spl_ce_SplObjectStorage) { return ZEND_UNCOMPARABLE; } @@ -1151,7 +1138,7 @@ PHP_METHOD(MultipleIterator, rewind) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_rewind, it, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_rewind, it, NULL); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1172,7 +1159,7 @@ PHP_METHOD(MultipleIterator, next) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_next, it, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_next, it, NULL); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1201,7 +1188,7 @@ PHP_METHOD(MultipleIterator, valid) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_valid, it, &retval); if (!Z_ISUNDEF(retval)) { valid = (Z_TYPE(retval) == IS_TRUE); @@ -1239,7 +1226,7 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_valid, it, &retval); if (!Z_ISUNDEF(retval)) { valid = Z_TYPE(retval) == IS_TRUE; @@ -1250,9 +1237,9 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ if (valid) { if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) { - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_current, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_current, it, &retval); } else { - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_key, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_key, it, &retval); } if (Z_ISUNDEF(retval)) { zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0); diff --git a/ext/standard/array.c b/ext/standard/array.c index eee260f224319..2f7a2298646ec 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -678,8 +678,8 @@ PHP_FUNCTION(count) } } /* if not and the object implements Countable we call its count() method */ - if (instanceof_function(zobj->ce, zend_ce_countable)) { - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + if (instanceof_function(OBJ_CE(zobj), zend_ce_countable)) { + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); if (Z_TYPE(retval) != IS_UNDEF) { RETVAL_LONG(zval_get_long(&retval)); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 4bf49c226ca39..360995d491c44 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -39,6 +39,7 @@ #include "ext/standard/php_dns.h" #include "ext/standard/php_uuencode.h" #include "ext/standard/crc32_x86.h" +#include "zend_types.h" #ifdef PHP_WIN32 #include "win32/php_win32_globals.h" @@ -1518,7 +1519,7 @@ PHP_FUNCTION(forward_static_call) zval retval; zend_fcall_info fci; zend_fcall_info_cache fci_cache; - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_FUNC(fci, fci_cache) @@ -1534,8 +1535,8 @@ PHP_FUNCTION(forward_static_call) called_scope = zend_get_called_scope(execute_data); if (called_scope && fci_cache.calling_scope && - instanceof_function(called_scope, fci_cache.calling_scope)) { - fci_cache.called_scope = called_scope; + instanceof_function(called_scope->ce, fci_cache.calling_scope)) { + fci_cache.called_scope = called_scope->ce; } if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { @@ -1554,7 +1555,7 @@ PHP_FUNCTION(forward_static_call_array) HashTable *params; zend_fcall_info fci; zend_fcall_info_cache fci_cache; - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_FUNC(fci, fci_cache) @@ -1567,8 +1568,8 @@ PHP_FUNCTION(forward_static_call_array) called_scope = zend_get_called_scope(execute_data); if (called_scope && fci_cache.calling_scope && - instanceof_function(called_scope, fci_cache.calling_scope)) { - fci_cache.called_scope = called_scope; + instanceof_function(called_scope->ce, fci_cache.calling_scope)) { + fci_cache.called_scope = called_scope->ce; } if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 31e5c57435639..e1eb5ab18fe48 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -796,7 +796,7 @@ PHP_FUNCTION(get_browser) if (return_array) { RETVAL_ARR(agent_ht); } else { - object_and_properties_init(return_value, zend_standard_class_def, agent_ht); + object_and_properties_init(return_value, ZEND_CE_TO_REF(zend_standard_class_def), agent_ht); } HashTable *target_ht = return_array ? Z_ARRVAL_P(return_value) : Z_OBJPROP_P(return_value); diff --git a/ext/standard/incomplete_class.c b/ext/standard/incomplete_class.c index 228e03fbe863e..61586c6f08a50 100644 --- a/ext/standard/incomplete_class.c +++ b/ext/standard/incomplete_class.c @@ -100,7 +100,7 @@ static zend_object *php_create_incomplete_object(zend_class_entry *class_type) { zend_object *object; - object = zend_objects_new( class_type); + object = zend_objects_new(class_type); object->handlers = &php_incomplete_object_handlers; object_properties_init(object, class_type); diff --git a/ext/standard/type.c b/ext/standard/type.c index 6cd72fc744ca4..1b561439ea650 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -61,11 +61,11 @@ PHP_FUNCTION(get_debug_type) case IS_ARRAY: RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_ARRAY)); case IS_OBJECT: - if (Z_OBJ_P(arg)->ce->ce_flags & ZEND_ACC_ANON_CLASS) { - name = ZSTR_VAL(Z_OBJ_P(arg)->ce->name); + if (Z_OBJCE_P(arg)->ce_flags & ZEND_ACC_ANON_CLASS) { + name = ZSTR_VAL(Z_OBJCE_P(arg)->name); RETURN_NEW_STR(zend_string_init(name, strlen(name), 0)); } else { - RETURN_STR_COPY(Z_OBJ_P(arg)->ce->name); + RETURN_STR_COPY(Z_OBJCE_P(arg)->name); } case IS_RESOURCE: name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg)); diff --git a/ext/standard/var.c b/ext/standard/var.c index 2975f165789e5..b922e1db0cbe5 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -23,11 +23,14 @@ #include "php.h" #include "php_string.h" #include "php_var.h" +#include "zend_compile.h" #include "zend_smart_str.h" #include "basic_functions.h" #include "php_incomplete_class.h" #include "zend_enum.h" #include "zend_exceptions.h" +#include "zend_string.h" +#include "zend_types.h" /* }}} */ struct php_serialize_data { @@ -76,7 +79,7 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze if (Z_TYPE_P(zv) == IS_UNDEF) { ZEND_ASSERT(ZEND_TYPE_IS_SET(prop_info->type)); - zend_string *type_str = zend_type_to_string(prop_info->type); + zend_string *type_str = zend_type_to_string(prop_info->type, prop_info->ce); php_printf("%*cuninitialized(%s)\n", level + 1, ' ', ZSTR_VAL(type_str)); zend_string_release(type_str); @@ -163,8 +166,27 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG); class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); - php_printf("%sobject(%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); + + zend_string *types = zend_empty_string; + if (Z_OBJ_P(struc)->cr->args.num_types) { + zend_type_list *args = &Z_OBJ_P(struc)->cr->args; + smart_str str = {0}; + smart_str_appendc(&str, '<'); + for (uint32_t i = 0; i < args->num_types; i++) { + if (i > 0) { + smart_str_appendc(&str, ','); + } + zend_string *t = zend_type_to_string(args->types[i], NULL); + smart_str_append(&str, t); + zend_string_release(t); + } + smart_str_appendc(&str, '>'); + types = smart_str_extract(&str); + } + + php_printf("%sobject(%s%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), ZSTR_VAL(types), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); zend_string_release_ex(class_name, 0); + zend_string_release(types); if (myht) { zend_ulong num; @@ -266,7 +288,7 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z ZEND_PUTS("]=>\n"); } if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) { - zend_string *type_str = zend_type_to_string(prop_info->type); + zend_string *type_str = zend_type_to_string(prop_info->type, prop_info->ce); php_printf("%*cuninitialized(%s)\n", level + 1, ' ', ZSTR_VAL(type_str)); zend_string_release(type_str); @@ -780,7 +802,7 @@ static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function * if (Z_TYPE(retval) != IS_ARRAY) { zval_ptr_dtor(&retval); - php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(obj->ce->name)); + php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(OBJ_NAME(obj))); return NULL; } @@ -1171,7 +1193,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ && Z_OBJ_HT_P(struc)->get_properties == zend_std_get_properties) { /* Optimized version without rebulding properties HashTable */ zend_object *obj = Z_OBJ_P(struc); - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_property_info *prop_info; zval *prop; int i; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index a050fb5f74a70..19f876fee969d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -270,9 +270,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) ZVAL_UNDEF(&fci.function_name); fci_cache.function_handler = zend_hash_find_ptr( - &fci.object->ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); + &OBJ_CE(fci.object)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); fci_cache.object = fci.object; - fci_cache.called_scope = fci.object->ce; + fci_cache.called_scope = OBJ_CE(fci.object); BG(serialize_lock)++; if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { @@ -626,7 +626,7 @@ declared_property: ZVAL_NULL(data); } else { /* Unusual override of dynamic property */ - int ret = is_property_visibility_changed(obj->ce, &key); + int ret = is_property_visibility_changed(OBJ_CE(obj), &key); if (ret > 0) { goto second_try; @@ -638,17 +638,17 @@ declared_property: } } } else { - int ret = is_property_visibility_changed(obj->ce, &key); + int ret = is_property_visibility_changed(OBJ_CE(obj), &key); if (EXPECTED(!ret)) { - if (UNEXPECTED(obj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + if (UNEXPECTED(OBJ_CE(obj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", - ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + ZSTR_VAL(OBJ_NAME(obj)), zend_get_unmangled_property_name(Z_STR_P(&key))); zval_ptr_dtor_str(&key); goto failure; - } else if (!(obj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(obj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + ZSTR_VAL(OBJ_NAME(obj)), zend_get_unmangled_property_name(Z_STR_P(&key))); if (EG(exception)) { zval_ptr_dtor_str(&key); goto failure; @@ -689,7 +689,7 @@ second_try: } if (UNEXPECTED(info)) { - if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { + if (!zend_verify_prop_assignable_by_ref(obj, info, data, /* strict */ 1)) { zval_ptr_dtor(data); ZVAL_UNDEF(data); goto failure; @@ -1184,10 +1184,12 @@ object ":" uiv ":" ["] { do { zend_string *lc_name; + ce = NULL; if (!(*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { + ce = class_ref->ce; break; } } @@ -1205,8 +1207,8 @@ object ":" uiv ":" ["] { } if ((*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { zend_string_release_ex(lc_name, 0); break; } diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index 75cc99d7b84c6..d8d575e39a65f 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -100,7 +100,7 @@ PHP_METHOD(PhpToken, tokenize) Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END(); - token_class = zend_get_called_scope(execute_data); + token_class = zend_get_called_scope(execute_data)->ce; /* Check construction preconditions in advance, so these are not repeated for each token. */ if (token_class->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index b6d27d7a506f2..e3f7bc16f569c 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -576,7 +576,7 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e ZVAL_NULL(&property_classProp_default_value); zend_string *property_classProp_name = zend_string_init("classProp", sizeof("classProp") - 1, 1); zend_string *property_classProp_class_stdClass = zend_string_init("stdClass", sizeof("stdClass")-1, 1); - zend_declare_typed_property(class_entry, property_classProp_name, &property_classProp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classProp_class_stdClass, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_classProp_name, &property_classProp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classProp_class_stdClass, 0, MAY_BE_NULL)); zend_string_release(property_classProp_name); zval property_classUnionProp_default_value; @@ -586,8 +586,8 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_string *property_classUnionProp_class_Iterator = zend_string_init("Iterator", sizeof("Iterator") - 1, 1); zend_type_list *property_classUnionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classUnionProp_type_list->num_types = 2; - property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_stdClass, 0, 0); - property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Iterator, 0, 0); + property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_stdClass, 0, 0); + property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Iterator, 0, 0); zend_type property_classUnionProp_type = ZEND_TYPE_INIT_UNION(property_classUnionProp_type_list, MAY_BE_NULL); zend_declare_typed_property(class_entry, property_classUnionProp_name, &property_classUnionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classUnionProp_type); zend_string_release(property_classUnionProp_name); @@ -599,8 +599,8 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_string *property_classIntersectionProp_class_Countable = zend_string_init("Countable", sizeof("Countable") - 1, 1); zend_type_list *property_classIntersectionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classIntersectionProp_type_list->num_types = 2; - property_classIntersectionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classIntersectionProp_class_Traversable, 0, 0); - property_classIntersectionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classIntersectionProp_class_Countable, 0, 0); + property_classIntersectionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classIntersectionProp_class_Traversable, 0, 0); + property_classIntersectionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classIntersectionProp_class_Countable, 0, 0); zend_type property_classIntersectionProp_type = ZEND_TYPE_INIT_INTERSECTION(property_classIntersectionProp_type_list, 0); zend_declare_typed_property(class_entry, property_classIntersectionProp_name, &property_classIntersectionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classIntersectionProp_type); zend_string_release(property_classIntersectionProp_name); @@ -708,8 +708,8 @@ static zend_class_entry *register_class__ZendTestTrait(void) zend_string *property_classUnionProp_class_Countable = zend_string_init("Countable", sizeof("Countable") - 1, 1); zend_type_list *property_classUnionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classUnionProp_type_list->num_types = 2; - property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Traversable, 0, 0); - property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Countable, 0, 0); + property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Traversable, 0, 0); + property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Countable, 0, 0); zend_type property_classUnionProp_type = ZEND_TYPE_INIT_UNION(property_classUnionProp_type_list, 0); zend_declare_typed_property(class_entry, property_classUnionProp_name, &property_classUnionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classUnionProp_type); zend_string_release(property_classUnionProp_name); @@ -983,7 +983,7 @@ static zend_class_entry *register_class_ZendTestNS2_Foo(void) ZVAL_UNDEF(&property_foo_default_value); zend_string *property_foo_name = zend_string_init("foo", sizeof("foo") - 1, 1); zend_string *property_foo_class_ZendTestNS2_ZendSubNS_Foo = zend_string_init("ZendTestNS2\\ZendSubNS\\Foo", sizeof("ZendTestNS2\\ZendSubNS\\Foo")-1, 1); - zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_foo_class_ZendTestNS2_ZendSubNS_Foo, 0, 0)); + zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_foo_class_ZendTestNS2_ZendSubNS_Foo, 0, 0)); zend_string_release(property_foo_name); return class_entry; diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c index 0a1e7570a493c..f98585b231623 100644 --- a/sapi/phpdbg/phpdbg_info.c +++ b/sapi/phpdbg/phpdbg_info.c @@ -402,13 +402,11 @@ PHPDBG_INFO(classes) /* {{{ */ ZEND_HASH_PACKED_FOREACH_PTR(&classes, ce) { phpdbg_print_class_name(ce); - if (ce->parent) { - zend_class_entry *pce; - pce = ce->parent; - do { + if (ce->num_parents) { + for (uint32_t i = 0; i < ce->num_parents; i++) { phpdbg_out("|-------- "); - phpdbg_print_class_name(pce); - } while ((pce = pce->parent)); + phpdbg_print_class_name(ce->parents[i]->ce); + } } if (ce->info.user.filename) { diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index b242e5345a180..9558b29158ab5 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -716,7 +716,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */ EG(exception) = NULL; - zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); + zend_call_known_instance_method_with_0_params(OBJ_CE(ex)->__tostring, ex, &tmp); file = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_FILE), /* silent */ true, &rv)); line = zval_get_long(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_LINE), /* silent */ true, &rv)); @@ -729,7 +729,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */ msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), /* silent */ true, &rv)); } - phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(ex->ce->name), ZSTR_VAL(file), line); + phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(OBJ_CE(ex)->name), ZSTR_VAL(file), line); zend_string_release(file); phpdbg_writeln("%s", ZSTR_VAL(msg)); zend_string_release(msg); @@ -1032,7 +1032,7 @@ PHPDBG_COMMAND(generator) /* {{{ */ if (param) { i = param->num; zend_object **obj = EG(objects_store).object_buckets + i; - if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && (*obj)->ce == zend_ce_generator) { + if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && OBJ_CE(*obj) == zend_ce_generator) { zend_generator *gen = (zend_generator *) *obj; if (gen->execute_data) { if (zend_generator_get_current(gen)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) { @@ -1049,7 +1049,7 @@ PHPDBG_COMMAND(generator) /* {{{ */ } else { for (i = 0; i < EG(objects_store).top; i++) { zend_object *obj = EG(objects_store).object_buckets[i]; - if (obj && IS_OBJ_VALID(obj) && obj->ce == zend_ce_generator) { + if (obj && IS_OBJ_VALID(obj) && OBJ_CE(obj) == zend_ce_generator) { zend_generator *gen = (zend_generator *) obj, *current = zend_generator_get_current(gen); if (gen->execute_data) { zend_string *s = phpdbg_compile_stackframe(gen->execute_data); @@ -1700,7 +1700,7 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ zend_string *msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(exception), exception, ZSTR_KNOWN(ZEND_STR_MESSAGE), /* silent */ true, &rv)); phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT ": %.*s", - ZSTR_VAL(exception->ce->name), ZSTR_VAL(file), line, + ZSTR_VAL(OBJ_NAME(exception)), ZSTR_VAL(file), line, ZSTR_LEN(msg) < 80 ? (int) ZSTR_LEN(msg) : 80, ZSTR_VAL(msg)); zend_string_release(msg); zend_string_release(file); diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 964d82ef6c8d5..439e3beda84eb 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -638,7 +638,7 @@ PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_obj CACHE_PTR(cur->extended_value & ~ZEND_LAST_CATCH, ce); } - if (ce == exception->ce || (ce && instanceof_function(exception->ce, ce))) { + if (ce == OBJ_CE(exception) || (ce && instanceof_function(OBJ_CE(exception), ce))) { return 1; }