diff options
Diffstat (limited to 'ext/opcache')
176 files changed, 6422 insertions, 3148 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 4168730e30..3327ec86df 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -81,37 +81,37 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int #define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)] #define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline -#define convert_to_string_safe(v) \ - if (Z_TYPE_P((v)) == IS_NULL) { \ - ZVAL_STRINGL((v), "", 0); \ - } else { \ - convert_to_string((v)); \ - } - static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b) { zend_op *opcodes = op_array->opcodes; - while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP) { + do { /* check if NOP breaks incorrect smart branch */ if (b->len == 2 - && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ - || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ) - && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST)) + && (opcodes[b->start + 1].opcode == ZEND_JMPZ + || opcodes[b->start + 1].opcode == ZEND_JMPNZ) + && (opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST)) && b->start > 0 - && zend_is_smart_branch(op_array->opcodes + b->start - 1)) { + && zend_is_smart_branch(opcodes + b->start - 1)) { break; } b->start++; b->len--; - } + } while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP); } static void strip_nops(zend_op_array *op_array, zend_basic_block *b) { uint32_t i, j; - strip_leading_nops(op_array, b); + if (b->len == 0) { + return; + } + + if (op_array->opcodes[b->start].opcode == ZEND_NOP) { + strip_leading_nops(op_array, b); + } + if (b->len == 0) { return; } @@ -168,14 +168,20 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array zend_op *opline, *src; zend_op *end, *last_op = NULL; - /* remove leading NOPs */ - strip_leading_nops(op_array, block); + if (block->len == 0) { + return; + } + + if (op_array->opcodes[block->start].opcode == ZEND_NOP) { + /* remove leading NOPs */ + strip_leading_nops(op_array, block); + } opline = op_array->opcodes + block->start; end = opline + block->len; while (opline < end) { /* Constant Propagation: strip X = QM_ASSIGN(const) */ - if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) && + if (opline->op1_type == IS_TMP_VAR && opline->opcode != ZEND_FREE) { src = VAR_SOURCE(opline->op1); if (src && @@ -184,7 +190,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array ) { znode_op op1 = opline->op1; if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) { - zend_optimizer_remove_live_range(op_array, op1.var); COPY_NODE(opline->result, opline->op1); COPY_NODE(opline->op1, src->op1); VAR_SOURCE(op1) = NULL; @@ -194,7 +199,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array zval c; ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); if (zend_optimizer_update_op1_const(op_array, opline, &c)) { - zend_optimizer_remove_live_range(op_array, op1.var); VAR_SOURCE(op1) = NULL; literal_dtor(&ZEND_OP1_LITERAL(src)); MAKE_NOP(src); @@ -253,7 +257,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } /* Constant Propagation: strip X = QM_ASSIGN(const) */ - if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { + if (opline->op2_type == IS_TMP_VAR) { src = VAR_SOURCE(opline->op2); if (src && src->opcode == ZEND_QM_ASSIGN && @@ -264,7 +268,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); if (zend_optimizer_update_op2_const(op_array, opline, &c)) { - zend_optimizer_remove_live_range(op_array, op2.var); VAR_SOURCE(op2) = NULL; literal_dtor(&ZEND_OP1_LITERAL(src)); MAKE_NOP(src); @@ -282,7 +285,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src->opcode == ZEND_CAST && src->extended_value == IS_STRING) { /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ - zend_optimizer_remove_live_range(op_array, opline->op1.var); VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); @@ -302,10 +304,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array int l, old_len; if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP1_LITERAL(opline)); + convert_to_string(&ZEND_OP1_LITERAL(opline)); } if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP1_LITERAL(last_op)); + convert_to_string(&ZEND_OP1_LITERAL(last_op)); } old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op)); l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline)); @@ -443,8 +445,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array break; case ZEND_CASE: + case ZEND_COPY_TMP: if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - /* CASE variable will be deleted later by FREE, so we can't optimize it */ + /* Variable will be deleted later by FREE, so we can't optimize it */ Tsource[VAR_NUM(opline->op1.var)] = NULL; break; } @@ -684,10 +687,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array int l, old_len; if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); + convert_to_string(&ZEND_OP2_LITERAL(opline)); } if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { - convert_to_string_safe(&ZEND_OP2_LITERAL(src)); + convert_to_string(&ZEND_OP2_LITERAL(src)); } VAR_SOURCE(opline->op1) = NULL; @@ -716,9 +719,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src = VAR_SOURCE(opline->op1); if (src && src->opcode == ZEND_CAST && - src->extended_value == IS_STRING) { + src->extended_value == IS_STRING && + src->op1_type != IS_CONST) { /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ - zend_optimizer_remove_live_range(op_array, opline->op1.var); VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); @@ -729,9 +732,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src = VAR_SOURCE(opline->op2); if (src && src->opcode == ZEND_CAST && - src->extended_value == IS_STRING) { + src->extended_value == IS_STRING && + src->op1_type != IS_CONST) { /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */ - zend_optimizer_remove_live_range(op_array, opline->op2.var); zend_op *src = VAR_SOURCE(opline->op2); VAR_SOURCE(opline->op2) = NULL; COPY_NODE(opline->op2, src->op1); @@ -857,7 +860,7 @@ optimize_const_unary_op: case ZEND_RETURN: case ZEND_EXIT: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + if (opline->op1_type == IS_TMP_VAR) { src = VAR_SOURCE(opline->op1); if (src && src->opcode == ZEND_QM_ASSIGN) { zend_op *op = src + 1; @@ -997,8 +1000,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start); } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start); @@ -1072,43 +1073,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op free_alloca(map, use_heap); } - /* adjust loop jump targets & remove unused live range entries */ - if (op_array->last_live_range) { - int i, j; - - for (i = 0, j = 0; i < op_array->last_live_range; i++) { - if (op_array->live_range[i].var == (uint32_t)-1) { - /* this live range already removed */ - continue; - } - if (!(blocks[cfg->map[op_array->live_range[i].start]].flags & ZEND_BB_REACHABLE)) { - ZEND_ASSERT(!(blocks[cfg->map[op_array->live_range[i].end]].flags & ZEND_BB_REACHABLE)); - } else { - uint32_t start_op = blocks[cfg->map[op_array->live_range[i].start]].start; - uint32_t end_op = blocks[cfg->map[op_array->live_range[i].end]].start; - - if (start_op == end_op) { - /* skip empty live range */ - continue; - } - op_array->live_range[i].start = start_op; - op_array->live_range[i].end = end_op; - if (i != j) { - op_array->live_range[j] = op_array->live_range[i]; - } - j++; - } - } - - if (i != j) { - op_array->last_live_range = j; - if (j == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } - } - } - /* adjust early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { ZEND_ASSERT(op_array == &ctx->script->main_op_array); @@ -1698,6 +1662,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use var_num = VAR_NUM(opline->result.var); switch (opline->opcode) { case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ADD_ARRAY_UNPACK: case ZEND_ROPE_ADD: /* these opcodes use the result as argument */ if (!zend_bitset_in(defined_here, var_num)) { @@ -1757,18 +1722,10 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use if (opline->result_type == IS_VAR) { if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) { switch (opline->opcode) { - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_ASSIGN: @@ -1815,6 +1772,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use SET_UNUSED(opline->result); break; case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ADD_ARRAY_UNPACK: case ZEND_ROPE_ADD: zend_bitset_incl(usage, VAR_NUM(opline->result.var)); break; @@ -1822,6 +1780,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use } else { switch (opline->opcode) { case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ADD_ARRAY_UNPACK: case ZEND_ROPE_ADD: break; default: @@ -1930,7 +1889,7 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* Build CFG */ checkpoint = zend_arena_checkpoint(ctx->arena); - if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg) != SUCCESS) { + if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) { zend_arena_release(&ctx->arena, checkpoint); return; } @@ -1944,17 +1903,11 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg); } - if (op_array->last_var || op_array->T) { - bitset_len = zend_bitset_len(op_array->last_var + op_array->T); - Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *)); - same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T); - usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); - } else { - bitset_len = 0; - Tsource = NULL; - same_t = NULL; - usage = NULL; - } + bitset_len = zend_bitset_len(op_array->last_var + op_array->T); + Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *)); + same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T); + usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); + blocks = cfg.blocks; end = blocks + cfg.blocks_count; for (pass = 0; pass < PASSES; pass++) { diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index f90b8175c3..1751311e88 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -28,6 +28,7 @@ #include "zend_constants.h" #include "zend_execute.h" #include "zend_vm.h" +#include "zend_extensions.h" #define DEBUG_COMPACT_LITERALS 0 @@ -107,7 +108,7 @@ static uint32_t add_static_slot(HashTable *hash, ret = Z_LVAL_P(pos); } else { ret = *cache_size; - *cache_size += 2 * sizeof(void *); + *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *); ZVAL_LONG(&tmp, ret); zend_hash_add(hash, key, &tmp); } @@ -182,6 +183,8 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1); break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -190,6 +193,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_OP: if (opline->op2_type == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2); } @@ -198,8 +206,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_FETCH_CLASS: - case ZEND_ADD_INTERFACE: - case ZEND_ADD_TRAIT: case ZEND_INSTANCEOF: if (opline->op2_type == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2); @@ -211,6 +217,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -223,6 +230,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_POST_INC_OBJ: case ZEND_POST_DEC_OBJ: case ZEND_ISSET_ISEMPTY_PROP_OBJ: + case ZEND_ASSIGN_OBJ_OP: if (opline->op1_type == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); } @@ -230,32 +238,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1); } break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->op2_type == IS_CONST) { - if (opline->extended_value == ZEND_ASSIGN_OBJ) { - LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1); - } else if (opline->extended_value == ZEND_ASSIGN_DIM) { - if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2); - } else { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - } - } else { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - } - } - break; case ZEND_BIND_GLOBAL: LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1); break; @@ -263,17 +245,14 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); break; case ZEND_DECLARE_FUNCTION: - case ZEND_DECLARE_CLASS: LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); break; - case ZEND_DECLARE_INHERITED_CLASS: - case ZEND_DECLARE_INHERITED_CLASS_DELAYED: + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2); - break; - case ZEND_DECLARE_ANON_INHERITED_CLASS: - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2); + if (opline->op2_type == IS_CONST) { + LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); + } break; case ZEND_ISSET_ISEMPTY_DIM_OBJ: case ZEND_ASSIGN_DIM: @@ -286,6 +265,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_LIST_R: case ZEND_FETCH_LIST_W: + case ZEND_ASSIGN_DIM_OP: if (opline->op1_type == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); } @@ -421,7 +401,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } else { map[i] = j; ZVAL_LONG(&zv, j); - zend_hash_str_add(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); + zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); if (i != j) { op_array->literals[j] = op_array->literals[i]; info[j] = info[i]; @@ -429,16 +409,18 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx j++; } break; - case IS_STRING: + case IS_STRING: { if (LITERAL_NUM_RELATED(info[i].flags) == 1) { key = zend_string_copy(Z_STR(op_array->literals[i])); - } else { + } else if ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE) { key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0); ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) + LITERAL_NUM_RELATED(info[i].flags) - 1; + } else { + /* Don't merge LITERAL_VALUE that has related literals */ + key = NULL; } - pos = zend_hash_find(&hash, key); - if (pos != NULL && + if (key && (pos = zend_hash_find(&hash, key)) != NULL && Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING && LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) && (LITERAL_NUM_RELATED(info[i].flags) != 2 || @@ -456,8 +438,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } else { map[i] = j; ZVAL_LONG(&zv, j); - zend_hash_add_new(&hash, key, &zv); - zend_string_release_ex(key, 0); + if (key) { + 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]; @@ -472,6 +456,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } } break; + } case IS_ARRAY: if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) { if (l_empty_arr < 0) { @@ -514,7 +499,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx method_slot = property_slot + j; /* Update opcodes to use new literals table */ - cache_size = 0; + cache_size = zend_op_array_extension_handles * sizeof(void*); opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { @@ -544,21 +529,31 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += sizeof(void *); } break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value != ZEND_ASSIGN_OBJ) { - break; + case ZEND_ASSIGN_STATIC_PROP_OP: + if (opline->op1_type == IS_CONST) { + // op1 static property + if (opline->op2_type == IS_CONST) { + (opline+1)->extended_value = add_static_slot(&hash, op_array, + opline->op2.constant, + opline->op1.constant, + LITERAL_STATIC_PROPERTY, + &cache_size); + } else { + (opline+1)->extended_value = cache_size; + cache_size += 3 * sizeof(void *); + } + } else if (opline->op2_type == IS_CONST) { + // op2 class + if (class_slot[opline->op2.constant] >= 0) { + (opline+1)->extended_value = class_slot[opline->op2.constant]; + } else { + (opline+1)->extended_value = cache_size; + class_slot[opline->op2.constant] = cache_size; + cache_size += sizeof(void *); + } } + break; + case ZEND_ASSIGN_OBJ_OP: if (opline->op2_type == IS_CONST) { // op2 property if (opline->op1_type == IS_UNUSED && @@ -566,7 +561,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx (opline+1)->extended_value = property_slot[opline->op2.constant]; } else { (opline+1)->extended_value = cache_size; - cache_size += 2 * sizeof(void *); + cache_size += 3 * sizeof(void *); if (opline->op1_type == IS_UNUSED) { property_slot[opline->op2.constant] = (opline+1)->extended_value; } @@ -574,6 +569,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -589,12 +585,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx // op2 property if (opline->op1_type == IS_UNUSED && property_slot[opline->op2.constant] >= 0) { - opline->extended_value = property_slot[opline->op2.constant]; + opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size; - cache_size += 2 * sizeof(void *); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + cache_size += 3 * sizeof(void *); if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = opline->extended_value; + property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS; } } } @@ -607,7 +603,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY); } else { opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); - cache_size += 2 * sizeof(void *); + cache_size += 3 * sizeof(void *); if (opline->op1_type == IS_UNUSED) { property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY; } @@ -698,6 +694,8 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += 2 * sizeof(void *); } break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -705,30 +703,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: - if (opline->op1_type == IS_CONST) { - // op1 static property - if (opline->op2_type == IS_CONST) { - opline->extended_value = add_static_slot(&hash, op_array, - opline->op2.constant, - opline->op1.constant, - LITERAL_STATIC_PROPERTY, - &cache_size); - } else { - opline->extended_value = cache_size; - cache_size += 2 * sizeof(void *); - } - } else if (opline->op2_type == IS_CONST) { - // op2 class - if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - class_slot[opline->op2.constant] = opline->extended_value; - } - } - break; case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: if (opline->op1_type == IS_CONST) { // op1 static property if (opline->op2_type == IS_CONST) { @@ -736,19 +715,19 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->op2.constant, opline->op1.constant, LITERAL_STATIC_PROPERTY, - &cache_size) | (opline->extended_value & ZEND_ISEMPTY); + &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); - cache_size += 2 * sizeof(void *); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + cache_size += 3 * sizeof(void *); } } else if (opline->op2_type == IS_CONST) { // op2 class if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + class_slot[opline->op2.constant] = cache_size; cache_size += sizeof(void *); - class_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY; } } break; @@ -799,6 +778,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx bind_var_slot[opline->op2.constant] = opline->extended_value; } break; + case ZEND_DECLARE_LAMBDA_FUNCTION: + case ZEND_DECLARE_ANON_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + opline->extended_value = cache_size; + cache_size += sizeof(void *); + break; } opline++; } diff --git a/ext/opcache/Optimizer/compact_vars.c b/ext/opcache/Optimizer/compact_vars.c index c7bda48c88..bf7a005787 100644 --- a/ext/opcache/Optimizer/compact_vars.c +++ b/ext/opcache/Optimizer/compact_vars.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Removing unused variables | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -95,15 +95,6 @@ void zend_optimizer_compact_vars(zend_op_array *op_array) { } } - /* Update TMP references in live ranges */ - if (op_array->live_range) { - for (i = 0; i < op_array->last_live_range; i++) { - op_array->live_range[i].var = - (op_array->live_range[i].var & ZEND_LIVE_MASK) | - NUM_VAR(vars_map[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK)]); - } - } - /* Update CV name table */ if (num_cvs != op_array->last_var) { if (num_cvs) { diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c index ef65b6abdf..8370bdc779 100644 --- a/ext/opcache/Optimizer/dce.c +++ b/ext/opcache/Optimizer/dce.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, DCE - Dead Code Elimination | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -127,7 +127,7 @@ static inline zend_bool may_have_side_effects( /* No side effects */ return 0; case ZEND_ROPE_END: - /* TODO: Rope dce optmization, see #76446 */ + /* TODO: Rope dce optimization, see #76446 */ return 1; case ZEND_JMP: case ZEND_JMPZ: @@ -201,21 +201,9 @@ static inline zend_bool may_have_side_effects( case ZEND_PRE_DEC: case ZEND_POST_DEC: return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def); - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OP: return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def) - || (opline->extended_value - && ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE); + || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def) @@ -241,6 +229,18 @@ static inline zend_bool may_have_side_effects( return 1; } return 0; + case ZEND_BIND_STATIC: + if (op_array->static_variables + && (opline->extended_value & ZEND_BIND_REF) != 0) { + zval *value = + (zval*)((char*)op_array->static_variables->arData + + (opline->extended_value & ~ZEND_BIND_REF)); + if (Z_TYPE_P(value) == IS_CONSTANT_AST) { + /* AST may contain undefined constants */ + return 1; + } + } + return 0; default: /* For everything we didn't handle, assume a side-effect */ return 1; @@ -267,19 +267,23 @@ static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) { } } -static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, int check) { +static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) { if (ssa_op->result_use >= 0) { add_to_worklists(ctx, ssa_op->result_use, check); } if (ssa_op->op1_use >= 0) { - if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) { + if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use) + || (opline->opcode == ZEND_ASSIGN + && (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) != 0)) { add_to_worklists(ctx, ssa_op->op1_use, check); } else { add_to_phi_worklist_no_val(ctx, ssa_op->op1_use); } } if (ssa_op->op2_use >= 0) { - if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)) { + if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use) + || (opline->opcode == ZEND_FE_FETCH_R + && (ssa->var_info[ssa_op->op2_use].type & MAY_BE_REF) != 0)) { add_to_worklists(ctx, ssa_op->op2_use, check); } else { add_to_phi_worklist_no_val(ctx, ssa_op->op2_use); @@ -330,18 +334,13 @@ static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, z case ZEND_ASSIGN_REF: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_PRE_INC_OBJ: @@ -475,80 +474,6 @@ static inline zend_bool may_break_varargs(const zend_op_array *op_array, const z return 0; } -static void dce_live_ranges(context *ctx, zend_op_array *op_array, zend_ssa *ssa) -{ - int i = 0; - int j = 0; - zend_live_range *live_range = op_array->live_range; - - while (i < op_array->last_live_range) { - if ((live_range->var & ZEND_LIVE_MASK) != ZEND_LIVE_TMPVAR) { - /* keep */ - j++; - } else { - uint32_t var = live_range->var & ~ZEND_LIVE_MASK; - uint32_t def = live_range->start - 1; - - if ((op_array->opcodes[def].result_type == IS_UNUSED) && - (UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_STMT) || - UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_FCALL_END) || - UNEXPECTED(op_array->opcodes[def].opcode == ZEND_END_SILENCE))) { - def--; - } - - if (op_array->opcodes[def].result_type == IS_UNUSED) { - if (op_array->opcodes[def].opcode == ZEND_DO_FCALL) { - /* constructor call */ - do { - def--; - if ((op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)) - && op_array->opcodes[def].result.var == var) { - ZEND_ASSERT(op_array->opcodes[def].opcode == ZEND_NEW); - break; - } - } while (def > 0); - } else if (op_array->opcodes[def].opcode == ZEND_OP_DATA) { - def--; - } - } - -#if ZEND_DEBUG - ZEND_ASSERT(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)); - ZEND_ASSERT(op_array->opcodes[def].result.var == var); - ZEND_ASSERT(ssa->ops[def].result_def >= 0); -#else - if (!(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)) - || op_array->opcodes[def].result.var != var - || ssa->ops[def].result_def < 0) { - /* TODO: Some wrong live-range? keep it. */ - j++; - live_range++; - i++; - continue; - } -#endif - - var = ssa->ops[def].result_def; - - if ((ssa->var_info[var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - && !is_var_dead(ctx, var)) { - /* keep */ - j++; - } else if (i != j) { - op_array->live_range[j] = *live_range; - } - } - - live_range++; - i++; - } - op_array->last_live_range = j; - if (op_array->last_live_range == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } -} - int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) { int i; zend_ssa_phi *phi; @@ -599,9 +524,9 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor if (zend_bitset_in(ctx.instr_worklist, i)) { zend_bitset_excl(ctx.instr_worklist, i); - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 0); + add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0); if (op_data >= 0) { - add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], 0); + add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0); } } else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects) || zend_may_throw(&op_array->opcodes[i], op_array, ssa) @@ -613,9 +538,9 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor zend_bitset_incl(ctx.instr_dead, i); zend_bitset_incl(ctx.instr_dead, i+1); } else { - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 0); + add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0); if (op_data >= 0) { - add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], 0); + add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0); } } } else { @@ -633,10 +558,10 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor || !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) { while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) { zend_bitset_excl(ctx.instr_dead, i); - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 1); + add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1); if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) { zend_bitset_excl(ctx.instr_dead, i+1); - add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], 1); + add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1); } } while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) { @@ -646,10 +571,6 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor } } - if (op_array->live_range) { - dce_live_ranges(&ctx, op_array, ssa); - } - /* Eliminate dead instructions */ ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) { removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]); diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 01baadd1e7..1c4e30597a 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -268,12 +268,6 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op } } - /* update brk/cont array */ - for (j = 0; j < op_array->last_live_range; j++) { - op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; - op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; - } - /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; @@ -314,6 +308,17 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op free_alloca(shiftlist, use_heap); } +static zend_bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { + if (ce1 == ce2) { + return 1; + } + if (!(ce1->ce_flags & ZEND_ACC_LINKED)) { + /* This case could be generalized, similarly to unlinked_instanceof */ + return 0; + } + return instanceof_function(ce1, ce2); +} + static inline zend_bool can_elide_return_type_check( zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) { zend_arg_info *info = &op_array->arg_info[-1]; @@ -335,7 +340,7 @@ static inline zend_bool can_elide_return_type_check( } if (ZEND_TYPE_IS_CLASS(info->type)) { - if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) { + if (!use_info->ce || !def_info->ce || !safe_instanceof(use_info->ce, def_info->ce)) { return 0; } } @@ -521,6 +526,19 @@ static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, ze } } +static zend_always_inline void take_successor_ex(zend_ssa *ssa, int block_num, zend_basic_block *block, int target_block) +{ + int i; + + for (i = 0; i < block->successors_count; i++) { + if (block->successors[i] != target_block) { + zend_ssa_remove_predecessor(ssa, block_num, block->successors[i]); + } + } + block->successors[0] = target_block; + block->successors_count = 1; +} + static void compress_block(zend_op_array *op_array, zend_basic_block *block) { while (block->len > 0) { @@ -625,8 +643,6 @@ static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa } } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) { @@ -878,9 +894,6 @@ optimize_jmpnz: take_successor_1(ssa, block_num, block); goto optimize_nop; } else { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } opline->opcode = ZEND_JMP; opline->result_type = IS_UNUSED; zend_ssa_remove_result_def(ssa, ssa_op); @@ -891,6 +904,64 @@ optimize_jmpnz: } break; } + case ZEND_SWITCH_LONG: + if (opline->op1_type == IS_CONST) { + zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); + if (Z_TYPE_P(zv) != IS_LONG) { + removed_ops++; + MAKE_NOP(opline); + opline->extended_value = 0; + take_successor_ex(ssa, block_num, block, block->successors[0]); + goto optimize_nop; + } else { + HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); + zval *jmp_zv = zend_hash_index_find(jmptable, Z_LVAL_P(zv)); + uint32_t target; + + if (jmp_zv) { + target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv)); + } else { + target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); + } + opline->opcode = ZEND_JMP; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target); + SET_UNUSED(opline->op2); + take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]); + goto optimize_jmp; + } + } + break; + case ZEND_SWITCH_STRING: + if (opline->op1_type == IS_CONST) { + zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); + if (Z_TYPE_P(zv) != IS_STRING) { + removed_ops++; + MAKE_NOP(opline); + opline->extended_value = 0; + take_successor_ex(ssa, block_num, block, block->successors[0]); + goto optimize_nop; + } else { + HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); + zval *jmp_zv = zend_hash_find(jmptable, Z_STR_P(zv)); + uint32_t target; + + if (jmp_zv) { + target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv)); + } else { + target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); + } + opline->opcode = ZEND_JMP; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target); + SET_UNUSED(opline->op2); + take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]); + goto optimize_jmp; + } + } + break; case ZEND_NOP: optimize_nop: compress_block(op_array, block); @@ -1052,6 +1123,34 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx && !(OP2_INFO() & MAY_BE_OBJECT)) { opline->opcode = ZEND_FAST_CONCAT; } + } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE + && opline->op1_type != IS_CONST + && ssa->ops[op_1].op1_def == v + && ssa->ops[op_1].op1_use >= 0 + && ssa->ops[op_1].op1_use_chain == -1 + && ssa->vars[v].use_chain >= 0 + && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) { + +// op_1: VERIFY_RETURN_TYPE #orig_var.? [T] -> #v.? [T] => NOP + + int orig_var = ssa->ops[op_1].op1_use; + if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { + + int ret = ssa->vars[v].use_chain; + + ssa->ops[ret].op1_use = orig_var; + ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain; + ssa->vars[orig_var].use_chain = ret; + + ssa->vars[v].definition = -1; + ssa->vars[v].use_chain = -1; + + ssa->ops[op_1].op1_def = -1; + ssa->ops[op_1].op1_use = -1; + + MAKE_NOP(opline); + remove_nops = 1; + } } } @@ -1144,8 +1243,8 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx } } - } else if (opline->opcode == ZEND_ASSIGN_ADD - && opline->extended_value == 0 + } else if (opline->opcode == ZEND_ASSIGN_OP + && opline->extended_value == ZEND_ADD && ssa->ops[op_1].op1_def == v && opline->op2_type == IS_CONST && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG @@ -1156,10 +1255,11 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx // op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV opline->opcode = ZEND_PRE_INC; + opline->extended_value = 0; SET_UNUSED(opline->op2); - } else if (opline->opcode == ZEND_ASSIGN_SUB - && opline->extended_value == 0 + } else if (opline->opcode == ZEND_ASSIGN_OP + && opline->extended_value == ZEND_SUB && ssa->ops[op_1].op1_def == v && opline->op2_type == IS_CONST && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG @@ -1170,60 +1270,25 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx // op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV opline->opcode = ZEND_PRE_DEC; + opline->extended_value = 0; SET_UNUSED(opline->op2); - } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE - && ssa->ops[op_1].op1_def == v - && ssa->ops[op_1].op1_use >= 0 - && ssa->ops[op_1].op1_use_chain == -1 - && ssa->vars[v].use_chain >= 0 - && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) { - -// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP - - int orig_var = ssa->ops[op_1].op1_use; - if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { - - int ret = ssa->vars[v].use_chain; - - ssa->ops[ret].op1_use = orig_var; - ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain; - ssa->vars[orig_var].use_chain = ret; - - ssa->vars[v].definition = -1; - ssa->vars[v].use_chain = -1; - - ssa->ops[op_1].op1_def = -1; - ssa->ops[op_1].op1_use = -1; - - MAKE_NOP(opline); - remove_nops = 1; - } - } else if (ssa->ops[op_1].op1_def == v && !RETURN_VALUE_USED(opline) && ssa->ops[op_1].op1_use >= 0 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - && (opline->opcode == ZEND_ASSIGN_ADD - || opline->opcode == ZEND_ASSIGN_SUB - || opline->opcode == ZEND_ASSIGN_MUL - || opline->opcode == ZEND_ASSIGN_DIV - || opline->opcode == ZEND_ASSIGN_MOD - || opline->opcode == ZEND_ASSIGN_SL - || opline->opcode == ZEND_ASSIGN_SR - || opline->opcode == ZEND_ASSIGN_BW_OR - || opline->opcode == ZEND_ASSIGN_BW_AND - || opline->opcode == ZEND_ASSIGN_BW_XOR) - && opline->extended_value == 0) { - -// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ? + && opline->opcode == ZEND_ASSIGN_OP + && opline->extended_value != ZEND_CONCAT) { + +// op_1: ASSIGN_OP #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ? /* Reconstruct SSA */ ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def; ssa->ops[op_1].op1_def = -1; /* Update opcode */ - opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD); + opline->opcode = opline->extended_value; + opline->extended_value = 0; opline->result_type = opline->op1_type; opline->result.var = opline->op1.var; @@ -1262,11 +1327,3 @@ void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* Destroy SSA */ zend_arena_release(&ctx->arena, checkpoint); } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/escape_analysis.c b/ext/opcache/Optimizer/escape_analysis.c index f88de6202a..c561bec9dc 100644 --- a/ext/opcache/Optimizer/escape_analysis.c +++ b/ext/opcache/Optimizer/escape_analysis.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache, Escape Analysis | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -217,6 +217,7 @@ static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, in break; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) { /* implicit object/array allocation */ return 1; @@ -256,23 +257,11 @@ static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var } else if (op->op1_def == var) { switch (opline->opcode) { case ZEND_ASSIGN: - return 1; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: - return 1; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - return (opline->extended_value != 0); + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: case ZEND_POST_INC_OBJ: @@ -310,24 +299,14 @@ static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int va case ZEND_FETCH_DIM_IS: case ZEND_FETCH_OBJ_IS: break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - if (!opline->extended_value) { - return 1; - } - /* break missing intentionally */ + case ZEND_ASSIGN_OP: + return 1; + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: break; case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: @@ -434,7 +413,7 @@ int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, } - /* 1. Build EES (Equi-Esape Sets) */ + /* 1. Build EES (Equi-Escape Sets) */ ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap); if (!ees) { return FAILURE; @@ -508,7 +487,8 @@ int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, if (opline->opcode == ZEND_OP_DATA && ((opline-1)->opcode == ZEND_ASSIGN_DIM || - (opline-1)->opcode == ZEND_ASSIGN_OBJ) && + (opline-1)->opcode == ZEND_ASSIGN_OBJ || + (opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) && op->op1_use == i && (op-1)->op1_use >= 0) { enclosing_root = ees[(op-1)->op1_use]; @@ -531,7 +511,6 @@ int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) { num_non_escaped--; if (num_non_escaped == 0) { - i = ssa_vars_count; changed = 0; } else { changed = 1; @@ -561,11 +540,3 @@ int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, return SUCCESS; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index cfd2ca90cf..32d2f10bf4 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -81,12 +81,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx zend_optimizer_shift_jump(op_array, opline, shiftlist); } - /* update brk/cont array */ - for (j = 0; j < op_array->last_live_range; j++) { - op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; - op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; - } - /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c index 1eae2ca22a..6af7865e92 100644 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -95,6 +95,8 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o { if (func->type == ZEND_USER_FUNCTION && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS)) + /* TODO: function copied from trait may be inconsistent ??? */ + && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE)) && fcall->extended_value >= func->op_array.required_num_args && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) { @@ -104,6 +106,12 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o uint32_t i, num_args = func->op_array.num_args; num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0; + if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL + && !(func->op_array.fn_flags & ZEND_ACC_STATIC)) { + /* Don't inline static call to instance method. */ + return; + } + if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) { /* TODO: we can't inlne methods, because $this may be used * not in object context ??? diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 912136433f..825091bfc3 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -52,7 +52,6 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c int currT; int i; int max = -1; - int var_to_free = -1; void *checkpoint = zend_arena_checkpoint(ctx->arena); bitset_len = zend_bitset_len(T); @@ -180,22 +179,9 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c } } - if (var_to_free >= 0) { - zend_bitset_excl(taken_T, var_to_free); - var_to_free = -1; - } - opline--; } - if (op_array->live_range) { - for (i = 0; i < op_array->last_live_range; i++) { - op_array->live_range[i].var = - NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) | - (op_array->live_range[i].var & ZEND_LIVE_MASK); - } - } - zend_arena_release(&ctx->arena, checkpoint); op_array->T = max + 1; } diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 2a09b2118a..8f41730114 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -24,6 +24,7 @@ * - perform compile-time evaluation of constant binary and unary operations * - convert CAST(IS_BOOL,x) into BOOL(x) * - pre-evaluate constant function calls + * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM */ #include "php.h" @@ -514,6 +515,52 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)); } break; +#if 0 + /* see ext/opcache/tests/bug78961.phpt */ +// case ZEND_FETCH_R: + case ZEND_FETCH_W: +// case ZEND_FETCH_RW: + case ZEND_FETCH_IS: +// case ZEND_FETCH_FUNC_ARG: + case ZEND_FETCH_UNSET: + /* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */ + if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 && + opline->op1_type == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && + zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") && + ((opline + 1)->opcode == opline->opcode + 1 || + ((opline + 1)->opcode == ZEND_UNSET_DIM && + opline->opcode == ZEND_FETCH_UNSET) || + ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ && + opline->opcode == ZEND_FETCH_IS)) && + (opline + 1)->op1_type == opline->result_type && + (opline + 1)->op1.var == opline->result.var && + ((opline + 1)->op2_type != IS_CONST || + Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) { + + if ((opline + 1)->opcode == ZEND_UNSET_DIM) { + (opline + 1)->opcode = ZEND_UNSET_VAR; + (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; + } else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) { + (opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR; + (opline + 1)->extended_value |= ZEND_FETCH_GLOBAL; + } else { + (opline + 1)->opcode = opline->opcode; + (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; + } + (opline + 1)->op1_type = (opline + 1)->op2_type; + (opline + 1)->op1 = (opline + 1)->op2; + if ((opline + 1)->op1_type == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) { + + convert_to_string(&ZEND_OP1_LITERAL(opline + 1)); + zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1))); + } + SET_UNUSED((opline + 1)->op2); + MAKE_NOP(opline); + } + break; +#endif case ZEND_RETURN: case ZEND_RETURN_BY_REF: diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index d5ce86e133..01e118e7e3 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -53,16 +53,6 @@ void zend_optimizer_pass2(zend_op_array *op_array) } } } - /* break missing *intentionally* - the assign_op's may only optimize op2 */ - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - if (opline->extended_value != 0) { - /* object tristate op - don't attempt to optimize it! */ - break; - } if (opline->op2_type == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { /* don't optimise if it should produce a runtime numeric string error */ @@ -85,14 +75,6 @@ void zend_optimizer_pass2(zend_op_array *op_array) } } } - /* break missing *intentionally - the assign_op's may only optimize op2 */ - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - if (opline->extended_value != 0) { - /* object tristate op - don't attempt to optimize it! */ - break; - } if (opline->op2_type == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { /* don't optimise if it should produce a runtime numeric string error */ @@ -111,12 +93,6 @@ void zend_optimizer_pass2(zend_op_array *op_array) convert_to_string(&ZEND_OP1_LITERAL(opline)); } } - /* break missing *intentionally - the assign_op's may only optimize op2 */ - case ZEND_ASSIGN_CONCAT: - if (opline->extended_value != 0) { - /* object tristate op - don't attempt to optimize it! */ - break; - } if (opline->op2_type == IS_CONST) { if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP2_LITERAL(opline)); @@ -124,6 +100,37 @@ void zend_optimizer_pass2(zend_op_array *op_array) } break; + case ZEND_ASSIGN_OP: + if (opline->op2_type == IS_CONST) { + if (opline->extended_value == ZEND_ADD + || opline->extended_value == ZEND_SUB + || opline->extended_value == ZEND_MUL + || opline->extended_value == ZEND_DIV + || opline->extended_value == ZEND_POW) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); + } + } + } else if (opline->extended_value == ZEND_MOD + || opline->extended_value == ZEND_SL + || opline->extended_value == ZEND_SR) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } + } + } else if (opline->extended_value == ZEND_CONCAT) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); + } + } + } + break; + case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index d382d37e11..5bbb2b0854 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -113,47 +113,12 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } - if ((opline->op1_type & (IS_VAR | IS_CV)) + if (ZEND_IS_BINARY_ASSIGN_OP_OPCODE(opline->opcode) + && (opline->op1_type & (IS_VAR | IS_CV)) && opline->op1.var == next_opline->op1.var && opline->op1_type == next_opline->op1_type) { - switch (opline->opcode) { - case ZEND_ADD: - opline->opcode = ZEND_ASSIGN_ADD; - break; - case ZEND_SUB: - opline->opcode = ZEND_ASSIGN_SUB; - break; - case ZEND_MUL: - opline->opcode = ZEND_ASSIGN_MUL; - break; - case ZEND_DIV: - opline->opcode = ZEND_ASSIGN_DIV; - break; - case ZEND_MOD: - opline->opcode = ZEND_ASSIGN_MOD; - break; - case ZEND_POW: - opline->opcode = ZEND_ASSIGN_POW; - break; - case ZEND_CONCAT: - opline->opcode = ZEND_ASSIGN_CONCAT; - break; - case ZEND_SL: - opline->opcode = ZEND_ASSIGN_SL; - break; - case ZEND_SR: - opline->opcode = ZEND_ASSIGN_SR; - break; - case ZEND_BW_OR: - opline->opcode = ZEND_ASSIGN_BW_OR; - break; - case ZEND_BW_AND: - opline->opcode = ZEND_ASSIGN_BW_AND; - break; - case ZEND_BW_XOR: - opline->opcode = ZEND_ASSIGN_BW_XOR; - break; - } + opline->extended_value = opline->opcode; + opline->opcode = ZEND_ASSIGN_OP; COPY_NODE(opline->result, next_opline->result); MAKE_NOP(next_opline); opline++; diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 53549a6208..c949660d51 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, SCCP - Sparse Conditional Constant Propagation | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -221,18 +221,11 @@ static zend_bool can_replace_op1( case ZEND_ASSIGN_REF: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_UNSET: @@ -253,10 +246,6 @@ static zend_bool can_replace_op1( case ZEND_FE_RESET_RW: return 0; /* Do not accept CONST */ - case ZEND_VERIFY_ABSTRACT_CLASS: - case ZEND_ADD_INTERFACE: - case ZEND_ADD_TRAIT: - case ZEND_BIND_TRAITS: case ZEND_ROPE_ADD: case ZEND_ROPE_END: case ZEND_BIND_STATIC: @@ -273,6 +262,9 @@ static zend_bool can_replace_op1( case ZEND_VERIFY_RETURN_TYPE: // TODO: This would require a non-local change ??? return 0; + case ZEND_OP_DATA: + return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF && + (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF; default: if (ssa_op->op1_def != -1) { ZEND_ASSERT(0); @@ -287,9 +279,7 @@ static zend_bool can_replace_op2( const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) { switch (opline->opcode) { /* Do not accept CONST */ - case ZEND_DECLARE_INHERITED_CLASS: - case ZEND_DECLARE_INHERITED_CLASS_DELAYED: - case ZEND_DECLARE_ANON_INHERITED_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: case ZEND_BIND_LEXICAL: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: @@ -358,7 +348,6 @@ static zend_bool try_replace_op2( ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use); if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) { zend_ssa_op *next_op = ssa_op + 1; - zend_optimizer_remove_live_range_ex(ctx->scdf.op_array, opline->result.var, ssa_op - ctx->scdf.ssa->ops); zend_ssa_unlink_use_chain(ctx->scdf.ssa, next_op - ctx->scdf.ssa->ops, next_op->op2_use); next_op->op2_use = -1; next_op->op2_use_chain = -1; @@ -571,6 +560,27 @@ static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) { return SUCCESS; } +static inline int ct_eval_add_array_unpack(zval *result, zval *array) { + zend_string *key; + zval *value; + if (Z_TYPE_P(array) != IS_ARRAY) { + return FAILURE; + } + + SEPARATE_ARRAY(result); + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array), key, value) { + if (key) { + return FAILURE; + } + value = zend_hash_next_index_insert(Z_ARR_P(result), value); + if (!value) { + return FAILURE; + } + Z_TRY_ADDREF_P(value); + } ZEND_HASH_FOREACH_END(); + return SUCCESS; +} + static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) { switch (Z_TYPE_P(result)) { case IS_NULL: @@ -746,6 +756,26 @@ static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval * return SUCCESS; } +static inline int ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) { + zval *value; + + if (Z_TYPE_P(op2) != IS_ARRAY && !IS_PARTIAL_ARRAY(op2)) { + return FAILURE; + } + if (Z_TYPE_P(op1) != IS_STRING && Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_NULL) { + return FAILURE; + } + if (fetch_array_elem(&value, op2, op1) == FAILURE) { + return FAILURE; + } + if (IS_PARTIAL_ARRAY(op2) && (!value || IS_BOT(value))) { + return FAILURE; + } + + ZVAL_BOOL(result, value != NULL); + return SUCCESS; +} + /* The functions chosen here are simple to implement and either likely to affect a branch, * or just happened to be commonly used with constant operands in WP (need to test other * applications as well, of course). */ @@ -1095,8 +1125,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o if (op2 && IS_BOT(op2)) { /* Update of unknown index */ SET_RESULT_BOT(result); - if (ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { + if (ssa_op->op1_def >= 0) { empty_partial_array(&zv); SET_RESULT(op1, &zv); zval_ptr_dtor_nogc(&zv); @@ -1113,8 +1142,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o || Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE || Z_TYPE_P(op1) == IS_ARRAY) - && ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { + && ssa_op->op1_def >= 0) { if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) { empty_partial_array(&zv); @@ -1169,9 +1197,18 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o if (ssa_op->op1_def >= 0 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { zval *data = get_op1_value(ctx, opline+1, ssa_op+1); + zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use]; + + /* Don't try to propagate assignments to (potentially) typed properties. We would + * need to deal with errors and type conversions first. */ + if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) { + SET_RESULT_BOT(result); + SET_RESULT_BOT(op1); + return; + } /* If $a in $a->foo=$c is UNDEF, treat it like NULL. There is no warning. */ - if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) { + if ((var_info->type & MAY_BE_ANY) == 0) { op1 = &EG(uninitialized_zval); } @@ -1294,8 +1331,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o if (op2 && IS_BOT(op2)) { /* Update of unknown index */ SET_RESULT_BOT(op1); - if (ssa_op->result_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { + if (ssa_op->result_def >= 0) { empty_partial_array(&zv); SET_RESULT(result, &zv); zval_ptr_dtor_nogc(&zv); @@ -1309,8 +1345,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { SET_RESULT_BOT(op1); - if (ssa_op->result_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { + if (ssa_op->result_def >= 0) { if (!result) { empty_partial_array(&zv); } else { @@ -1361,6 +1396,31 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } return; } + case ZEND_ADD_ARRAY_UNPACK: { + zval *result = &ctx->values[ssa_op->result_use]; + if (IS_BOT(result) || IS_BOT(op1)) { + SET_RESULT_BOT(result); + return; + } + SKIP_IF_TOP(result); + SKIP_IF_TOP(op1); + + /* See comment for ADD_ARRAY_ELEMENT. */ + if (Z_TYPE_P(result) == IS_NULL) { + SET_RESULT_BOT(result); + return; + } + ZVAL_COPY_VALUE(&zv, result); + ZVAL_NULL(result); + + if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) { + SET_RESULT(result, &zv); + } else { + SET_RESULT_BOT(result); + } + zval_ptr_dtor_nogc(&zv); + return; + } case ZEND_NEW: if (ssa_op->result_def >= 0 && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { @@ -1371,6 +1431,17 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o SET_RESULT_BOT(result); } return; + case ZEND_ASSIGN_STATIC_PROP_REF: + case ZEND_ASSIGN_OBJ_REF: + /* Handled here because we also need to BOT the OP_DATA operand, while the generic + * code below will not do so. */ + SET_RESULT_BOT(result); + SET_RESULT_BOT(op1); + SET_RESULT_BOT(op2); + opline++; + ssa_op++; + SET_RESULT_BOT(op1); + break; } if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) { @@ -1414,36 +1485,26 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } SET_RESULT_BOT(result); break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: if (op1) { SKIP_IF_TOP(op1); } if (op2) { SKIP_IF_TOP(op2); } - if (!opline->extended_value) { - if (ct_eval_binary_op(&zv, zend_compound_assign_to_binary_op(opline->opcode), op1, op2) == SUCCESS) { + if (opline->opcode == ZEND_ASSIGN_OP) { + if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) { SET_RESULT(op1, &zv); SET_RESULT(result, &zv); zval_ptr_dtor_nogc(&zv); break; } - } else if (opline->extended_value == ZEND_ASSIGN_DIM) { + } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) { if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY) - && ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE - && op2) { + && ssa_op->op1_def >= 0 && op2) { zval tmp; zval *data = get_op1_value(ctx, opline+1, ssa_op+1); @@ -1460,7 +1521,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o break; } - if (ct_eval_binary_op(&tmp, zend_compound_assign_to_binary_op(opline->opcode), &tmp, data) != SUCCESS) { + if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) { SET_RESULT_BOT(result); SET_RESULT_BOT(op1); zval_ptr_dtor_nogc(&tmp); @@ -1485,7 +1546,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o zval_ptr_dtor_nogc(&zv); } } - } else if (opline->extended_value == ZEND_ASSIGN_OBJ) { + } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { if (op1 && IS_PARTIAL_OBJECT(op1) && ssa_op->op1_def >= 0 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { @@ -1505,7 +1566,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o break; } - if (ct_eval_binary_op(&tmp, zend_compound_assign_to_binary_op(opline->opcode), &tmp, data) != SUCCESS) { + if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) { SET_RESULT_BOT(result); SET_RESULT_BOT(op1); zval_ptr_dtor_nogc(&tmp); @@ -1652,6 +1713,16 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } SET_RESULT_BOT(result); break; + case ZEND_ARRAY_KEY_EXISTS: + SKIP_IF_TOP(op1); + SKIP_IF_TOP(op2); + if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) { + SET_RESULT(result, &zv); + zval_ptr_dtor_nogc(&zv); + break; + } + SET_RESULT_BOT(result); + break; case ZEND_FETCH_DIM_R: case ZEND_FETCH_DIM_IS: case ZEND_FETCH_LIST_R: @@ -1706,6 +1777,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o case ZEND_QM_ASSIGN: case ZEND_JMP_SET: case ZEND_COALESCE: + case ZEND_COPY_TMP: SET_RESULT(result, op1); break; #if 0 @@ -1856,8 +1928,6 @@ static void sccp_mark_feasible_successors( switch (opline->opcode) { case ZEND_ASSERT_CHECK: case ZEND_CATCH: - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); @@ -1920,6 +1990,42 @@ static void sccp_mark_feasible_successors( } s = zend_hash_num_elements(Z_ARR_P(op1)) != 0; break; + case ZEND_SWITCH_LONG: + if (Z_TYPE_P(op1) == IS_LONG) { + zend_op_array *op_array = scdf->op_array; + zend_ssa *ssa = scdf->ssa; + HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); + zval *jmp_zv = zend_hash_index_find(jmptable, Z_LVAL_P(op1)); + int target; + + if (jmp_zv) { + target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))]; + } else { + target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; + } + scdf_mark_edge_feasible(scdf, block_num, target); + return; + } + s = 0; + break; + case ZEND_SWITCH_STRING: + if (Z_TYPE_P(op1) == IS_STRING) { + zend_op_array *op_array = scdf->op_array; + zend_ssa *ssa = scdf->ssa; + HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); + zval *jmp_zv = zend_hash_find(jmptable, Z_STR_P(op1)); + int target; + + if (jmp_zv) { + target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))]; + } else { + target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; + } + scdf_mark_edge_feasible(scdf, block_num, target); + return; + } + s = 0; + break; default: for (s = 0; s < block->successors_count; s++) { scdf_mark_edge_feasible(scdf, block_num, block->successors[s]); @@ -2000,7 +2106,7 @@ static void join_phi_values(zval *a, zval *b, zend_bool escape) { return; } if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) { - if (escape || join_partial_arrays(a, b) != SUCCESS) { + if (join_partial_arrays(a, b) != SUCCESS) { zval_ptr_dtor_nogc(a); MAKE_BOT(a); } @@ -2010,7 +2116,7 @@ static void join_phi_values(zval *a, zval *b, zend_bool escape) { MAKE_BOT(a); } } else if (!zend_is_identical(a, b)) { - if (escape || join_partial_arrays(a, b) != SUCCESS) { + if (join_partial_arrays(a, b) != SUCCESS) { zval_ptr_dtor_nogc(a); MAKE_BOT(a); } @@ -2156,7 +2262,7 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, if (ssa_op->result_def == var_num) { if (ssa_op->op1_def >= 0 || ssa_op->op2_def >= 0) { - /* we cannot remove instruction that defines other varibales */ + /* we cannot remove instruction that defines other variables */ return 0; } else if (opline->opcode == ZEND_JMPZ_EX || opline->opcode == ZEND_JMPNZ_EX @@ -2177,13 +2283,13 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, && opline->opcode != ZEND_ROPE_INIT && opline->opcode != ZEND_ROPE_ADD && opline->opcode != ZEND_INIT_ARRAY - && opline->opcode != ZEND_ADD_ARRAY_ELEMENT) { + && opline->opcode != ZEND_ADD_ARRAY_ELEMENT + && opline->opcode != ZEND_ADD_ARRAY_UNPACK) { /* Replace with QM_ASSIGN */ zend_uchar old_type = opline->result_type; uint32_t old_var = opline->result.var; ssa_op->result_def = -1; - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); if (opline->opcode == ZEND_DO_ICALL) { removed_ops = remove_call(ctx, opline, ssa_op) - 1; } else { @@ -2198,9 +2304,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, } return 0; } else { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } zend_ssa_remove_result_def(ssa, ssa_op); if (opline->opcode == ZEND_DO_ICALL) { removed_ops = remove_call(ctx, opline, ssa_op); @@ -2226,18 +2329,10 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, switch (opline->opcode) { case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) { return 0; @@ -2263,9 +2358,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, if (ssa_op->result_def >= 0) { if (ssa->vars[ssa_op->result_def].use_chain < 0 && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } zend_ssa_remove_result_def(ssa, ssa_op); opline->result_type = IS_UNUSED; } else if (opline->opcode != ZEND_PRE_INC && @@ -2293,22 +2385,11 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, removed_ops++; zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1); break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - if (opline->extended_value) { - removed_ops++; - zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1); - } + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: + removed_ops++; + zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1); break; default: break; diff --git a/ext/opcache/Optimizer/scdf.c b/ext/opcache/Optimizer/scdf.c index e9becf2d3d..aa7ea3a1a2 100644 --- a/ext/opcache/Optimizer/scdf.c +++ b/ext/opcache/Optimizer/scdf.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Sparse Conditional Data Flow Propagation Framework | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -158,7 +158,7 @@ void scdf_solve(scdf_ctx *scdf, const char *name) { /* Zero length blocks don't have a last instruction that would normally do this */ scdf_mark_edge_feasible(scdf, i, block->successors[0]); } else { - zend_op *opline; + zend_op *opline = NULL; int j, end = block->start + block->len; for (j = block->start; j < end; j++) { opline = &scdf->op_array->opcodes[j]; @@ -170,6 +170,7 @@ void scdf_solve(scdf_ctx *scdf, const char *name) { if (block->successors_count == 1) { scdf_mark_edge_feasible(scdf, i, block->successors[0]); } else if (block->successors_count > 1) { + ZEND_ASSERT(opline && "Should have opline in non-empty block"); if (opline->opcode == ZEND_OP_DATA) { opline--; j--; @@ -184,18 +185,27 @@ void scdf_solve(scdf_ctx *scdf, const char *name) { /* If a live range starts in a reachable block and ends in an unreachable block, we should * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var * is necessary for the correctness of temporary compaction. */ -static zend_bool kept_alive_by_live_range(scdf_ctx *scdf, uint32_t block) { +static zend_bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) { uint32_t i; const zend_op_array *op_array = scdf->op_array; const zend_cfg *cfg = &scdf->ssa->cfg; - for (i = 0; i < op_array->last_live_range; i++) { - zend_live_range *live_range = &op_array->live_range[i]; - uint32_t start_block = cfg->map[live_range->start]; - uint32_t end_block = cfg->map[live_range->end]; - - if (end_block == block && start_block != block - && zend_bitset_in(scdf->executable_blocks, start_block)) { - return 1; + const zend_basic_block *block = &cfg->blocks[block_idx]; + if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) { + return 0; + } + for (i = block->start; i < block->start + block->len; i++) { + zend_op *opline = &op_array->opcodes[i]; + if (zend_optimizer_is_loop_var_free(opline)) { + int ssa_var = scdf->ssa->ops[i].op1_use; + if (ssa_var >= 0) { + int op_num = scdf->ssa->vars[ssa_var].definition; + uint32_t def_block; + ZEND_ASSERT(op_num >= 0); + def_block = cfg->map[op_num]; + if (zend_bitset_in(scdf->executable_blocks, def_block)) { + return 1; + } + } } } return 0; @@ -208,11 +218,10 @@ int scdf_remove_unreachable_blocks(scdf_ctx *scdf) { zend_ssa *ssa = scdf->ssa; int i; int removed_ops = 0; - for (i = 0; i < ssa->cfg.blocks_count; i++) { if (!zend_bitset_in(scdf->executable_blocks, i) && (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE) - && !kept_alive_by_live_range(scdf, i)) { + && !kept_alive_by_loop_var_free(scdf, i)) { removed_ops += ssa->cfg.blocks[i].len; zend_ssa_remove_block(scdf->op_array, ssa, i); } diff --git a/ext/opcache/Optimizer/scdf.h b/ext/opcache/Optimizer/scdf.h index 1f651e04f4..1b730936ec 100644 --- a/ext/opcache/Optimizer/scdf.h +++ b/ext/opcache/Optimizer/scdf.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Call Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/Optimizer/ssa_integrity.c b/ext/opcache/Optimizer/ssa_integrity.c index 0deaf0e8a9..ede40be59a 100644 --- a/ext/opcache/Optimizer/ssa_integrity.c +++ b/ext/opcache/Optimizer/ssa_integrity.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, SSA validation | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index 109fc72353..348e55d363 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Call Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -53,6 +53,7 @@ static int zend_op_array_collect(zend_call_graph *call_graph, zend_op_array *op_ static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *script, zend_op_array_func_t func) { zend_class_entry *ce; + zend_string *key; zend_op_array *op_array; if (func(call_graph, &script->main_op_array) != SUCCESS) { @@ -65,9 +66,14 @@ static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *scrip } } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { if (func(call_graph, op_array) != SUCCESS) { return FAILURE; } @@ -296,11 +302,3 @@ zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, z return map; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h index 6c8e5efa09..033c675b63 100644 --- a/ext/opcache/Optimizer/zend_call_graph.h +++ b/ext/opcache/Optimizer/zend_call_graph.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Call Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -76,11 +76,3 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f END_EXTERN_C() #endif /* ZEND_CALL_GRAPH_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index c31ec73d5e..66c15be311 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, CFG - Control Flow Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -110,7 +110,7 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg * blocks[start].flags = ZEND_BB_START; zend_mark_reachable(op_array->opcodes, cfg, blocks + start); - if (op_array->last_live_range || op_array->last_try_catch) { + if (op_array->last_try_catch) { zend_basic_block *b; int j, changed; uint32_t *block_map = cfg->map; @@ -118,49 +118,6 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg * do { changed = 0; - /* Add live range paths */ - for (j = 0; j < op_array->last_live_range; j++) { - zend_live_range *live_range = &op_array->live_range[j]; - if (live_range->var == (uint32_t)-1) { - /* this live range already removed */ - continue; - } - b = blocks + block_map[live_range->start]; - if (b->flags & ZEND_BB_REACHABLE) { - while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) { - /* check if NOP breaks incorrect smart branch */ - if (b->len == 2 - && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ - || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ) - && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST)) - && b->start > 0 - && zend_is_smart_branch(op_array->opcodes + b->start - 1)) { - break; - } - b->start++; - b->len--; - } - if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) { - /* mark as removed (empty live range) */ - live_range->var = (uint32_t)-1; - continue; - } - b->flags |= ZEND_BB_GEN_VAR; - b = blocks + block_map[live_range->end]; - b->flags |= ZEND_BB_KILL_VAR; - if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) { - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, b); - } else { - b->flags |= ZEND_BB_UNREACHABLE_FREE; - } - } - } else { - ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE)); - } - } - /* Add exception paths */ for (j = 0; j < op_array->last_try_catch; j++) { @@ -237,6 +194,33 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg * } } while (changed); } + + if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) { + zend_basic_block *b; + int j; + uint32_t *block_map = cfg->map; + + /* Mark blocks that are unreachable, but free a loop var created in a reachable block. */ + for (b = blocks; b < blocks + cfg->blocks_count; b++) { + if (b->flags & ZEND_BB_REACHABLE) { + continue; + } + + for (j = b->start; j < b->start + b->len; j++) { + zend_op *opline = &op_array->opcodes[j]; + if (zend_optimizer_is_loop_var_free(opline)) { + zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline); + if (def_opline) { + uint32_t def_block = block_map[def_opline - op_array->opcodes]; + if (blocks[def_block].flags & ZEND_BB_REACHABLE) { + b->flags |= ZEND_BB_UNREACHABLE_FREE; + break; + } + } + } + } + } + } } /* }}} */ @@ -293,7 +277,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b zval *zv; zend_bool extra_entry_block = 0; - cfg->flags = build_flags & (ZEND_CFG_SPLIT_AT_LIVE_RANGES|ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY); + cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY); cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); @@ -389,8 +373,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b } BB_START(i + 1); break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); @@ -413,21 +395,14 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b BB_START(i + 1); break; } - case ZEND_UNSET_VAR: - case ZEND_ISSET_ISEMPTY_VAR: - if (opline->extended_value & ZEND_FETCH_LOCAL) { - flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) && - !op_array->function_name) { - flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; - } - break; case ZEND_FETCH_R: case ZEND_FETCH_W: case ZEND_FETCH_RW: case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_IS: case ZEND_FETCH_UNSET: + case ZEND_UNSET_VAR: + case ZEND_ISSET_ISEMPTY_VAR: if (opline->extended_value & ZEND_FETCH_LOCAL) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) && @@ -440,9 +415,19 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b break; case ZEND_EXT_NOP: case ZEND_EXT_STMT: + flags |= ZEND_FUNC_HAS_EXTENDED_STMT; + break; case ZEND_EXT_FCALL_BEGIN: case ZEND_EXT_FCALL_END: - flags |= ZEND_FUNC_HAS_EXTENDED_INFO; + flags |= ZEND_FUNC_HAS_EXTENDED_FCALL; + break; + case ZEND_FREE: + if (opline->extended_value == ZEND_FREE_SWITCH) { + flags |= ZEND_FUNC_FREE_LOOP_VAR; + } + break; + case ZEND_FE_FREE: + flags |= ZEND_FUNC_FREE_LOOP_VAR; break; } } @@ -453,13 +438,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b extra_entry_block = 1; } - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - for (j = 0; j < op_array->last_live_range; j++) { - BB_START(op_array->live_range[j].start); - BB_START(op_array->live_range[j].end); - } - } - if (op_array->last_try_catch) { for (j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); @@ -554,8 +532,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b block->successors[0] = j + 1; } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: block->successors_count = 2; @@ -915,11 +891,3 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ return SUCCESS; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h index 2f144dbe5c..7d6ef25eee 100644 --- a/ext/opcache/Optimizer/zend_cfg.h +++ b/ext/opcache/Optimizer/zend_cfg.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, CFG - Control Flow Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -29,17 +29,15 @@ #define ZEND_BB_CATCH (1<<6) /* start of catch block */ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */ -#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */ -#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */ #define ZEND_BB_LOOP_HEADER (1<<16) #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17) -#define ZEND_BB_REACHABLE (1<<31) +#define ZEND_BB_REACHABLE (1U<<31) -#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR) +#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE) typedef struct _zend_basic_block { int *successors; /* successor block indices */ @@ -94,12 +92,11 @@ typedef struct _zend_cfg { } zend_cfg; /* Build Flags */ -#define ZEND_RT_CONSTANTS (1<<31) +#define ZEND_RT_CONSTANTS (1U<<31) #define ZEND_CFG_STACKLESS (1<<30) #define ZEND_SSA_DEBUG_LIVENESS (1<<29) #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28) #define ZEND_SSA_RC_INFERENCE (1<<27) -#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26) #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25) #define ZEND_CFG_RECV_ENTRY (1<<24) #define ZEND_CALL_TREE (1<<23) @@ -129,11 +126,3 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg); END_EXTERN_C() #endif /* ZEND_CFG_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index 4430f193a8..e995b673b7 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, DFG - Data Flow Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -52,8 +52,14 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg if (next < end && next->opcode == ZEND_OP_DATA) { if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(next->op1.var); - if (!DFG_ISSET(def, set_size, j, var_num)) { + if (next->op1_type == IS_CV && (opline->opcode == ZEND_ASSIGN_OBJ_REF + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_REF)) { DFG_SET(use, set_size, j, var_num); + DFG_SET(def, set_size, j, var_num); + } else { + if (!DFG_ISSET(def, set_size, j, var_num)) { + DFG_SET(use, set_size, j, var_num); + } } } if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { @@ -92,6 +98,7 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg case ZEND_UNSET_CV: case ZEND_ASSIGN: case ZEND_ASSIGN_REF: + case ZEND_ASSIGN_OBJ_REF: case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_SEND_VAR_EX: @@ -100,18 +107,10 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_NO_REF_EX: case ZEND_FE_RESET_RW: - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: @@ -252,11 +251,3 @@ op2_use: return SUCCESS; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_dfg.h b/ext/opcache/Optimizer/zend_dfg.h index 0c50cef7da..9720f79cc1 100644 --- a/ext/opcache/Optimizer/zend_dfg.h +++ b/ext/opcache/Optimizer/zend_dfg.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, DFG - Data Flow Graph | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -48,11 +48,3 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg END_EXTERN_C() #endif /* ZEND_DFG_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 9e3342e669..d6ef63415a 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Bytecode Visualisation | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -446,12 +446,8 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) { fprintf(stderr, " %u", opline->extended_value); - } else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) { - if (opline->extended_value == ZEND_ASSIGN_DIM) { - fprintf(stderr, " (dim)"); - } else if (opline->extended_value == ZEND_ASSIGN_OBJ) { - fprintf(stderr, " (obj)"); - } + } else if (ZEND_VM_EXT_OP == (flags & ZEND_VM_EXT_MASK)) { + fprintf(stderr, " (%s)", zend_get_opcode_name(opline->extended_value) + 5); } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) { switch (opline->extended_value) { case IS_NULL: @@ -554,7 +550,7 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) { if (opline->extended_value == ZEND_RETURNS_VALUE) { fprintf(stderr, " (value)"); - } else if (opline->extended_value == ZEND_RETURNS_FUNCTION) { + } else if (opline->extended_value & ZEND_RETURNS_FUNCTION) { fprintf(stderr, " (function)"); } } else { @@ -585,6 +581,16 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * fprintf(stderr, " (ref)"); } } + if ((ZEND_VM_EXT_DIM_OBJ_WRITE|ZEND_VM_EXT_FETCH_REF) & flags) { + uint32_t obj_flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; + if (obj_flags == ZEND_FETCH_REF) { + fprintf(stderr, " (ref)"); + } else if (obj_flags == ZEND_FETCH_DIM_WRITE) { + fprintf(stderr, " (dim write)"); + } else if (obj_flags == ZEND_FETCH_OBJ_WRITE) { + fprintf(stderr, " (obj write)"); + } + } } if (opline->op1_type == IS_CONST) { @@ -747,15 +753,12 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags if (b->flags & ZEND_BB_FINALLY_END) { fprintf(stderr, " finally_end"); } - if (b->flags & ZEND_BB_GEN_VAR) { - fprintf(stderr, " gen_var"); - } - if (b->flags & ZEND_BB_KILL_VAR) { - fprintf(stderr, " kill_var"); - } if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) && !(b->flags & ZEND_BB_REACHABLE)) { fprintf(stderr, " unreachable"); } + if (b->flags & ZEND_BB_UNREACHABLE_FREE) { + fprintf(stderr, " unreachable_free"); + } if (b->flags & ZEND_BB_LOOP_HEADER) { fprintf(stderr, " loop_header"); } @@ -918,8 +921,11 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons if (func_flags & ZEND_FUNC_NO_LOOPS) { fprintf(stderr, ", no_loops"); } - if (func_flags & ZEND_FUNC_HAS_EXTENDED_INFO) { - fprintf(stderr, ", extended_info"); + if (func_flags & ZEND_FUNC_HAS_EXTENDED_STMT) { + fprintf(stderr, ", extended_stmt"); + } + if (func_flags & ZEND_FUNC_HAS_EXTENDED_FCALL) { + fprintf(stderr, ", extended_fcall"); } //TODO: this is useful only for JIT??? #if 0 @@ -995,20 +1001,13 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons } } } - if (op_array->last_live_range) { + if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); for (i = 0; i < op_array->last_live_range; i++) { - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - fprintf(stderr, " %u: BB%u - BB%u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - cfg->map[op_array->live_range[i].start], - cfg->map[op_array->live_range[i].end]); - } else { - fprintf(stderr, " %u: L%u - L%u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - op_array->live_range[i].start, - op_array->live_range[i].end); - } + fprintf(stderr, " %u: L%u - L%u ", + EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), + op_array->live_range[i].start, + op_array->live_range[i].end); switch (op_array->live_range[i].var & ZEND_LIVE_MASK) { case ZEND_LIVE_TMPVAR: fprintf(stderr, "(tmp/var)\n"); @@ -1022,6 +1021,9 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons case ZEND_LIVE_ROPE: fprintf(stderr, "(rope)\n"); break; + case ZEND_LIVE_NEW: + fprintf(stderr, "(new)\n"); + break; } } } @@ -1058,7 +1060,7 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons zend_dump_op(op_array, NULL, opline, dump_flags, data); opline++; } - if (op_array->last_live_range) { + if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); for (i = 0; i < op_array->last_live_range; i++) { fprintf(stderr, " %u: L%u - L%u ", @@ -1078,6 +1080,9 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons case ZEND_LIVE_ROPE: fprintf(stderr, "(rope)\n"); break; + case ZEND_LIVE_NEW: + fprintf(stderr, "(new)\n"); + break; } } } @@ -1231,11 +1236,3 @@ void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa) } } } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h index 035bc51731..820f3daf19 100644 --- a/ext/opcache/Optimizer/zend_dump.h +++ b/ext/opcache/Optimizer/zend_dump.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Bytecode Visualisation | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -26,6 +26,7 @@ #define ZEND_DUMP_RC_INFERENCE (1<<1) #define ZEND_DUMP_CFG (1<<2) #define ZEND_DUMP_SSA (1<<3) +#define ZEND_DUMP_LIVE_RANGES (1<<4) #define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS BEGIN_EXTERN_C() @@ -44,11 +45,3 @@ void zend_dump_ht(HashTable *ht); END_EXTERN_C() #endif /* ZEND_DUMP_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c index ba5ef82c4c..ce23207de9 100644 --- a/ext/opcache/Optimizer/zend_func_info.c +++ b/ext/opcache/Optimizer/zend_func_info.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Func Info | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -234,11 +234,12 @@ static const func_info_t func_infos[] = { FC("defined", zend_b_s_info), // TODO: inline FN("get_class", MAY_BE_FALSE | MAY_BE_STRING), FN("get_called_class", MAY_BE_FALSE | MAY_BE_STRING), - FN("get_parrent_class", MAY_BE_FALSE | MAY_BE_STRING), + FN("get_parent_class", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_NULL), F0("is_subclass_of", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline F0("is_a", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline F1("get_class_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), FN("get_object_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), + FN("get_mangled_object_vars", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), F1("get_class_methods", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F0("method_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("property_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), @@ -334,9 +335,7 @@ static const func_info_t func_infos[] = { F1("str_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING), F0("substr_compare", MAY_BE_FALSE | MAY_BE_LONG), -#ifdef HAVE_STRCOLL F0("strcoll", MAY_BE_NULL | MAY_BE_LONG), -#endif #ifdef HAVE_STRFMON F1("money_format", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), #endif @@ -413,6 +412,8 @@ static const func_info_t func_infos[] = { F0("proc_nice", MAY_BE_FALSE | MAY_BE_TRUE), #endif F0("rand", MAY_BE_NULL | MAY_BE_LONG), + F1("random_bytes", MAY_BE_STRING), + F1("random_int", MAY_BE_LONG), F0("srand", MAY_BE_NULL), F0("getrandmax", MAY_BE_NULL | MAY_BE_LONG), F0("mt_rand", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), @@ -500,8 +501,8 @@ static const func_info_t func_infos[] = { F1("sys_getloadavg", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), #endif #ifdef HAVE_GETTIMEOFDAY - F1("microtime", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING), - F1("gettimeofday", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING), + F1("microtime", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_STRING), + F1("gettimeofday", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), #endif #ifdef HAVE_GETRUSAGE F1("getrusage", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), @@ -606,6 +607,7 @@ static const func_info_t func_infos[] = { F0("is_scalar", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("is_callable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("is_countable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("is_iterable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("pclose", MAY_BE_FALSE | MAY_BE_LONG), F1("popen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F0("readfile", MAY_BE_FALSE | MAY_BE_LONG), @@ -670,7 +672,6 @@ static const func_info_t func_infos[] = { F0("stream_set_write_buffer", MAY_BE_FALSE | MAY_BE_LONG), F0("set_file_buffer", MAY_BE_FALSE | MAY_BE_LONG), F0("stream_set_chunk_size", MAY_BE_FALSE | MAY_BE_LONG), - F0("set_socket_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("stream_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("socket_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("stream_get_meta_data", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), @@ -811,7 +812,7 @@ static const func_info_t func_infos[] = { F0("array_unshift", MAY_BE_NULL | MAY_BE_LONG), F1("array_splice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F1("array_slice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_merge", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + FN("array_merge", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F1("array_merge_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F1("array_replace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F1("array_replace_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), @@ -849,6 +850,8 @@ static const func_info_t func_infos[] = { F1("array_chunk", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F1("array_combine", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F0("array_key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + FN("array_key_first", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING), + FN("array_key_last", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING), F1("pos", UNKNOWN_INFO), F0("sizeof", MAY_BE_NULL | MAY_BE_LONG), F0("key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), @@ -877,10 +880,8 @@ static const func_info_t func_infos[] = { F0("mktime", MAY_BE_FALSE | MAY_BE_LONG), F0("gmmktime", MAY_BE_FALSE | MAY_BE_LONG), F0("checkdate", MAY_BE_FALSE | MAY_BE_TRUE), -#ifdef HAVE_STRFTIME F1("strftime", MAY_BE_FALSE | MAY_BE_STRING), F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING), -#endif F0("time", MAY_BE_LONG), F1("localtime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), F1("getdate", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), @@ -932,82 +933,6 @@ static const func_info_t func_infos[] = { F1("preg_grep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), F0("preg_last_error", MAY_BE_NULL | MAY_BE_LONG), - /* ext/ereg */ - F0("ereg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("eregi", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("spliti", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("sql_regcase", MAY_BE_NULL | MAY_BE_STRING), - - /* ext/mysql */ - F1("mysql_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_pconnect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("mysql_close", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_select_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_create_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_drop_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mysql_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE), - F1("mysql_unbuffered_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE), - F1("mysql_db_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE), - F1("mysql_list_dbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_list_tables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_list_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_list_processes", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_errno", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("mysql_affected_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("mysql_insert_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_num_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("mysql_num_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_fetch_row", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("mysql_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("mysql_fetch_assoc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("mysql_fetch_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), - F0("mysql_data_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mysql_fetch_lengths", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("mysql_fetch_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), - F0("mysql_field_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_free_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mysql_field_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_field_table", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_field_len", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_field_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_field_flags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_escape_string", MAY_BE_NULL | MAY_BE_STRING), - F1("mysql_real_escape_string", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_stat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_thread_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_client_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_ping", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mysql_get_client_info", MAY_BE_NULL | MAY_BE_STRING), - F1("mysql_get_host_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_get_proto_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_get_server_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_set_charset", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mysql", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_fieldname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_fieldtable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_fieldlen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_fieldtype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_fieldflags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("mysql_selectdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_createdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_dropdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_freeresult", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mysql_numfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("mysql_numrows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("mysql_listdbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_listtables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_listfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("mysql_db_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_dbname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_tablename", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mysql_table_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - /* ext/mysqli */ F1("mysqli_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F0("mysqli_close", MAY_BE_NULL | MAY_BE_TRUE), @@ -1286,9 +1211,12 @@ static const func_info_t func_infos[] = { /* ext/hash */ F1("hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F0("hash_equals", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("hash_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("hash_hmac", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("hash_hmac_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("hash_hmac_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("hash_hkdf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("hash_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F0("hash_update", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("hash_update_stream", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), @@ -1622,7 +1550,7 @@ static const func_info_t func_infos[] = { F0("imagegd", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("imagegd2", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("imagedestroy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("magecolorallocate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + F0("imagecolorallocate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagepalettecopy", MAY_BE_NULL | MAY_BE_FALSE), F0("imagecolorat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorclosest", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), @@ -1680,6 +1608,17 @@ static const func_info_t func_infos[] = { F0("imagesetinterpolation", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("imageresolution", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), + /* ext/spl */ + F1("class_implements", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), + F1("class_parents", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), + F1("class_uses", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), + F0("iterator_apply", MAY_BE_NULL | MAY_BE_LONG), + F0("iterator_count", MAY_BE_FALSE | MAY_BE_LONG), + F1("iterator_to_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + F1("spl_classes", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), + F1("spl_object_hash", MAY_BE_NULL | MAY_BE_STRING), + F0("spl_object_id", MAY_BE_NULL | MAY_BE_LONG), + }; static HashTable func_info; @@ -1773,11 +1712,3 @@ int zend_func_info_shutdown(void) } return SUCCESS; } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h index 45d38ddc2c..7eeb363da5 100644 --- a/ext/opcache/Optimizer/zend_func_info.h +++ b/ext/opcache/Optimizer/zend_func_info.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, Func Info | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -27,10 +27,12 @@ #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */ #define ZEND_FUNC_NO_LOOPS (1<<3) #define ZEND_FUNC_IRREDUCIBLE (1<<4) +#define ZEND_FUNC_FREE_LOOP_VAR (1<<5) #define ZEND_FUNC_RECURSIVE (1<<7) #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8) #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9) -#define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10) +#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10) +#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11) /* The following flags are valid only for return values of internal functions * returned by zend_get_func_info() @@ -60,11 +62,3 @@ int zend_func_info_shutdown(void); END_EXTERN_C() #endif /* ZEND_FUNC_INFO_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 2579d3ff96..fa494794cb 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, e-SSA based Type & Range Inference | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -535,31 +535,16 @@ static inline zend_bool zend_abs_range( return 1; } +static inline zend_long safe_shift_left(zend_long n, zend_long s) { + return (zend_long) ((zend_ulong) n << (zend_ulong) s); +} + static inline zend_bool shift_left_overflows(zend_long n, zend_long s) { /* This considers shifts that shift in the sign bit to be overflowing as well */ if (n >= 0) { - return s >= SIZEOF_ZEND_LONG * 8 - 1 || (n << s) < n; + return s >= SIZEOF_ZEND_LONG * 8 - 1 || safe_shift_left(n, s) < n; } else { - return s >= SIZEOF_ZEND_LONG * 8 - 1 || (n << s) > n; - } -} - -/* Get the normal op corresponding to a compound assignment op */ -static inline zend_uchar get_compound_assign_op(zend_uchar opcode) { - switch (opcode) { - case ZEND_ASSIGN_ADD: return ZEND_ADD; - case ZEND_ASSIGN_SUB: return ZEND_SUB; - case ZEND_ASSIGN_MUL: return ZEND_MUL; - case ZEND_ASSIGN_DIV: return ZEND_DIV; - case ZEND_ASSIGN_MOD: return ZEND_MOD; - case ZEND_ASSIGN_SL: return ZEND_SL; - case ZEND_ASSIGN_SR: return ZEND_SR; - case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT; - case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR; - case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND; - case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR; - case ZEND_ASSIGN_POW: return ZEND_POW; - EMPTY_SWITCH_DEFAULT_CASE() + return s >= SIZEOF_ZEND_LONG * 8 || safe_shift_left(n, s) > n; } } @@ -575,19 +560,21 @@ static int zend_inference_calc_binary_op_range( op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - tmp->min = op1_min + op2_min; - tmp->max = op1_max + op2_max; if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() || - (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) { + zend_add_will_overflow(op1_min, op2_min)) { tmp->underflow = 1; tmp->min = ZEND_LONG_MIN; + } else { + tmp->min = op1_min + op2_min; } if (OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() || - (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) { + zend_add_will_overflow(op1_max, op2_max)) { tmp->overflow = 1; tmp->max = ZEND_LONG_MAX; + } else { + tmp->max = op1_max + op2_max; } return 1; } @@ -598,42 +585,47 @@ static int zend_inference_calc_binary_op_range( op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - tmp->min = op1_min - op2_max; - tmp->max = op1_max - op2_min; if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_OVERFLOW() || - (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) { + zend_sub_will_overflow(op1_min, op2_max)) { tmp->underflow = 1; tmp->min = ZEND_LONG_MIN; + } else { + tmp->min = op1_min - op2_max; } if (OP1_RANGE_OVERFLOW() || OP2_RANGE_UNDERFLOW() || - (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) { + zend_sub_will_overflow(op1_max, op2_min)) { tmp->overflow = 1; tmp->max = ZEND_LONG_MAX; + } else { + tmp->max = op1_max - op2_min; } return 1; } break; case ZEND_MUL: if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { + double dummy; + zend_long t1_overflow, t2_overflow, t3_overflow, t4_overflow; op1_min = OP1_MIN_RANGE(); op2_min = OP2_MIN_RANGE(); op1_max = OP1_MAX_RANGE(); op2_max = OP2_MAX_RANGE(); - t1 = op1_min * op2_min; - t2 = op1_min * op2_max; - t3 = op1_max * op2_min; - t4 = op1_max * op2_max; + /* Suppress uninit variable warnings, these will only be used if the overflow + * flags are all false. */ + t1 = t2 = t3 = t4 = 0; + ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_min, t1, dummy, t1_overflow); + ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_max, t2, dummy, t2_overflow); + ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_min, t3, dummy, t3_overflow); + ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_max, t4, dummy, t4_overflow); + (void) dummy; + // FIXME: more careful overflow checks? - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW() || - (double)t1 != (double)op1_min * (double)op2_min || - (double)t2 != (double)op1_min * (double)op2_max || - (double)t3 != (double)op1_max * (double)op2_min || - (double)t4 != (double)op1_max * (double)op2_max) { + if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() || + OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() || + t1_overflow || t2_overflow || t3_overflow || t4_overflow + ) { tmp->underflow = 1; tmp->overflow = 1; tmp->min = ZEND_LONG_MIN; @@ -751,10 +743,10 @@ static int zend_inference_calc_binary_op_range( tmp->min = ZEND_LONG_MIN; tmp->max = ZEND_LONG_MAX; } else { - t1 = op1_min << op2_min; - t2 = op1_min << op2_max; - t3 = op1_max << op2_min; - t4 = op1_max << op2_max; + t1 = safe_shift_left(op1_min, op2_min); + t2 = safe_shift_left(op1_min, op2_max); + t3 = safe_shift_left(op1_max, op2_min); + t4 = safe_shift_left(op1_max, op2_max); tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); } @@ -1208,6 +1200,7 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int case ZEND_QM_ASSIGN: case ZEND_JMP_SET: case ZEND_COALESCE: + case ZEND_COPY_TMP: if (ssa->ops[line].op1_def == var) { if (ssa->ops[line].op1_def >= 0) { if (OP1_HAS_RANGE()) { @@ -1369,23 +1362,20 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int } } break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value == 0) { + case ZEND_ASSIGN_OP: + if (opline->extended_value != ZEND_CONCAT + && opline->extended_value != ZEND_POW) { if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) { return zend_inference_calc_binary_op_range( op_array, ssa, opline, &ssa->ops[line], - get_compound_assign_op(opline->opcode), tmp); + opline->extended_value, tmp); } - } else if ((opline+1)->opcode == ZEND_OP_DATA) { + } + break; + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: + if ((opline+1)->opcode == ZEND_OP_DATA) { if (ssa->ops[line+1].op1_def == var) { opline++; if (OP1_HAS_RANGE()) { @@ -1398,13 +1388,13 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int } } break; -// case ZEND_ASSIGN_CONCAT: case ZEND_OP_DATA: if ((opline-1)->opcode == ZEND_ASSIGN_DIM || (opline-1)->opcode == ZEND_ASSIGN_OBJ || - (opline-1)->opcode == ZEND_ASSIGN_ADD || - (opline-1)->opcode == ZEND_ASSIGN_SUB || - (opline-1)->opcode == ZEND_ASSIGN_MUL) { + ((opline-1)->opcode == ZEND_ASSIGN_OP && + ((opline-1)->extended_value == ZEND_ADD || + (opline-1)->extended_value == ZEND_SUB || + (opline-1)->extended_value == ZEND_MUL))) { if (ssa->ops[line].op1_def == var) { if (OP1_HAS_RANGE()) { tmp->min = OP1_MIN_RANGE(); @@ -2257,6 +2247,24 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_ return NULL; } +static uint32_t zend_convert_type_code_to_may_be(zend_uchar type_code) { + switch (type_code) { + case IS_VOID: + return MAY_BE_NULL; + case IS_CALLABLE: + return MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case IS_ITERABLE: + return MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case IS_ARRAY: + return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case _IS_BOOL: + return MAY_BE_TRUE|MAY_BE_FALSE; + default: + ZEND_ASSERT(type_code < IS_REFERENCE); + return 1 << type_code; + } +} + static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) { uint32_t tmp = 0; @@ -2269,22 +2277,7 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar *pce = get_class_entry(script, lcname); zend_string_release_ex(lcname, 0); } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - zend_uchar type_hint = ZEND_TYPE_CODE(arg_info->type); - - if (type_hint == IS_VOID) { - tmp |= MAY_BE_NULL; - } else if (type_hint == IS_CALLABLE) { - tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == IS_ITERABLE) { - tmp |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == IS_ARRAY) { - tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == _IS_BOOL) { - tmp |= MAY_BE_TRUE|MAY_BE_FALSE; - } else { - ZEND_ASSERT(type_hint < IS_REFERENCE); - tmp |= 1 << type_hint; - } + tmp |= zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(arg_info->type)); } else { tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } @@ -2294,6 +2287,124 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar return tmp; } +static zend_property_info *lookup_prop_info(zend_class_entry *ce, zend_string *name, zend_class_entry *scope) { + zend_property_info *prop_info; + + /* If the class is linked, reuse the precise runtime logic. */ + if ((ce->ce_flags & ZEND_ACC_LINKED) + && (!scope || (scope->ce_flags & ZEND_ACC_LINKED))) { + zend_class_entry *prev_scope = EG(fake_scope); + EG(fake_scope) = scope; + prop_info = zend_get_property_info(ce, name, 1); + EG(fake_scope) = prev_scope; + if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) { + return prop_info; + } + return NULL; + } + + /* Otherwise, handle only some safe cases */ + prop_info = zend_hash_find_ptr(&ce->properties_info, name); + if (prop_info && + ((prop_info->ce == scope) || + (!scope && (prop_info->flags & ZEND_ACC_PUBLIC))) + ) { + return prop_info; + } + return NULL; +} + +static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int i) +{ + zend_property_info *prop_info = NULL; + if (opline->op2_type == IS_CONST) { + zend_class_entry *ce = NULL; + + if (opline->op1_type == IS_UNUSED) { + ce = op_array->scope; + } else if (ssa->ops[i].op1_use >= 0) { + ce = ssa->var_info[ssa->ops[i].op1_use].ce; + } + if (ce) { + prop_info = lookup_prop_info(ce, + Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)), + op_array->scope); + if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) { + prop_info = NULL; + } + } + } + return prop_info; +} + +static zend_property_info *zend_fetch_static_prop_info(const zend_script *script, const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline) +{ + zend_property_info *prop_info = NULL; + if (opline->op1_type == IS_CONST) { + zend_class_entry *ce = NULL; + if (opline->op2_type == IS_UNUSED) { + int fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK; + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + case ZEND_FETCH_CLASS_STATIC: + /* We enforce that static property types cannot change during inheritance, so + * handling static the same way as self here is legal. */ + ce = op_array->scope; + break; + case ZEND_FETCH_CLASS_PARENT: + if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { + ce = op_array->scope->parent; + } + break; + } + } else if (opline->op2_type == IS_CONST) { + zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants); + ce = get_class_entry(script, Z_STR_P(zv + 1)); + } + + if (ce) { + zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants); + prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope); + if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) { + prop_info = NULL; + } + } + } + return prop_info; +} + +static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_info *prop_info, zend_class_entry **pce) +{ + if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { + uint32_t type = ZEND_TYPE_IS_CLASS(prop_info->type) + ? MAY_BE_OBJECT + : zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(prop_info->type)); + + if (ZEND_TYPE_ALLOW_NULL(prop_info->type)) { + type |= MAY_BE_NULL; + } + if (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { + type |= MAY_BE_RC1 | MAY_BE_RCN; + } + if (pce) { + if (ZEND_TYPE_IS_CE(prop_info->type)) { + *pce = ZEND_TYPE_CE(prop_info->type); + } else if (ZEND_TYPE_IS_NAME(prop_info->type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(prop_info->type)); + *pce = get_class_entry(script, lcname); + zend_string_release(lcname); + } else { + *pce = NULL; + } + } + return type; + } + if (pce) { + *pce = NULL; + } + return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN; +} + static int zend_update_type_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script, @@ -2393,6 +2504,13 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_IN_ARRAY: UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def); break; + case ZEND_ARRAY_KEY_EXISTS: + tmp = MAY_BE_FALSE|MAY_BE_TRUE; + if (t2 & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_ARRAY|MAY_BE_OBJECT))) { + tmp |= MAY_BE_NULL; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + break; case ZEND_CAST: if (ssa_ops[i].op1_def >= 0) { tmp = t1; @@ -2444,6 +2562,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_QM_ASSIGN: case ZEND_JMP_SET: case ZEND_COALESCE: + case ZEND_COPY_TMP: if (ssa_ops[i].op1_def >= 0) { tmp = t1; if ((t1 & (MAY_BE_RC1|MAY_BE_REF)) && (opline->op1_type == IS_CV)) { @@ -2473,32 +2592,30 @@ static int zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def); break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: + { + zend_property_info *prop_info = NULL; orig = 0; tmp = 0; - if (opline->extended_value == ZEND_ASSIGN_OBJ) { - tmp |= MAY_BE_REF; + if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { + prop_info = zend_fetch_prop_info(op_array, ssa, opline, i); orig = t1; - t1 = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + t1 = zend_fetch_prop_type(script, prop_info, &ce); t2 = OP1_DATA_INFO(); - } else if (opline->extended_value == ZEND_ASSIGN_DIM) { + } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) { if (t1 & MAY_BE_ARRAY_OF_REF) { tmp |= MAY_BE_REF; } orig = t1; t1 = zend_array_element_type(t1, 1, 0); t2 = OP1_DATA_INFO(); + } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { + prop_info = zend_fetch_static_prop_info(script, op_array, ssa, opline); + t1 = zend_fetch_prop_type(script, prop_info, &ce); + t2 = OP1_DATA_INFO(); } else { if (t1 & MAY_BE_REF) { tmp |= MAY_BE_REF; @@ -2506,7 +2623,7 @@ static int zend_update_type_info(const zend_op_array *op_array, } tmp |= binary_op_result_type( - ssa, get_compound_assign_op(opline->opcode), t1, t2, ssa_ops[i].op1_def, optimization_level); + ssa, opline->extended_value, t1, t2, ssa_ops[i].op1_def, optimization_level); if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) { tmp |= MAY_BE_RC1; } @@ -2514,13 +2631,13 @@ static int zend_update_type_info(const zend_op_array *op_array, tmp |= MAY_BE_RC1 | MAY_BE_RCN; } - if (opline->extended_value == ZEND_ASSIGN_DIM) { + if (opline->opcode == ZEND_ASSIGN_DIM_OP) { if (opline->op1_type == IS_CV) { orig = assign_dim_result_type(orig, OP2_INFO(), tmp, opline->op2_type); UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def); COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } - } else if (opline->extended_value == ZEND_ASSIGN_OBJ) { + } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { if (opline->op1_type == IS_CV) { if (!(orig & MAY_BE_REF)) { if (orig & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { @@ -2534,11 +2651,14 @@ static int zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def); COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } + } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP) { + /* Nothing to do */ } else { UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { - if (opline->extended_value == ZEND_ASSIGN_DIM) { + ce = NULL; + if (opline->opcode == ZEND_ASSIGN_DIM_OP) { if (opline->op2_type == IS_UNUSED) { /* When appending to an array and the LONG_MAX key is already used * null will be returned. */ @@ -2553,16 +2673,31 @@ static int zend_update_type_info(const zend_op_array *op_array, * results in a null return value. */ tmp |= MAY_BE_NULL; } - } else if (opline->extended_value == ZEND_ASSIGN_OBJ) { + } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { if (orig & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT))) { /* null and false (and empty string) are implicitly converted to object, * anything else results in a null return value. */ tmp |= MAY_BE_NULL; } + + /* The return value must also satisfy the property type */ + if (prop_info) { + tmp &= zend_fetch_prop_type(script, prop_info, NULL); + } + } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { + /* The return value must also satisfy the property type */ + if (prop_info) { + tmp &= zend_fetch_prop_type(script, prop_info, NULL); + } } + tmp &= ~MAY_BE_REF; UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } } break; + } case ZEND_PRE_INC: case ZEND_PRE_DEC: tmp = 0; @@ -2737,9 +2872,13 @@ static int zend_update_type_info(const zend_op_array *op_array, COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { - // TODO: ??? - tmp = MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + // TODO: If there is no __set we might do better + tmp = zend_fetch_prop_type(script, + zend_fetch_prop_info(op_array, ssa, opline, i), &ce); UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } } if ((opline+1)->op1_type == IS_CV) { opline++; @@ -2837,6 +2976,49 @@ static int zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); } break; + case ZEND_ASSIGN_OBJ_REF: + if (opline->op1_type == IS_CV) { + tmp = t1; + if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { + tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE); + tmp |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN; + } + if (tmp & MAY_BE_OBJECT) { + tmp |= MAY_BE_RC1 | MAY_BE_RCN; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); + COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); + } + + t2 = OP1_DATA_INFO(); + if ((opline+1)->op1_type == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION)) { + tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF; + } else { + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_RC1|MAY_BE_RCN); + } + if (t2 & MAY_BE_UNDEF) { + tmp |= MAY_BE_NULL; + } + if (ssa_ops[i].result_def >= 0) { + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + } + if ((opline+1)->op1_type == IS_CV) { + opline++; + i++; + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); + if (t2 & MAY_BE_UNDEF) { + tmp |= MAY_BE_NULL; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); + } + break; + case ZEND_ASSIGN_STATIC_PROP_REF: + if ((opline+1)->op1_type == IS_CV) { + opline++; + i++; + UPDATE_SSA_TYPE(MAY_BE_REF, ssa_ops[i].op1_def); + } + break; case ZEND_BIND_GLOBAL: tmp = MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; @@ -2845,6 +3027,9 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_BIND_STATIC: tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | ((opline->extended_value & ZEND_BIND_REF) ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN)); + if (opline->extended_value & ZEND_BIND_IMPLICIT) { + tmp |= MAY_BE_UNDEF; + } UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); break; case ZEND_SEND_VAR: @@ -2982,10 +3167,7 @@ static int zend_update_type_info(const zend_op_array *op_array, } break; } - case ZEND_DECLARE_CLASS: - case ZEND_DECLARE_INHERITED_CLASS: case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def); if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)))) != NULL) { UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def); @@ -3003,7 +3185,7 @@ static int zend_update_type_info(const zend_op_array *op_array, } break; case ZEND_FETCH_CLASS_PARENT: - if (op_array->scope && op_array->scope->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_ops[i].result_def); } else { UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def); @@ -3067,6 +3249,9 @@ static int zend_update_type_info(const zend_op_array *op_array, } if (ssa_ops[i].result_def >= 0) { tmp = MAY_BE_RC1|MAY_BE_ARRAY; + if (ssa_ops[i].result_use >= 0) { + tmp |= ssa_var_info[ssa_ops[i].result_use].type; + } if (opline->op1_type != IS_UNUSED) { tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT; if (t1 & MAY_BE_UNDEF) { @@ -3075,30 +3260,39 @@ static int zend_update_type_info(const zend_op_array *op_array, if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } - } - if (ssa_ops[i].result_use >= 0) { - tmp |= ssa_var_info[ssa_ops[i].result_use].type; - } - if (opline->op2_type == IS_UNUSED) { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } else { - if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE)) { + if (opline->op2_type == IS_UNUSED) { tmp |= MAY_BE_ARRAY_KEY_LONG; - } - if (t2 & (MAY_BE_STRING)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - if (opline->op2_type != IS_CONST) { - // FIXME: numeric string + } else { + if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) { tmp |= MAY_BE_ARRAY_KEY_LONG; } - } - if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; + if (t2 & (MAY_BE_STRING)) { + tmp |= MAY_BE_ARRAY_KEY_STRING; + if (opline->op2_type != IS_CONST) { + // FIXME: numeric string + tmp |= MAY_BE_ARRAY_KEY_LONG; + } + } + if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) { + tmp |= MAY_BE_ARRAY_KEY_STRING; + } } } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); } break; + case ZEND_ADD_ARRAY_UNPACK: + tmp = ssa_var_info[ssa_ops[i].result_use].type; + ZEND_ASSERT(tmp & MAY_BE_ARRAY); + /* Ignore string keys as they will throw. */ + if (t1 & MAY_BE_ARRAY_KEY_LONG) { + tmp |= MAY_BE_ARRAY_KEY_LONG | (t1 & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)); + } + if (t1 & MAY_BE_OBJECT) { + tmp |= MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + break; case ZEND_UNSET_CV: tmp = MAY_BE_UNDEF; if (!op_array->function_name) { @@ -3233,79 +3427,67 @@ static int zend_update_type_info(const zend_op_array *op_array, tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN); } } - j = ssa_vars[ssa_ops[i].result_def].use_chain; - while (j >= 0) { - switch (op_array->opcodes[j].opcode) { - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM: - tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY; - break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - if (op_array->opcodes[j].extended_value == ZEND_ASSIGN_DIM) { + if (opline->opcode == ZEND_FETCH_DIM_RW + || opline->opcode == ZEND_FETCH_DIM_W + || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG + || opline->opcode == ZEND_FETCH_LIST_W) { + j = ssa_vars[ssa_ops[i].result_def].use_chain; + while (j >= 0) { + switch (op_array->opcodes[j].opcode) { + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_LIST_W: + case ZEND_ASSIGN_DIM: + case ZEND_ASSIGN_DIM_OP: tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY; - } else if (op_array->opcodes[j].extended_value == ZEND_ASSIGN_OBJ) { + break; + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_OBJ_REF: + case ZEND_PRE_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: tmp |= MAY_BE_ARRAY_OF_OBJECT; - } - break; - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_ASSIGN_OBJ: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - tmp |= MAY_BE_ARRAY_OF_OBJECT; - break; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_REF: - case ZEND_ASSIGN_REF: - case ZEND_YIELD: - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_RETURN_BY_REF: - case ZEND_VERIFY_RETURN_TYPE: - case ZEND_MAKE_REF: - case ZEND_FE_RESET_RW: - tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - break; - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - if (tmp & MAY_BE_ARRAY_OF_LONG) { - /* may overflow */ - tmp |= MAY_BE_ARRAY_OF_DOUBLE; - } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) { - tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE; - } - break; - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_OBJ_UNSET: - break; - default : - break; + break; + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_REF: + case ZEND_ASSIGN_REF: + case ZEND_YIELD: + case ZEND_INIT_ARRAY: + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_RETURN_BY_REF: + case ZEND_VERIFY_RETURN_TYPE: + case ZEND_MAKE_REF: + case ZEND_FE_RESET_RW: + tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + break; + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: + if (tmp & MAY_BE_ARRAY_OF_LONG) { + /* may overflow */ + tmp |= MAY_BE_ARRAY_OF_DOUBLE; + } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) { + tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE; + } + break; + case ZEND_SEND_VAR: + /* This can occur if a DIM_FETCH_FUNC_ARG with UNUSED op2 is left + * behind, because it can't be converted to DIM_FETCH_R. */ + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + j = zend_ssa_next_use(ssa_ops, ssa_ops[i].result_def, j); } - j = zend_ssa_next_use(ssa_ops, ssa_ops[i].result_def, j); } if ((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) { UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); @@ -3363,14 +3545,31 @@ static int zend_update_type_info(const zend_op_array *op_array, COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - if (opline->result_type == IS_TMP_VAR) { - /* can't be REF because of ZVAL_COPY_DEREF() usage */ - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else { - tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ERROR; + tmp = zend_fetch_prop_type(script, + zend_fetch_prop_info(op_array, ssa, opline, i), &ce); + if (opline->result_type != IS_TMP_VAR) { + tmp |= MAY_BE_REF | MAY_BE_ERROR; } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } + } + break; + case ZEND_FETCH_STATIC_PROP_R: + case ZEND_FETCH_STATIC_PROP_IS: + case ZEND_FETCH_STATIC_PROP_RW: + case ZEND_FETCH_STATIC_PROP_W: + case ZEND_FETCH_STATIC_PROP_UNSET: + case ZEND_FETCH_STATIC_PROP_FUNC_ARG: + tmp = zend_fetch_prop_type(script, + zend_fetch_static_prop_info(script, op_array, ssa, opline), &ce); + if (opline->result_type != IS_TMP_VAR) { + tmp |= MAY_BE_REF | MAY_BE_ERROR; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); } break; case ZEND_DO_FCALL: @@ -3493,9 +3692,11 @@ unknown_opcode: static uint32_t get_class_entry_rank(zend_class_entry *ce) { uint32_t rank = 0; - while (ce->parent) { - rank++; - ce = ce->parent; + if (ce->ce_flags & ZEND_ACC_LINKED) { + while (ce->parent) { + rank++; + ce = ce->parent; + } } return rank; } @@ -3516,17 +3717,17 @@ static zend_class_entry *join_class_entries( while (rank1 != rank2) { if (rank1 > rank2) { - ce1 = ce1->parent; + ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; rank1--; } else { - ce2 = ce2->parent; + ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; rank2--; } } while (ce1 != ce2) { - ce1 = ce1->parent; - ce2 = ce2->parent; + ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; + ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; } if (ce1) { @@ -3557,6 +3758,9 @@ int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script if (!p->has_range_constraint) { zend_ssa_type_constraint *constraint = &p->constraint.type; tmp &= constraint->type_mask; + if (!(tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { + tmp &= ~(MAY_BE_RC1|MAY_BE_RCN); + } if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) { if (!ce) { ce = constraint->ce; @@ -3740,13 +3944,19 @@ static zend_bool can_convert_to_double( return 0; } } else { + zend_uchar opcode = opline->opcode; + + if (opcode == ZEND_ASSIGN_OP) { + opcode = opline->extended_value; + } + /* Avoid division by zero */ - if (opline->opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) { + if (opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) { return 0; } - get_binary_op(opline->opcode)(&orig_result, &orig_op1, &orig_op2); - get_binary_op(opline->opcode)(&dval_result, &dval_op1, &dval_op2); + get_binary_op(opcode)(&orig_result, &orig_op1, &orig_op2); + get_binary_op(opcode)(&dval_result, &dval_op1, &dval_op2); ZEND_ASSERT(Z_TYPE(dval_result) == IS_DOUBLE); if (zval_get_double(&orig_result) != Z_DVAL(dval_result)) { return 0; @@ -4190,6 +4400,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_REF: case ZEND_BIND_GLOBAL: + case ZEND_BIND_STATIC: case ZEND_FETCH_DIM_IS: case ZEND_FETCH_OBJ_IS: case ZEND_SEND_REF: @@ -4201,7 +4412,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) { switch (opline->opcode) { case ZEND_CASE: @@ -4225,9 +4436,9 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } + } - if (opline->op2_type == IS_CV) { + if (opline->op2_type == IS_CV) { if (t2 & MAY_BE_UNDEF) { switch (opline->opcode) { case ZEND_ASSIGN_REF: @@ -4247,7 +4458,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } + } switch (opline->opcode) { case ZEND_NOP: @@ -4276,6 +4487,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa case ZEND_ISSET_ISEMPTY_CV: case ZEND_FUNC_NUM_ARGS: case ZEND_FUNC_GET_ARGS: + case ZEND_COPY_TMP: return 0; case ZEND_INIT_FCALL: /* can't throw, because call is resolved at compile time */ @@ -4352,63 +4564,53 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 0; } return (t1 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)) || (t2 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)); - case ZEND_ASSIGN_ADD: - if (opline->extended_value != 0) { - return 1; - } - if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY - && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - if (opline->extended_value != 0) { - return 1; - } - if (!OP2_HAS_RANGE() || - (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) { - /* Division by zero */ - return 1; - } - /* break missing intentionally */ - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_POW: - if (opline->extended_value != 0) { - return 1; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - if (opline->extended_value != 0) { - return 1; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || - !OP2_HAS_RANGE() || - OP2_MIN_RANGE() < 0; - case ZEND_ASSIGN_CONCAT: - if (opline->extended_value != 0) { - return 1; + case ZEND_ASSIGN_OP: + if (opline->extended_value == ZEND_ADD) { + if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY + && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) { + return 0; + } + return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); + } else if (opline->extended_value == ZEND_DIV || + opline->extended_value == ZEND_MOD) { + if (!OP2_HAS_RANGE() || + (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) { + /* Division by zero */ + return 1; + } + return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); + } else if (opline->extended_value == ZEND_SUB || + opline->extended_value == ZEND_MUL || + opline->extended_value == ZEND_POW) { + return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); + } else if (opline->extended_value == ZEND_SL || + opline->extended_value == ZEND_SR) { + return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + !OP2_HAS_RANGE() || + OP2_MIN_RANGE() < 0; + } else if (opline->extended_value == ZEND_CONCAT) { + return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); + } else if (opline->extended_value == ZEND_BW_OR || + opline->extended_value == ZEND_BW_AND || + opline->extended_value == ZEND_BW_XOR) { + if ((t1 & MAY_BE_ANY) == MAY_BE_STRING + && (t2 & MAY_BE_ANY) == MAY_BE_STRING) { + return 0; + } + return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || + (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); } - return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value != 0) { + return 1; + case ZEND_ASSIGN: + if (t1 & MAY_BE_REF) { return 1; } - if ((t1 & MAY_BE_ANY) == MAY_BE_STRING - && (t2 & MAY_BE_ANY) == MAY_BE_STRING) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_ASSIGN: + case ZEND_BIND_STATIC: case ZEND_UNSET_VAR: return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)); case ZEND_ASSIGN_DIM: @@ -4514,11 +4716,3 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index 297be8e616..ec98fcbef9 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, e-SSA based Type & Range Inference | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -238,6 +238,14 @@ DEFINE_SSA_OP_DEF_INFO(result) #define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1))) #define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline)) +static zend_always_inline zend_bool zend_add_will_overflow(zend_long a, zend_long b) { + return (b > 0 && a > ZEND_LONG_MAX - b) + || (b < 0 && a < ZEND_LONG_MIN - b); +} +static zend_always_inline zend_bool zend_sub_will_overflow(zend_long a, zend_long b) { + return (b > 0 && a < ZEND_LONG_MIN + b) + || (b < 0 && a > ZEND_LONG_MAX + b); +} BEGIN_EXTERN_C() @@ -269,11 +277,3 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa END_EXTERN_C() #endif /* ZEND_INFERENCE_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 09e68cdbc4..dda9e86e53 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -53,25 +53,6 @@ void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* zend_hash_add(ctx->constants, Z_STR_P(name), &val); } -zend_uchar zend_compound_assign_to_binary_op(zend_uchar opcode) -{ - switch (opcode) { - case ZEND_ASSIGN_ADD: return ZEND_ADD; - case ZEND_ASSIGN_SUB: return ZEND_SUB; - case ZEND_ASSIGN_MUL: return ZEND_MUL; - case ZEND_ASSIGN_DIV: return ZEND_DIV; - case ZEND_ASSIGN_MOD: return ZEND_MOD; - case ZEND_ASSIGN_SL: return ZEND_SL; - case ZEND_ASSIGN_SR: return ZEND_SR; - case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT; - case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR; - case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND; - case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR; - case ZEND_ASSIGN_POW: return ZEND_POW; - EMPTY_SWITCH_DEFAULT_CASE() - } -} - int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */ { binary_op_type binary_op = get_binary_op(opcode); @@ -256,6 +237,14 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, zval *val) { switch (opline->opcode) { + case ZEND_OP_DATA: + switch ((opline-1)->opcode) { + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_STATIC_PROP_REF: + return 0; + } + opline->op1.constant = zend_optimizer_add_literal(op_array, val); + break; case ZEND_FREE: case ZEND_CHECK_VAR: MAKE_NOP(opline); @@ -312,6 +301,13 @@ int 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_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + break; + case ZEND_ASSIGN_STATIC_PROP_OP: + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -319,21 +315,17 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: - TO_STRING_NOWARN(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type == IS_CONST && opline->extended_value + sizeof(void*) == op_array->cache_size) { - op_array->cache_size += sizeof(void *); - } else { - opline->extended_value = alloc_cache_slots(op_array, 2); - } - break; case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: TO_STRING_NOWARN(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_ISEMPTY) + sizeof(void*) == op_array->cache_size) { + if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) { op_array->cache_size += sizeof(void *); } else { - opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } break; case ZEND_SEND_VAR: @@ -350,6 +342,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, return 0; case ZEND_CASE: case ZEND_FETCH_LIST_R: + case ZEND_COPY_TMP: return 0; case ZEND_CONCAT: case ZEND_FAST_CONCAT: @@ -399,13 +392,6 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); opline->extended_value = alloc_cache_slots(op_array, 1); break; - case ZEND_ADD_INTERFACE: - case ZEND_ADD_TRAIT: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; case ZEND_INIT_FCALL_BY_NAME: REQUIRES_STRING(val); drop_leading_backslash(val); @@ -413,6 +399,8 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); opline->result.num = alloc_cache_slots(op_array, 1); break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -420,21 +408,17 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_OP: REQUIRES_STRING(val); drop_leading_backslash(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); if (opline->op1_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1); - } - break; - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - if (opline->op1_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS)); } break; case ZEND_INIT_FCALL: @@ -485,6 +469,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -496,49 +481,17 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_PRE_DEC_OBJ: case ZEND_POST_INC_OBJ: case ZEND_POST_DEC_OBJ: + case ZEND_ASSIGN_OBJ_OP: TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 2); + opline->extended_value = alloc_cache_slots(op_array, 3); break; case ZEND_ISSET_ISEMPTY_PROP_OBJ: TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY); - break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_POW: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value == ZEND_ASSIGN_OBJ) { - TO_STRING_NOWARN(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - (opline+1)->extended_value = alloc_cache_slots(op_array, 2); - } else if (opline->extended_value == ZEND_ASSIGN_DIM) { - if (Z_TYPE_P(val) == IS_STRING) { - zend_ulong index; - - if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { - ZVAL_LONG(&tmp, index); - opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - zend_string_hash_val(Z_STR_P(val)); - zend_optimizer_add_literal(op_array, val); - Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE; - break; - } - } - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - } else { - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - } + opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY); break; + case ZEND_ASSIGN_DIM_OP: case ZEND_ISSET_ISEMPTY_DIM_OBJ: case ZEND_ASSIGN_DIM: case ZEND_UNSET_DIM: @@ -597,104 +550,6 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, return 1; } -void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) -{ - if (op_array->last_live_range) { - int i = 0; - int j = 0; - - do { - if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) { - if (i != j) { - op_array->live_range[j] = op_array->live_range[i]; - } - j++; - } - i++; - } while (i < op_array->last_live_range); - if (i != j) { - op_array->last_live_range = j; - if (j == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } - } - } -} - -static uint32_t zend_determine_constructor_call(zend_op_array *op_array, uint32_t start) { - int call = 0; - while (++start < op_array->last) { - switch (op_array->opcodes[start].opcode) { - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_FCALL: - case ZEND_NEW: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_USER_CALL: - call++; - break; - case ZEND_DO_FCALL: - if (call == 0) { - return start; - } - /* break missing intentionally */ - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - call--; - break; - default: - break; - } - } - - ZEND_ASSERT(0); - return -1; -} - -void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start) -{ - uint32_t i = 0; - - switch (op_array->opcodes[start].opcode) { - case ZEND_ROPE_ADD: - case ZEND_ADD_ARRAY_ELEMENT: - return; - case ZEND_ROPE_INIT: - var |= ZEND_LIVE_ROPE; - break; - case ZEND_BEGIN_SILENCE: - var |= ZEND_LIVE_SILENCE; - break; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - var |= ZEND_LIVE_LOOP; - start++; - break; - case ZEND_NEW: - start = zend_determine_constructor_call(op_array, start); - start++; - break; - default: - start++; - } - - while (i < op_array->last_live_range) { - if (op_array->live_range[i].var == var - && op_array->live_range[i].start == start) { - op_array->last_live_range--; - if (i < op_array->last_live_range) { - memmove(&op_array->live_range[i], &op_array->live_range[i+1], (op_array->last_live_range - i) * sizeof(zend_live_range)); - } - break; - } - i++; - } -} - int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *opline, zend_uchar type, @@ -760,67 +615,44 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var); MAKE_NOP(m); zval_ptr_dtor_nogc(val); - zend_optimizer_remove_live_range(op_array, var); return 1; } case ZEND_SWITCH_LONG: case ZEND_SWITCH_STRING: - case ZEND_CASE: - case ZEND_FREE: { - zend_op *m, *n; - int brk = op_array->last_live_range; - zend_bool in_switch = 0; - while (brk--) { - if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) && - op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) { - in_switch = 1; - break; - } - } - - if (!in_switch) { - ZEND_ASSERT(opline->opcode == ZEND_FREE); - MAKE_NOP(opline); - zval_ptr_dtor_nogc(val); - return 1; - } - - m = opline; - n = op_array->opcodes + op_array->live_range[brk].end; - if (n->opcode == ZEND_FREE && - !(n->extended_value & ZEND_FREE_ON_RETURN)) { - n++; - } else { - n = op_array->opcodes + op_array->last; - } - - while (m < n) { - if (m->op1_type == type && - m->op1.var == var) { - if (m->opcode == ZEND_CASE - || m->opcode == ZEND_SWITCH_LONG - || m->opcode == ZEND_SWITCH_STRING) { + case ZEND_CASE: { + zend_op *end = op_array->opcodes + op_array->last; + while (opline < end) { + if (opline->op1_type == type && opline->op1.var == var) { + if (opline->opcode == ZEND_CASE + || opline->opcode == ZEND_SWITCH_LONG + || opline->opcode == ZEND_SWITCH_STRING) { zval v; - if (m->opcode == ZEND_CASE) { - m->opcode = ZEND_IS_EQUAL; + if (opline->opcode == ZEND_CASE) { + opline->opcode = ZEND_IS_EQUAL; } ZVAL_COPY(&v, val); if (Z_TYPE(v) == IS_STRING) { zend_string_hash_val(Z_STR(v)); } - m->op1.constant = zend_optimizer_add_literal(op_array, &v); - m->op1_type = IS_CONST; - } else if (m->opcode == ZEND_FREE) { - MAKE_NOP(m); + opline->op1.constant = zend_optimizer_add_literal(op_array, &v); + opline->op1_type = IS_CONST; + } else if (opline->opcode == ZEND_FREE) { + if (opline->extended_value == ZEND_FREE_SWITCH) { + /* We found the end of the switch. */ + MAKE_NOP(opline); + break; + } + + ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN); + MAKE_NOP(opline); } else { ZEND_ASSERT(0); } } - m++; + opline++; } zval_ptr_dtor_nogc(val); - zend_optimizer_remove_live_range(op_array, var); return 1; } case ZEND_VERIFY_RETURN_TYPE: { @@ -844,20 +676,12 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, default: break; } - if (zend_optimizer_update_op1_const(op_array, opline, val)) { - zend_optimizer_remove_live_range(op_array, var); - return 1; - } - return 0; + return zend_optimizer_update_op1_const(op_array, opline, val); } if (opline->op2_type == type && opline->op2.var == var) { - if (zend_optimizer_update_op2_const(op_array, opline, val)) { - zend_optimizer_remove_live_range(op_array, var); - return 1; - } - return 0; + return zend_optimizer_update_op2_const(op_array, opline, val); } opline++; } @@ -886,8 +710,6 @@ void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, z case ZEND_ASSERT_CHECK: ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); @@ -937,8 +759,6 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); @@ -1030,7 +850,14 @@ zend_function *zend_optimizer_get_called_func( script, op_array, opline, rt_constants); if (ce) { zend_string *func_name = Z_STR_P(GET_OP(op2) + 1); - return zend_hash_find_ptr(&ce->function_table, func_name); + zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name); + if (fbc) { + zend_bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0; + zend_bool same_scope = fbc->common.scope == op_array->scope; + if (is_public|| same_scope) { + return fbc; + } + } } } break; @@ -1090,6 +917,18 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) } } +zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) { + uint32_t var = free_opline->op1.var; + ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline)); + + while (--free_opline >= op_array->opcodes) { + if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) { + return free_opline; + } + } + return NULL; +} + static void zend_optimize(zend_op_array *op_array, zend_optimizer_ctx *ctx) { @@ -1098,7 +937,7 @@ static void zend_optimize(zend_op_array *op_array, } if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) { - zend_dump_op_array(op_array, 0, "before optimizer", NULL); + zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); } /* pass 1 @@ -1305,8 +1144,6 @@ static void zend_redo_pass_two(zend_op_array *op_array) opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_SWITCH_LONG: @@ -1394,8 +1231,6 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_SWITCH_LONG: @@ -1421,6 +1256,16 @@ static void zend_optimize_op_array(zend_op_array *op_array, /* Redo pass_two() */ zend_redo_pass_two(op_array); + + if (op_array->live_range) { +#if HAVE_DFA_PASS + if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) && + (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) { + return; + } +#endif + zend_recalc_live_ranges(op_array, NULL); + } } static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx) @@ -1461,11 +1306,22 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) } } } + +static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { + zend_func_info *func_info = ZEND_FUNC_INFO(op_array); + zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes]; + if (ssa_op->result_def >= 0) { + uint32_t type = func_info->ssa.var_info[ssa_op->result_def].type; + return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; + } + return 1; +} #endif int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) { zend_class_entry *ce; + zend_string *key; zend_op_array *op_array; zend_string *name; zend_optimizer_ctx ctx; @@ -1485,17 +1341,15 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend zend_optimize_op_array(op_array, &ctx); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_optimize_op_array(op_array, &ctx); - } else if (op_array->type == ZEND_USER_FUNCTION) { - zend_op_array *orig_op_array; - if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) { - HashTable *ht = op_array->static_variables; - *op_array = *orig_op_array; - op_array->static_variables = ht; - } } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); @@ -1573,11 +1427,18 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend } for (i = 0; i < call_graph.op_arrays_count; i++) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); + op_array = call_graph.op_arrays[i]; + func_info = ZEND_FUNC_INFO(op_array); if (func_info && func_info->ssa.var_info) { - zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa); + zend_redo_pass_two_ex(op_array, &func_info->ssa); + if (op_array->live_range) { + zend_recalc_live_ranges(op_array, needs_live_range); + } } else { - zend_redo_pass_two(call_graph.op_arrays[i]); + zend_redo_pass_two(op_array); + if (op_array->live_range) { + zend_recalc_live_ranges(op_array, NULL); + } } } @@ -1585,19 +1446,6 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); } - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope != ce) { - zend_op_array *orig_op_array; - if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) { - HashTable *ht = op_array->static_variables; - *op_array = *orig_op_array; - op_array->static_variables = ht; - } - } - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FOREACH_END(); - zend_arena_release(&ctx.arena, checkpoint); } else #endif @@ -1609,34 +1457,61 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend zend_adjust_fcall_stack_size(op_array, &ctx); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_adjust_fcall_stack_size(op_array, &ctx); - } else if (op_array->type == ZEND_USER_FUNCTION) { - zend_op_array *orig_op_array; - if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) { - HashTable *ht = op_array->static_variables; - *op_array = *orig_op_array; - op_array->static_variables = ht; - } } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); } + ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + continue; + } + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { + if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) { + zend_op_array *orig_op_array = + zend_hash_find_ptr(&op_array->scope->function_table, name); + + ZEND_ASSERT(orig_op_array != NULL); + if (orig_op_array != op_array) { + uint32_t fn_flags = op_array->fn_flags; + zend_function *prototype = op_array->prototype; + HashTable *ht = op_array->static_variables; + + *op_array = *orig_op_array; + op_array->fn_flags = fn_flags; + op_array->prototype = prototype; + op_array->static_variables = ht; + } + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { - zend_dump_op_array(&script->main_op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + zend_dump_op_array(&script->main_op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + zend_dump_op_array(op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce) { - zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_dump_op_array(op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); @@ -1659,11 +1534,3 @@ int zend_optimizer_shutdown(void) { return zend_func_info_shutdown(); } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h index bdd8c85add..2841d018a5 100644 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 6c12a0d828..df7be73d4f 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -71,6 +71,11 @@ typedef struct _zend_optimizer_ctx { target = src; \ } while (0) +static inline zend_bool zend_optimizer_is_loop_var_free(const zend_op *opline) { + return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN) + || (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH); +} + int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv); int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy); void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value); @@ -90,9 +95,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_uchar type, uint32_t var, zval *val); +zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline); -void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var); -void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start); void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx); void zend_optimizer_pass2(zend_op_array *op_array); void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx); @@ -111,7 +115,6 @@ zend_function *zend_optimizer_get_called_func( uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args); void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline); void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist); -zend_uchar zend_compound_assign_to_binary_op(zend_uchar opcode); int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_arrya, zend_ssa *ssa, zend_call_info **call_map); int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects); int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index aa6ce0d7fd..fdc383128b 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, SSA - Static Single Assignment Form | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -221,15 +221,6 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f } /* }}} */ -static inline zend_bool add_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a > ZEND_LONG_MAX - b) - || (b < 0 && a < ZEND_LONG_MIN - b); -} -static inline zend_bool sub_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a < ZEND_LONG_MIN + b) - || (b < 0 && a > ZEND_LONG_MAX + b); -} - /* e-SSA construction: Pi placement (Pi is actually a Phi with single * source and constraint). * Order of Phis is importent, Pis must be placed before Phis @@ -291,7 +282,7 @@ static void place_essa_pis( } if (var1 >= 0 && var2 >= 0) { - if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) { + if (!zend_sub_will_overflow(val1, val2) && !zend_sub_will_overflow(val2, val1)) { zend_long tmp = val1; val1 -= val2; val2 -= tmp; @@ -316,7 +307,7 @@ static void place_essa_pis( } else { var1 = -1; } - if (!add_will_overflow(val2, add_val2)) { + if (!zend_add_will_overflow(val2, add_val2)) { val2 += add_val2; } else { var1 = -1; @@ -337,7 +328,7 @@ static void place_essa_pis( } else { var2 = -1; } - if (!add_will_overflow(val1, add_val1)) { + if (!zend_add_will_overflow(val1, add_val1)) { val1 += add_val1; } else { var2 = -1; @@ -533,7 +524,7 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, int i, j; zend_op *opline, *end; int *tmp = NULL; - ALLOCA_FLAG(use_heap); + ALLOCA_FLAG(use_heap = 0); // FIXME: Can we optimize this copying out in some cases? if (blocks[n].next_child >= 0) { @@ -648,6 +639,22 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, //NEW_SSA_VAR(next->op1.var) } break; + case ZEND_ASSIGN_OBJ_REF: + if (opline->op1_type == IS_CV) { + ssa_ops[k].op1_def = ssa_vars_count; + var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; + ssa_vars_count++; + //NEW_SSA_VAR(opline->op1.var) + } + /* break missing intentionally */ + case ZEND_ASSIGN_STATIC_PROP_REF: + if (next->op1_type == IS_CV) { + ssa_ops[k + 1].op1_def = ssa_vars_count; + var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count; + ssa_vars_count++; + //NEW_SSA_VAR(next->op1.var) + } + break; case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: case ZEND_POST_INC_OBJ: @@ -671,6 +678,9 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, //NEW_SSA_VAR(opline+->op1.var) } break; + case ZEND_ADD_ARRAY_UNPACK: + ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)]; + break; case ZEND_SEND_VAR: case ZEND_CAST: case ZEND_QM_ASSIGN: @@ -706,18 +716,10 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, //NEW_SSA_VAR(opline->op1.var) } break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_OP: + case ZEND_ASSIGN_DIM_OP: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_STATIC_PROP_OP: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: @@ -1426,9 +1428,6 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ continue; } - if (op_array->opcodes[j].result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, op_array->opcodes[j].result.var, j); - } zend_ssa_remove_defs_of_instr(ssa, &ssa->ops[j]); zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]); } @@ -1608,11 +1607,3 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, zend_bool update_ old_var->phi_use_chain = NULL; } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h index 4707e3807a..0dbbf40efd 100644 --- a/ext/opcache/Optimizer/zend_ssa.h +++ b/ext/opcache/Optimizer/zend_ssa.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine, SSA - Static Single Assignment Form | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -221,7 +221,9 @@ static zend_always_inline zend_bool zend_ssa_is_no_val_use(const zend_op *opline /*if (opline->opcode == ZEND_FE_FETCH_R) { return ssa_op->op2_use == var && ssa_op->op1_use != var; }*/ - if (ssa_op->result_use == var && opline->opcode != ZEND_ADD_ARRAY_ELEMENT) { + if (ssa_op->result_use == var + && opline->opcode != ZEND_ADD_ARRAY_ELEMENT + && opline->opcode != ZEND_ADD_ARRAY_UNPACK) { return ssa_op->op1_use != var && ssa_op->op2_use != var; } return 0; @@ -317,11 +319,3 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend } while (0) #endif /* ZEND_SSA_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/Optimizer/zend_worklist.h b/ext/opcache/Optimizer/zend_worklist.h index 3ff1df14ee..2f3e3dd979 100644 --- a/ext/opcache/Optimizer/zend_worklist.h +++ b/ext/opcache/Optimizer/zend_worklist.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -119,11 +119,3 @@ static inline int zend_worklist_pop(zend_worklist *worklist) } #endif /* _ZEND_WORKLIST_H_ */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff --git a/ext/opcache/README b/ext/opcache/README deleted file mode 100644 index 99b8c0f2c9..0000000000 --- a/ext/opcache/README +++ /dev/null @@ -1,215 +0,0 @@ -The Zend OPcache -================ - -The Zend OPcache provides faster PHP execution through opcode caching and -optimization. It improves PHP performance by storing precompiled script -bytecode in the shared memory. This eliminates the stages of reading code from -the disk and compiling it on future access. In addition, it applies a few -bytecode optimization patterns that make code execution faster. - -Compatibility -------------- - -This version of Zend OPcache is compatible with PHP 5.2.*, 5.3.*, 5.4.* -and PHP-5.5 development branch. PHP 5.2 support may be removed in the future. - -Quick Install -------------- - -- Compile - - $PHP_DIR/bin/phpize - ./configure \ - --with-php-config=$PHP_DIR/bin/php-config - make - -- Install - - make install # this will copy opcache.so into PHP extension directory - -- Edit php.ini - - zend_extension=/...full path.../opcache.so - -NOTE: In case you are going to use Zend OPcache together with Xdebug or Zend Debugger, -be sure that the debugger is loaded after OPcache. "php -v" must show the debugger -after OPcache. - -- Restart PHP - -Speed Tuning -------------- - -We recommend the following configuration options for best performance -in a production environment. - -opcache.memory_consumption=128 -opcache.interned_strings_buffer=8 -opcache.max_accelerated_files=4000 -opcache.revalidate_freq=60 -opcache.enable_cli=1 - -You also may add the following, but it may break some applications and -frameworks. Please, read description of these directives and add them on your -own risk. - -opcache.save_comments=0 -opcache.enable_file_override=1 - -In some cases you may like to prefer enabling/disabling some features -to avoid incompatibilities at the cost of some performance degradation. - -For development environment we would recommend setting opcache.revalidate_freq -into 0. - -Configuration Directives ------------------------- - -opcache.enable (default "1") - OPcache On/Off switch. When set to Off, code is not optimized and cached. - -opcache.enable_cli (default "0") - Enables the OPcache for the CLI version of PHP. It's mostly for testing - and debugging. - -opcache.memory_consumption (default "64") - The OPcache shared memory storage size. The amount of memory for storing - precompiled PHP code in Mbytes. - -opcache.interned_strings_buffer (default "4") - The amount of memory for interned strings in Mbytes. - -opcache.max_accelerated_files (default "2000") - The maximum number of keys (scripts) in the OPcache hash table. - The number is actually the first one in the following set of prime - numbers that is bigger than the one supplied: { 223, 463, 983, 1979, 3907, - 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 }. Only numbers - between 200 and 1000000 are allowed. - -opcache.max_wasted_percentage (default "5") - When the cache fills up this setting decides when to actually reset (dump - all entries) it. At the default of 5% it means that if less than 5% of - the entries in the now full cache are wasted/orphaned, then you have a good - active cache. There is no point emptying a cache full of good entries - only to most likely refill it with those same entries. Once more than 5% - of the cache consists of wasted entries, the cache will reset in this - cache-full scenario. This can be set up to as high as 50%. - -opcache.use_cwd (default "1") - When this directive is enabled, the OPcache appends the current working - directory to the script key, thus eliminating possible collisions between - files with the same name (basename). Disabling the directive improves - performance, but may break existing applications. - -opcache.validate_timestamps (default "1") - When disabled, you must reset the OPcache manually or restart the - webserver for changes to the filesystem to take effect. - The frequency of the check is controlled by the directive - "opcache.revalidate_freq". - -opcache.validate_permission (default "0") - Leads OPcache to check file readability on each access to cached file. - This directive should be enabled in shared hosting environment, when few - users (PHP-FPM pools) reuse the common OPcache shared memory. - -opcache.validate_root (default "0") - This directive prevents file name collisions in different "chroot" - environments. It should be enabled for sites that may serve requests in - different "chroot" environments. - -opcache.revalidate_freq (default "2") - How often (in seconds) to check file timestamps for changes to the shared - memory storage allocation. ("1" means validate once per second, but only - once per request. "0" means always validate) - -opcache.file_update_protection (default "2") - Prevents caching files that are less than this number of seconds old. - It protects from caching of incompletely updated files. In case all file - updates on your site are atomic, you may increase performance setting it - to "0". - -opcache.revalidate_path (default "0") - Enables or disables file search in include_path optimization - If the file search is disabled and a cached file is found that uses - the same include_path, the file is not searched again. Thus, if a file - with the same name appears somewhere else in include_path, it - won't be found. Enable this directive if this optimization has an effect on - your applications. The default for this directive is disabled, which means - that optimization is active. - -opcache.save_comments (default "1") - If disabled, all PHPDoc comments are dropped from the code to reduce the - size of the optimized code. Disabling "Doc Comments" may break some - existing applications and frameworks (e.g. Doctrine, ZF2, PHPUnit) - -opcache.enable_file_override (default "0") - Allow file existence override (file_exists, etc.) performance feature. - -opcache.optimization_level (default "0xffffffff") - A bitmask, where each bit enables or disables the appropriate OPcache - passes - -opcache.dups_fix (default "0") - Enable this hack as a workaround for "Cannot redeclare class" errors. - -opcache.blacklist_filename - The location of the OPcache blacklist file (wildcards allowed). - Each OPcache blacklist file is a text file that holds the names of files - that should not be accelerated. The file format is to add each filename - to a new line. The filename may be a full path or just a file prefix - (i.e., /var/www/x blacklists all the files and directories in /var/www - that start with 'x'). Line starting with a ; are ignored (comments). - Files are usually triggered by one of the following three reasons: - 1) Directories that contain auto generated code, like Smarty or ZFW cache. - 2) Code that does not work well when accelerated, due to some delayed - compile time evaluation. - 3) Code that triggers an OPcache bug. - -opcache.max_file_size (default "0") - Allows exclusion of large files from being cached. By default all files - are cached. - -opcache.consistency_checks (default "0") - Check the cache checksum each N requests. - The default value of "0" means that the checks are disabled. - Because calculating the checksum impairs performance, this directive should - be enabled only as part of a debugging process. - -opcache.force_restart_timeout (default "180") - How long to wait (in seconds) for a scheduled restart to begin if the cache - is not being accessed. - The OPcache uses this directive to identify a situation where there may - be a problem with a process. After this time period has passed, the - OPcache assumes that something has happened and starts killing the - processes that still hold the locks that are preventing a restart. - If the log level is 3 or above, a "killed locker" error is recorded - in the Apache logs when this happens. - -opcache.error_log - OPcache error_log file name. Empty string assumes "stderr". - -opcache.log_verbosity_level (default "1") - All OPcache errors go to the Web server log. - By default, only fatal errors (level 0) or errors (level 1) are logged. - You can also enable warnings (level 2), info messages (level 3) or - debug messages (level 4). - -opcache.preferred_memory_model - Preferred Shared Memory back-end. Leave empty and let the system decide. - -opcache.protect_memory (default "0") - Protect the shared memory from unexpected writing during script execution. - Useful for internal debugging only. - -opcache.restrict_api (default "") - Allows calling OPcache API functions only from PHP scripts which path is - started from specified string. The default "" means no restriction. - -opcache.mmap_base - Mapping base of shared memory segments (for Windows only). All the PHP - processes have to map shared memory into the same address space. This - directive allows to manually fix the "Unable to reattach to base address" - errors. - -opcache.lockfile_path (default "/tmp") - Absolute path used to store shared lockfiles (for *nix only) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 80f94eaf28..b0b27d7975 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -31,6 +31,9 @@ #include "zend_accelerator_blacklist.h" #include "zend_list.h" #include "zend_execute.h" +#include "zend_inheritance.h" +#include "zend_exceptions.h" +#include "main/php_main.h" #include "main/SAPI.h" #include "main/php_streams.h" #include "main/php_open_temporary_file.h" @@ -39,12 +42,10 @@ #include "zend_virtual_cwd.h" #include "zend_accelerator_util_funcs.h" #include "zend_accelerator_hash.h" +#include "zend_file_cache.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/md5.h" - -#ifdef HAVE_OPCACHE_FILE_CACHE -# include "zend_file_cache.h" -#endif +#include "ext/hash/php_hash.h" #ifndef ZEND_WIN32 #include <netdb.h> @@ -54,6 +55,7 @@ typedef int uid_t; typedef int gid_t; #include <io.h> +#include <lmcons.h> #endif #ifndef ZEND_WIN32 @@ -71,7 +73,10 @@ typedef int gid_t; #ifndef ZEND_WIN32 # include <sys/types.h> +# include <sys/wait.h> # include <sys/ipc.h> +# include <pwd.h> +# include <grp.h> #endif #include <sys/stat.h> @@ -109,12 +114,14 @@ ZEND_TSRMLS_CACHE_DEFINE() zend_accel_shared_globals *accel_shared_globals = NULL; /* true globals, no need for thread safety */ +char accel_system_id[32]; +#ifdef ZEND_WIN32 +char accel_uname_id[32]; +#endif zend_bool accel_startup_ok = 0; static char *zps_failure_reason = NULL; char *zps_api_failure_reason = NULL; -#ifdef HAVE_OPCACHE_FILE_CACHE zend_bool file_cache_only = 0; /* process uses file cache only */ -#endif #if ENABLE_FILE_CACHE_FALLBACK zend_bool fallback_process = 0; /* process uses file cache fallback */ #endif @@ -128,6 +135,11 @@ static int (*orig_post_startup_cb)(void); static void accel_gen_system_id(void); static int accel_post_startup(void); +static int accel_finish_startup(void); + +static void preload_shutdown(void); +static void preload_activate(void); +static void preload_restart(void); #ifdef ZEND_WIN32 # define INCREMENT(v) InterlockedIncrement64(&ZCSG(v)) @@ -302,7 +314,9 @@ static inline int accel_restart_is_active(void) static inline int accel_activate_add(void) { #ifdef ZEND_WIN32 + SHM_UNPROTECT(); INCREMENT(mem_usage); + SHM_PROTECT(); #else struct flock mem_usage_lock; @@ -324,8 +338,10 @@ static inline void accel_deactivate_sub(void) { #ifdef ZEND_WIN32 if (ZCG(counted)) { + SHM_UNPROTECT(); DECREMENT(mem_usage); ZCG(counted) = 0; + SHM_PROTECT(); } #else struct flock mem_usage_unlock; @@ -460,11 +476,9 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str) uint32_t pos, *hash_slot; zend_string *s; -#ifdef HAVE_OPCACHE_FILE_CACHE if (UNEXPECTED(file_cache_only)) { return str; } -#endif if (IS_ACCEL_INTERNED(str)) { /* this is already an interned string */ @@ -733,18 +747,6 @@ static zend_string* ZEND_FASTCALL accel_replace_string_by_shm_permanent(zend_str return str; } -static zend_string* ZEND_FASTCALL accel_replace_string_by_process_permanent(zend_string *str) -{ - zend_string *ret = zend_interned_string_find_permanent(str); - if (ret) { - zend_string_release(str); - return ret; - } - ZEND_ASSERT(!IS_ACCEL_INTERNED(str)); - return str; -} - - static void accel_use_shm_interned_strings(void) { HANDLE_BLOCK_INTERRUPTIONS(); @@ -765,11 +767,6 @@ static void accel_use_shm_interned_strings(void) HANDLE_UNBLOCK_INTERRUPTIONS(); } -static void accel_use_permanent_interned_strings(void) -{ - accel_copy_permanent_strings(accel_replace_string_by_process_permanent); -} - #ifndef ZEND_WIN32 static inline void kill_all_lockers(struct flock *mem_usage_check) { @@ -973,11 +970,6 @@ accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_ #endif switch (file_handle->type) { - case ZEND_HANDLE_FD: - if (zend_fstat(file_handle->handle.fd, &statbuf) == -1) { - return 0; - } - break; case ZEND_HANDLE_FP: if (zend_fstat(fileno(file_handle->handle.fp), &statbuf) == -1) { if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) { @@ -986,7 +978,6 @@ accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_ } break; case ZEND_HANDLE_FILENAME: - case ZEND_HANDLE_MAPPED: if (file_handle->opened_path) { char *file_path = ZSTR_VAL(file_handle->opened_path); @@ -1087,8 +1078,7 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri file_handle->opened_path = NULL; } - ps_handle.type = ZEND_HANDLE_FILENAME; - ps_handle.filename = ZSTR_VAL(persistent_script->script.filename); + zend_stream_init_filename(&ps_handle, ZSTR_VAL(persistent_script->script.filename)); ps_handle.opened_path = persistent_script->script.filename; if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) { @@ -1100,7 +1090,9 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle) { - if (ZCG(accel_directives).revalidate_freq && + if (persistent_script->timestamp == 0) { + return SUCCESS; /* Don't check timestamps of preloaded scripts */ + } else if (ZCG(accel_directives).revalidate_freq && persistent_script->dynamic_members.revalidate >= ZCG(request_time)) { return SUCCESS; } else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) { @@ -1304,18 +1296,14 @@ int zend_accel_invalidate(const char *filename, size_t filename_len, zend_bool f return FAILURE; } -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { zend_file_cache_invalidate(realpath); } -#endif persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath); if (persistent_script && !persistent_script->corrupted) { zend_file_handle file_handle; - - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = ZSTR_VAL(realpath); + zend_stream_init_filename(&file_handle, ZSTR_VAL(realpath)); file_handle.opened_path = realpath; if (force || @@ -1375,7 +1363,6 @@ static zend_always_inline zend_bool is_phar_file(zend_string *filename) !strstr(ZSTR_VAL(filename), "://"); } -#ifdef HAVE_OPCACHE_FILE_CACHE static zend_persistent_script *store_script_in_file_cache(zend_persistent_script *new_persistent_script) { uint32_t memory_used; @@ -1398,6 +1385,8 @@ static zend_persistent_script *store_script_in_file_cache(zend_persistent_script ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used); #endif + zend_shared_alloc_clear_xlat_table(); + /* Copy into memory block */ new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 0); @@ -1443,7 +1432,6 @@ static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *from_shared_memory = 1; return store_script_in_file_cache(new_persistent_script); } -#endif static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, const char *key, unsigned int key_length, int *from_shared_memory) { @@ -1457,11 +1445,9 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr } orig_compiler_options = CG(compiler_options); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE; } -#endif if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) { CG(compiler_options) = orig_compiler_options; return new_persistent_script; @@ -1494,12 +1480,10 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr ZSMMG(memory_exhausted) = 1; zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH); zend_shared_alloc_unlock(); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { new_persistent_script = store_script_in_file_cache(new_persistent_script); *from_shared_memory = 1; } -#endif return new_persistent_script; } @@ -1554,15 +1538,15 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr zend_shared_alloc_destroy_xlat_table(); zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM); zend_shared_alloc_unlock(); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { new_persistent_script = store_script_in_file_cache(new_persistent_script); *from_shared_memory = 1; } -#endif return new_persistent_script; } + zend_shared_alloc_clear_xlat_table(); + /* Copy into shared memory */ new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length, 1); @@ -1607,13 +1591,11 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr zend_shared_alloc_unlock(); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { SHM_PROTECT(); zend_file_cache_script_store(new_persistent_script, 1); SHM_UNPROTECT(); } -#endif *from_shared_memory = 1; return new_persistent_script; @@ -1683,8 +1665,8 @@ static void zend_accel_init_auto_globals(void) static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, const char *key, zend_op_array **op_array_p) { zend_persistent_script *new_persistent_script; + uint32_t orig_functions_count, orig_class_count; zend_op_array *orig_active_op_array; - HashTable *orig_function_table, *orig_class_table; zval orig_user_error_handler; zend_op_array *op_array; int do_bailout = 0; @@ -1695,11 +1677,13 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl if (file_handle->type == ZEND_HANDLE_FILENAME) { if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) != SUCCESS) { *op_array_p = NULL; - if (type == ZEND_REQUIRE) { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); - zend_bailout(); - } else { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + if (!EG(exception)) { + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + } } return NULL; } @@ -1748,17 +1732,13 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl } } - new_persistent_script = create_persistent_script(); - /* Save the original values for the op_array, function table and class table */ orig_active_op_array = CG(active_op_array); - orig_function_table = CG(function_table); - orig_class_table = CG(class_table); + orig_functions_count = EG(function_table)->nNumUsed; + orig_class_count = EG(class_table)->nNumUsed; ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); /* Override them with ours */ - CG(function_table) = &ZCG(function_table); - EG(class_table) = CG(class_table) = &new_persistent_script->script.class_table; ZVAL_UNDEF(&EG(user_error_handler)); zend_try { @@ -1767,11 +1747,10 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; -#ifdef HAVE_OPCACHE_FILE_CACHE + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; if (ZCG(accel_directives).file_cache) { CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE; } -#endif op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type); CG(compiler_options) = orig_compiler_options; } zend_catch { @@ -1782,14 +1761,10 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl /* Restore originals */ CG(active_op_array) = orig_active_op_array; - CG(function_table) = orig_function_table; - EG(class_table) = CG(class_table) = orig_class_table; EG(user_error_handler) = orig_user_error_handler; if (!op_array) { /* compilation failed */ - free_persistent_script(new_persistent_script, 1); - zend_accel_free_user_functions(&ZCG(function_table)); if (do_bailout) { zend_bailout(); } @@ -1800,12 +1775,14 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl Here we aren't sure we would store it, but we will need it further anyway. */ - zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->script.function_table); + new_persistent_script = create_persistent_script(); + new_persistent_script->script.main_op_array = *op_array; + zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script); + zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script); new_persistent_script->script.first_early_binding_opline = (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) ? zend_build_delayed_early_binding_list(op_array) : (uint32_t)-1; - new_persistent_script->script.main_op_array = *op_array; efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */ @@ -1836,7 +1813,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl return new_persistent_script; } -#ifdef HAVE_OPCACHE_FILE_CACHE zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type) { zend_persistent_script *persistent_script; @@ -1851,11 +1827,13 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type) if (!file_handle->opened_path) { if (file_handle->type == ZEND_HANDLE_FILENAME && accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) { - if (type == ZEND_REQUIRE) { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); - zend_bailout(); - } else { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + if (!EG(exception)) { + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + } } return NULL; } @@ -1908,7 +1886,6 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type) return op_array; } -#endif int check_persistent_script_access(zend_persistent_script *persistent_script) { @@ -1945,27 +1922,21 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) /* The Accelerator is disabled, act as if without the Accelerator */ ZCG(cache_opline) = NULL; ZCG(cache_persistent_script) = NULL; -#ifdef HAVE_OPCACHE_FILE_CACHE if (file_handle->filename && ZCG(accel_directives).file_cache && ZCG(enabled) && accel_startup_ok) { return file_cache_compile_file(file_handle, type); } -#endif return accelerator_orig_compile_file(file_handle, type); -#ifdef HAVE_OPCACHE_FILE_CACHE } else if (file_cache_only) { ZCG(cache_opline) = NULL; ZCG(cache_persistent_script) = NULL; return file_cache_compile_file(file_handle, type); -#endif } else if (!ZCG(accelerator_enabled) || (ZCSG(restart_in_progress) && accel_restart_is_active())) { -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { return file_cache_compile_file(file_handle, type); } -#endif ZCG(cache_opline) = NULL; ZCG(cache_persistent_script) = NULL; return accelerator_orig_compile_file(file_handle, type); @@ -2012,11 +1983,13 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) /* open file to resolve the path */ if (file_handle->type == ZEND_HANDLE_FILENAME && accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) { - if (type == ZEND_REQUIRE) { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); - zend_bailout(); - } else { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + if (!EG(exception)) { + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + } } return NULL; } @@ -2055,26 +2028,26 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) */ if (!ZCG(counted)) { if (accel_activate_add() == FAILURE) { -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { return file_cache_compile_file(file_handle, type); } -#endif return accelerator_orig_compile_file(file_handle, type); } ZCG(counted) = 1; } - /* Revalidate acessibility of cached file */ + /* Revalidate accessibility of cached file */ if (EXPECTED(persistent_script != NULL) && UNEXPECTED(ZCG(accel_directives).validate_permission) && file_handle->type == ZEND_HANDLE_FILENAME && UNEXPECTED(check_persistent_script_access(persistent_script))) { - if (type == ZEND_REQUIRE) { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); - zend_bailout(); - } else { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + if (!EG(exception)) { + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); + } } return NULL; } @@ -2126,12 +2099,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) } } -#ifdef HAVE_OPCACHE_FILE_CACHE /* Check the second level cache */ if (!persistent_script && ZCG(accel_directives).file_cache) { persistent_script = zend_file_cache_script_load(file_handle); } -#endif /* If script was not found or invalidated by validate_timestamps */ if (!persistent_script) { @@ -2145,11 +2116,9 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) { SHM_PROTECT(); HANDLE_UNBLOCK_INTERRUPTIONS(); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { return file_cache_compile_file(file_handle, type); } -#endif return accelerator_orig_compile_file(file_handle, type); } @@ -2236,6 +2205,26 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) return zend_accel_load_script(persistent_script, from_shared_memory); } +#ifdef ZEND_WIN32 +static int accel_gen_uname_id(void) +{ + PHP_MD5_CTX ctx; + unsigned char digest[16]; + wchar_t uname[UNLEN + 1]; + DWORD unsize = UNLEN; + + if (!GetUserNameW(uname, &unsize)) { + return FAILURE; + } + PHP_MD5Init(&ctx); + PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t)); + PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id)); + PHP_MD5Final(digest, &ctx); + php_hash_bin2hex(accel_uname_id, digest, sizeof digest); + return SUCCESS; +} +#endif + /* zend_stream_open_function() replacement for PHP 5.3 and above */ static int persistent_stream_open_function(const char *filename, zend_file_handle *handle) { @@ -2250,10 +2239,8 @@ static int persistent_stream_open_function(const char *filename, zend_file_handl ZCG(cache_opline) == EG(current_execute_data)->opline)) { /* we are in include_once or FastCGI request */ - handle->filename = (char*)filename; - handle->free_filename = 0; + zend_stream_init_filename(handle, (char*) filename); handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename); - handle->type = ZEND_HANDLE_FILENAME; return SUCCESS; } ZCG(cache_opline) = NULL; @@ -2265,10 +2252,7 @@ static int persistent_stream_open_function(const char *filename, zend_file_handl /* zend_resolve_path() replacement for PHP 5.3 and above */ static zend_string* persistent_zend_resolve_path(const char *filename, size_t filename_len) { - if ( -#ifdef HAVE_OPCACHE_FILE_CACHE - !file_cache_only && -#endif + if (!file_cache_only && ZCG(accelerator_enabled)) { /* check if callback is called from include_once or it's a main request */ @@ -2353,12 +2337,17 @@ static void zend_reset_cache_vars(void) ZSMMG(wasted_shared_memory) = 0; ZCSG(restart_pending) = 0; ZCSG(force_restart_time) = 0; + ZCSG(map_ptr_last) = CG(map_ptr_last); } static void accel_reset_pcre_cache(void) { Bucket *p; + if (PCRE_G(per_request_cache)) { + return; + } + ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) { /* Remove PCRE cache entries with inconsistent keys */ if (zend_accel_in_shm(p->key)) { @@ -2368,16 +2357,11 @@ static void accel_reset_pcre_cache(void) } ZEND_HASH_FOREACH_END(); } -static void accel_activate(void) +int accel_activate(INIT_FUNC_ARGS) { if (!ZCG(enabled) || !accel_startup_ok) { ZCG(accelerator_enabled) = 0; - return; - } - - if (!ZCG(function_table).nTableSize) { - zend_hash_init(&ZCG(function_table), zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1); - zend_accel_copy_internal_functions(); + return SUCCESS; } /* PHP-5.4 and above return "double", but we use 1 sec precision */ @@ -2388,21 +2372,14 @@ static void accel_activate(void) ZCG(include_path_key_len) = 0; ZCG(include_path_check) = 1; - /* check if ZCG(function_table) wasn't somehow polluted on the way */ - if (ZCG(internal_functions_count) != (zend_long)zend_hash_num_elements(&ZCG(function_table))) { - zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table))); - } - ZCG(cwd) = NULL; ZCG(cwd_key_len) = 0; ZCG(cwd_check) = 1; -#ifdef HAVE_OPCACHE_FILE_CACHE if (file_cache_only) { ZCG(accelerator_enabled) = 0; - return; + return SUCCESS; } -#endif #ifndef ZEND_WIN32 if (ZCG(accel_directives).validate_root) { @@ -2418,7 +2395,7 @@ static void accel_activate(void) zend_alter_ini_entry_chars(key, "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_RUNTIME); zend_string_release_ex(key, 0); zend_accel_error(ACCEL_LOG_WARNING, "Can't cache files in chroot() directory with too big inode"); - return; + return SUCCESS; } } } @@ -2459,6 +2436,7 @@ static void accel_activate(void) } accel_restart_enter(); + zend_map_ptr_reset(); zend_reset_cache_vars(); zend_accel_hash_clean(&ZCSG(hash)); @@ -2467,6 +2445,9 @@ static void accel_activate(void) } zend_shared_alloc_restore_state(); + if (ZCSG(preload_script)) { + preload_restart(); + } ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart); if (ZCSG(last_restart_time) < ZCG(request_time)) { ZCSG(last_restart_time) = ZCG(request_time); @@ -2497,10 +2478,21 @@ static void accel_activate(void) accel_reset_pcre_cache(); ZCG(pcre_reseted) = 1; } + + if (ZCSG(preload_script)) { + preload_activate(); + } + + return SUCCESS; } int accel_post_deactivate(void) { + if (ZCG(cwd)) { + zend_string_release_ex(ZCG(cwd), 0); + ZCG(cwd) = NULL; + } + if (!ZCG(enabled) || !accel_startup_ok) { return SUCCESS; } @@ -2512,19 +2504,6 @@ int accel_post_deactivate(void) return SUCCESS; } -static void accel_deactivate(void) -{ - /* ensure that we restore function_table and class_table - * In general, they're restored by persistent_compile_file(), but in case - * the script is aborted abnormally, they may become messed up. - */ - - if (ZCG(cwd)) { - zend_string_release_ex(ZCG(cwd), 0); - ZCG(cwd) = NULL; - } -} - static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2) { (void)element2; /* keep the compiler happy */ @@ -2638,8 +2617,6 @@ static int zend_accel_init_shm(void) STRTAB_INVALID_POS, (char*)ZCSG(interned_strings).start - ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table))); - - zend_interned_strings_set_permanent_storage_copy_handlers(accel_use_shm_interned_strings, accel_use_permanent_interned_strings); } zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php); @@ -2655,7 +2632,6 @@ static int zend_accel_init_shm(void) ZCSG(last_restart_time) = 0; ZCSG(restart_in_progress) = 0; - for (i = 0; i < -HT_MIN_MASK; i++) { ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX; } @@ -2671,27 +2647,14 @@ static void accel_globals_ctor(zend_accel_globals *accel_globals) ZEND_TSRMLS_CACHE_UPDATE(); #endif memset(accel_globals, 0, sizeof(zend_accel_globals)); - - /* TODO refactor to init this just once. */ - accel_gen_system_id(); } -static void accel_globals_dtor(zend_accel_globals *accel_globals) -{ - if (accel_globals->function_table.nTableSize) { - accel_globals->function_table.pDestructor = NULL; - zend_hash_destroy(&accel_globals->function_table); - } -} - -#define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_CHAR) ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT) +#define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT) static void accel_gen_system_id(void) { PHP_MD5_CTX context; - unsigned char digest[16], c; - char *md5str = ZCG(system_id); - int i; + unsigned char digest[16]; PHP_MD5Init(&context); PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1); @@ -2703,14 +2666,7 @@ static void accel_gen_system_id(void) PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1); } PHP_MD5Final(digest, &context); - for (i = 0; i < 16; i++) { - c = digest[i] >> 4; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5str[i * 2] = c; - c = digest[i] & 0x0f; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5str[(i * 2) + 1] = c; - } + php_hash_bin2hex(accel_system_id, digest, sizeof digest); } #ifdef HAVE_HUGE_CODE_PAGES @@ -2724,10 +2680,16 @@ static void accel_gen_system_id(void) # ifndef MAP_FAILED # define MAP_FAILED ((void*)-1) # endif +# ifdef MAP_ALIGNED_SUPER +# include <sys/types.h> +# include <sys/sysctl.h> +# include <sys/user.h> +# define MAP_HUGETLB MAP_ALIGNED_SUPER +# endif # endif # if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) -static int accel_remap_huge_pages(void *start, size_t size, const char *name, size_t offset) +static int accel_remap_huge_pages(void *start, size_t size, size_t real_size, const char *name, size_t offset) { void *ret = MAP_FAILED; void *mem; @@ -2742,7 +2704,7 @@ static int accel_remap_huge_pages(void *start, size_t size, const char *name, si strerror(errno), errno); return -1; } - memcpy(mem, start, size); + memcpy(mem, start, real_size); # ifdef MAP_HUGETLB ret = mmap(start, size, @@ -2759,7 +2721,7 @@ static int accel_remap_huge_pages(void *start, size_t size, const char *name, si ZEND_ASSERT(ret != MAP_FAILED); # ifdef MADV_HUGEPAGE if (-1 == madvise(start, size, MADV_HUGEPAGE)) { - memcpy(start, mem, size); + memcpy(start, mem, real_size); mprotect(start, size, PROT_READ | PROT_EXEC); munmap(mem, size); zend_error(E_WARNING, @@ -2768,7 +2730,7 @@ static int accel_remap_huge_pages(void *start, size_t size, const char *name, si return -1; } # else - memcpy(start, mem, size); + memcpy(start, mem, real_size); mprotect(start, size, PROT_READ | PROT_EXEC); munmap(mem, size); zend_error(E_WARNING, @@ -2779,7 +2741,7 @@ static int accel_remap_huge_pages(void *start, size_t size, const char *name, si } if (ret == start) { - memcpy(start, mem, size); + memcpy(start, mem, real_size); mprotect(start, size, PROT_READ | PROT_EXEC); } munmap(mem, size); @@ -2789,6 +2751,7 @@ static int accel_remap_huge_pages(void *start, size_t size, const char *name, si static void accel_move_code_to_huge_pages(void) { +#if defined(__linux__) FILE *f; long unsigned int huge_page_size = 2 * 1024 * 1024; @@ -2798,18 +2761,70 @@ static void accel_move_code_to_huge_pages(void) char perm[5], dev[6], name[MAXPATHLEN]; int ret; - ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name); - if (ret == 7 && perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') { - long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size); - long unsigned int seg_end = (end & ~(huge_page_size-1L)); + while (1) { + ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name); + if (ret == 7) { + if (perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') { + long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size); + long unsigned int seg_end = (end & ~(huge_page_size-1L)); + long unsigned int real_end; + + ret = fscanf(f, "%lx-", &start); + if (ret == 1 && start == seg_end + huge_page_size) { + real_end = end; + seg_end = start; + } else { + real_end = seg_end; + } - if (seg_end > seg_start) { - zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name); - accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, name, offset + seg_start - start); + if (seg_end > seg_start) { + zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name); + accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, real_end - seg_start, name, offset + seg_start - start); + } + break; + } + } else { + break; } } fclose(f); } +#elif defined(__FreeBSD__) + size_t s = 0; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + long unsigned int huge_page_size = 2 * 1024 * 1024; + if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) { + s = s * 4 / 3; + void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (addr != MAP_FAILED) { + if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) { + uintptr_t start = (uintptr_t)addr; + uintptr_t end = start + s; + while (start < end) { + struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start; + size_t sz = entry->kve_structsize; + if (sz == 0) { + break; + } + int permflags = entry->kve_protection; + if ((permflags & KVME_PROT_READ) && !(permflags & KVME_PROT_WRITE) && + (permflags & KVME_PROT_EXEC) && entry->kve_path[0] != '\0') { + long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size); + long unsigned int seg_end = (end & ~(huge_page_size-1L)); + if (seg_end > seg_start) { + zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, entry->kve_path); + accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, seg_end - seg_start, entry->kve_path, entry->kve_offset + seg_start - start); + // First relevant segment found is our binary + break; + } + } + start += sz; + } + } + munmap(addr, s); + } + } +#endif } # else static void accel_move_code_to_huge_pages(void) @@ -2823,7 +2838,7 @@ static void accel_move_code_to_huge_pages(void) static int accel_startup(zend_extension *extension) { #ifdef ZTS - accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor); + accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, NULL); #else accel_globals_ctor(&accel_globals); #endif @@ -2834,13 +2849,20 @@ static int accel_startup(zend_extension *extension) # endif #endif + accel_gen_system_id(); + if (start_accel_module() == FAILURE) { accel_startup_ok = 0; zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!"); return FAILURE; } - accel_gen_system_id(); +#ifdef ZEND_WIN32 + if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) { + zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb); + return SUCCESS; + } +#endif #ifdef HAVE_HUGE_CODE_PAGES if (ZCG(accel_directives).huge_code_pages && @@ -2871,6 +2893,9 @@ static int accel_startup(zend_extension *extension) orig_post_startup_cb = zend_post_startup_cb; zend_post_startup_cb = accel_post_startup; + /* Prevent unloadig */ + extension->handle = 0; + return SUCCESS; } @@ -2891,12 +2916,8 @@ static int accel_post_startup(void) /********************************************/ /* End of non-SHM dependent initializations */ /********************************************/ -#ifdef HAVE_OPCACHE_FILE_CACHE file_cache_only = ZCG(accel_directives).file_cache_only; if (!file_cache_only) { -#else - if (1) { -#endif switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) { case ALLOC_SUCCESS: if (zend_accel_init_shm() == FAILURE) { @@ -2911,9 +2932,6 @@ static int accel_post_startup(void) case SUCCESSFULLY_REATTACHED: zend_shared_alloc_lock(); accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals); - if (ZCG(accel_directives).interned_strings_buffer) { - zend_interned_strings_set_permanent_storage_copy_handlers(accel_use_shm_interned_strings, accel_use_permanent_interned_strings); - } zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php); zend_shared_alloc_unlock(); break; @@ -2947,7 +2965,6 @@ static int accel_post_startup(void) zend_shared_alloc_unlock(); SHM_PROTECT(); -#ifdef HAVE_OPCACHE_FILE_CACHE } else if (!ZCG(accel_directives).file_cache) { accel_startup_ok = 0; zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache"); @@ -2957,7 +2974,6 @@ static int accel_post_startup(void) /* Init auto-global strings */ zend_accel_init_auto_globals(); -#endif } #if ENABLE_FILE_CACHE_FALLBACK file_cache_fallback: @@ -3009,16 +3025,18 @@ file_cache_fallback: zend_optimizer_startup(); - return SUCCESS; + if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) { + accel_use_shm_interned_strings(); + } + + return accel_finish_startup(); } -static void accel_free_ts_resources() +static void (*orig_post_shutdown_cb)(void); + +static void accel_post_shutdown(void) { -#ifndef ZTS - accel_globals_dtor(&accel_globals); -#else - ts_free_id(accel_globals_id); -#endif + zend_shared_alloc_shutdown(); } void accel_shutdown(void) @@ -3031,21 +3049,30 @@ void accel_shutdown(void) zend_accel_blacklist_shutdown(&accel_blacklist); if (!ZCG(enabled) || !accel_startup_ok) { - accel_free_ts_resources(); +#ifdef ZTS + ts_free_id(accel_globals_id); +#endif return; } -#ifdef HAVE_OPCACHE_FILE_CACHE + if (ZCSG(preload_script)) { + preload_shutdown(); + } + _file_cache_only = file_cache_only; -#endif accel_reset_pcre_cache(); - accel_free_ts_resources(); +#ifdef ZTS + ts_free_id(accel_globals_id); +#endif if (!_file_cache_only) { - zend_shared_alloc_shutdown(); + /* Delay SHM dettach */ + orig_post_shutdown_cb = zend_post_shutdown_cb; + zend_post_shutdown_cb = accel_post_shutdown; } + zend_compile_file = accelerator_orig_compile_file; if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) { @@ -3127,16 +3154,1714 @@ void accelerator_shm_read_unlock(void) } } +/* Preloading */ +static HashTable *preload_scripts = NULL; +static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type); + +static void preload_shutdown(void) +{ + zval *zv; + +#if 0 + if (EG(zend_constants)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(zend_constants), zv) { + zend_constant *c = Z_PTR_P(zv); + if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } +#endif + + if (EG(function_table)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_function *func = Z_PTR_P(zv); + if (func->type == ZEND_INTERNAL_FUNCTION) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } + + if (EG(class_table)) { + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } +} + +static void preload_activate(void) +{ + if (ZCSG(preload_script)->ping_auto_globals_mask) { + zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask); + } +} + +static void preload_restart(void) +{ + zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(ZCSG(preload_script)->script.filename), ZSTR_LEN(ZCSG(preload_script)->script.filename), 0, ZCSG(preload_script)); + if (ZCSG(saved_scripts)) { + zend_persistent_script **p = ZCSG(saved_scripts); + while (*p) { + zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL((*p)->script.filename), ZSTR_LEN((*p)->script.filename), 0, *p); + p++; + } + } +} + +static size_t preload_try_strip_filename(zend_string *filename) { + /*FIXME: better way to hanlde eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */ + if (ZSTR_LEN(filename) > sizeof(" eval()'d code") + && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') { + const char *cfilename = ZSTR_VAL(filename); + size_t cfilenamelen = ZSTR_LEN(filename) - sizeof(" eval()'d code") - 1 /*:*/; + while (cfilenamelen && cfilename[--cfilenamelen] != '('); + return cfilenamelen; + } + return 0; +} + +static void preload_move_user_functions(HashTable *src, HashTable *dst) +{ + Bucket *p; + dtor_func_t orig_dtor = src->pDestructor; + zend_string *filename = NULL; + int copy = 0; + + src->pDestructor = NULL; + zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); + ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { + zend_function *function = Z_PTR(p->val); + + if (EXPECTED(function->type == ZEND_USER_FUNCTION)) { + if (function->op_array.filename != filename) { + filename = function->op_array.filename; + if (filename) { + if (!(copy = zend_hash_exists(preload_scripts, filename))) { + size_t eval_len = preload_try_strip_filename(filename); + if (eval_len) { + copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len); + } + } + } else { + copy = 0; + } + } + if (copy) { + _zend_hash_append_ptr(dst, p->key, function); + } else { + orig_dtor(&p->val); + } + zend_hash_del_bucket(src, p); + } else { + break; + } + } ZEND_HASH_FOREACH_END(); + src->pDestructor = orig_dtor; +} + +static void preload_move_user_classes(HashTable *src, HashTable *dst) +{ + Bucket *p; + dtor_func_t orig_dtor = src->pDestructor; + zend_string *filename = NULL; + int copy = 0; + + src->pDestructor = NULL; + zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); + ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { + zend_class_entry *ce = Z_PTR(p->val); + + if (EXPECTED(ce->type == ZEND_USER_CLASS)) { + if (ce->info.user.filename != filename) { + filename = ce->info.user.filename; + if (filename) { + if (!(copy = zend_hash_exists(preload_scripts, filename))) { + size_t eval_len = preload_try_strip_filename(filename); + if (eval_len) { + copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len); + } + } + } else { + copy = 0; + } + } + if (copy) { + _zend_hash_append(dst, p->key, &p->val); + } else { + orig_dtor(&p->val); + } + zend_hash_del_bucket(src, p); + } else { + break; + } + } ZEND_HASH_FOREACH_END(); + src->pDestructor = orig_dtor; +} + +static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type) +{ + zend_op_array *op_array = preload_orig_compile_file(file_handle, type); + + if (op_array && op_array->refcount) { + zend_persistent_script *script; + + script = create_persistent_script(); + script->script.first_early_binding_opline = (uint32_t)-1; + script->script.filename = zend_string_copy(op_array->filename); + zend_string_hash_val(script->script.filename); + script->script.main_op_array = *op_array; + +//??? efree(op_array->refcount); + op_array->refcount = NULL; + + if (op_array->static_variables && + !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(op_array->static_variables); + } + + zend_hash_add_ptr(preload_scripts, script->script.filename, script); + } + + return op_array; +} + +static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp) +{ + Bucket *b1 = base; + Bucket *b2; + Bucket *end = b1 + count; + Bucket tmp; + zend_class_entry *ce, *p; + + 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 (p->type == ZEND_USER_CLASS) { + b2 = b1 + 1; + while (b2 < end) { + if (p == Z_PTR(b2->val)) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } + b2++; + } + } + } + if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) { + uint32_t i = 0; + for (i = 0; i < ce->num_interfaces; i++) { + p = ce->interfaces[i]; + if (p->type == ZEND_USER_CLASS) { + b2 = b1 + 1; + while (b2 < end) { + if (p == Z_PTR(b2->val)) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } + b2++; + } + } + } + } + b1++; + } +} + +static void get_unresolved_initializer(zend_class_entry *ce, const char **kind, const char **name) { + zend_string *key; + zend_class_constant *c; + zend_property_info *prop; + + *kind = "unknown"; + *name = ""; + + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + *kind = "constant "; + *name = ZSTR_VAL(key); + } + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop) { + zval *val; + if (prop->flags & ZEND_ACC_STATIC) { + val = &ce->default_static_members_table[prop->offset]; + } else { + val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)]; + } + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + *kind = (prop->flags & ZEND_ACC_STATIC) ? "static property $" : "property $"; + *name = ZSTR_VAL(key); + } + } ZEND_HASH_FOREACH_END(); +} + +static zend_bool preload_needed_types_known(zend_class_entry *ce); +static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) { + zend_class_entry *p; + *kind = "Unknown reason"; + *name = ""; + + if (ce->parent_name) { + zend_string *key = zend_string_tolower(ce->parent_name); + p = zend_hash_find_ptr(EG(class_table), key); + zend_string_release(key); + if (!p) { + *kind = "Unknown parent "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + *kind = "Parent with unresolved initializers "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + *kind = "Parent with unresolved property types "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + } + + if (ce->num_interfaces) { + uint32_t i; + for (i = 0; i < ce->num_interfaces; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); + if (!p) { + *kind = "Unknown interface "; + *name = ZSTR_VAL(ce->interface_names[i].name); + return; + } + } + } + + if (ce->num_traits) { + uint32_t i; + for (i = 0; i < ce->num_traits; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); + if (!p) { + *kind = "Unknown trait "; + *name = ZSTR_VAL(ce->trait_names[i].name); + return; + } + } + } + + if (!preload_needed_types_known(ce)) { + *kind = "Unknown type dependencies"; + return; + } +} + +static zend_bool preload_try_resolve_constants(zend_class_entry *ce) +{ + zend_bool ok, changed; + zend_class_constant *c; + zval *val; + + EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */ + CG(in_compilation) = 1; /* prevent autoloading */ + do { + ok = 1; + changed = 0; + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + val = &c->value; + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) { + changed = 1; + } else { + ok = 0; + } + } + } ZEND_HASH_FOREACH_END(); + if (ce->default_properties_count) { + uint32_t i; + for (i = 0; i < ce->default_properties_count; i++) { + val = &ce->default_properties_table[i]; + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + zend_property_info *prop = ce->properties_info_table[i]; + if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) { + ok = 0; + } + } + } + } + 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; + + val = ce->default_static_members_table + ce->default_static_members_count - 1; + while (count) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) { + ok = 0; + } + } + val--; + count--; + } + } + } while (changed && !ok); + EG(exception) = NULL; + CG(in_compilation) = 0; + + return ok; +} + +static zend_bool preload_try_resolve_property_types(zend_class_entry *ce) +{ + zend_bool ok = 1; + zend_property_info *prop; + zend_class_entry *p; + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + zend_string *name, *lcname; + + if (!ZEND_TYPE_IS_NAME(prop->type)) { + continue; + } + + name = ZEND_TYPE_NAME(prop->type); + lcname = zend_string_tolower(name); + p = zend_hash_find_ptr(EG(class_table), lcname); + zend_string_release(lcname); + if (!p) { + ok = 0; + continue; + } + if (p != ce) { + if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + ok = 0; + continue; + } + if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + ok = 0; + continue; + } + } + + zend_string_release(name); + prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type)); + } ZEND_HASH_FOREACH_END(); + } + + return ok; +} + +static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type type) { + zend_string *name, *lcname; + zend_bool known; + if (!ZEND_TYPE_IS_NAME(type)) { + return 1; + } + + name = ZEND_TYPE_NAME(type); + if (zend_string_equals_literal_ci(name, "self") || + zend_string_equals_literal_ci(name, "parent") || + zend_string_equals_ci(name, ce->name)) { + return 1; + } + + lcname = zend_string_tolower(name); + known = zend_hash_exists(EG(class_table), lcname); + zend_string_release(lcname); + return known; +} + +static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) { + zend_class_entry *p; + if (ce->trait_aliases || ce->trait_precedences) { + return 1; + } + + if (ce->parent_name) { + zend_string *key = zend_string_tolower(ce->parent_name); + p = zend_hash_find_ptr(EG(class_table), key); + zend_string_release(key); + if (zend_hash_exists(&p->function_table, lcname)) { + return 1; + } + } + + if (ce->num_interfaces) { + uint32_t i; + for (i = 0; i < ce->num_interfaces; i++) { + zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); + if (zend_hash_exists(&p->function_table, lcname)) { + return 1; + } + } + } + + if (ce->num_traits) { + uint32_t i; + for (i = 0; i < ce->num_traits; i++) { + zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); + if (zend_hash_exists(&p->function_table, lcname)) { + return 1; + } + } + } + + return 0; +} + +static zend_bool preload_needed_types_known(zend_class_entry *ce) { + zend_function *fptr; + zend_string *lcname; + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) { + uint32_t i; + if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + if (!preload_is_type_known(ce, fptr->common.arg_info[-1].type) && + preload_is_method_maybe_override(ce, lcname)) { + return 0; + } + } + for (i = 0; i < fptr->common.num_args; i++) { + if (!preload_is_type_known(ce, fptr->common.arg_info[i].type) && + preload_is_method_maybe_override(ce, lcname)) { + return 0; + } + } + } ZEND_HASH_FOREACH_END(); + return 1; +} + +static void preload_link(void) +{ + zval *zv; + zend_persistent_script *script; + zend_class_entry *ce, *parent, *p; + zend_string *key; + zend_bool found, changed; + uint32_t i; + dtor_func_t orig_dtor; + zend_function *function; + + /* Resolve class dependencies */ + do { + changed = 0; + + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS)) + && !(ce->ce_flags & ZEND_ACC_LINKED)) { + + if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) { + key = zend_string_tolower(ce->name); + if (zend_hash_exists(EG(class_table), key)) { + zend_string_release(key); + continue; + } + zend_string_release(key); + } + + parent = NULL; + + if (ce->parent_name) { + key = zend_string_tolower(ce->parent_name); + parent = zend_hash_find_ptr(EG(class_table), key); + zend_string_release(key); + if (!parent) continue; + if (!(parent->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + continue; + } + if (!(parent->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + continue; + } + } + + if (ce->num_interfaces) { + found = 1; + for (i = 0; i < ce->num_interfaces; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); + if (!p) { + found = 0; + break; + } + if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + found = 0; + break; + } + } + if (!found) continue; + } + + if (ce->num_traits) { + found = 1; + for (i = 0; i < ce->num_traits; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); + if (!p) { + found = 0; + break; + } + } + if (!found) continue; + } + + /* TODO: This is much more restrictive than necessary. We only need to actually + * know the types for covariant checks, but don't need them if we can ensure + * compatibility through a simple string comparison. We could improve this using + * a more general version of zend_can_early_bind(). */ + if (!preload_needed_types_known(ce)) { + continue; + } + + { + zend_string *key = zend_string_tolower(ce->name); + zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key); + zend_string_release(key); + } + + if (EXPECTED(zv)) { + /* Set filename & lineno information for inheritance errors */ + CG(in_compilation) = 1; + CG(compiled_filename) = ce->info.user.filename; + if (ce->parent_name + && !ce->num_interfaces + && !ce->num_traits + && (parent->type == ZEND_INTERNAL_CLASS + || parent->info.user.filename == ce->info.user.filename)) { + /* simulate early binding */ + CG(zend_lineno) = ce->info.user.line_end; + } else { + CG(zend_lineno) = ce->info.user.line_start; + } + if (zend_do_link_class(ce, NULL) == FAILURE) { + ZEND_ASSERT(0 && "Class linking failed?"); + } + CG(in_compilation) = 0; + CG(compiled_filename) = NULL; + + changed = 1; + } + } + if (ce->ce_flags & ZEND_ACC_LINKED) { + if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */ + || preload_try_resolve_constants(ce)) { + ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + changed = 1; + } + } + + if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */ + || preload_try_resolve_property_types(ce)) { + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + changed = 1; + } + } + } + } ZEND_HASH_FOREACH_END(); + } while (changed); + + /* Move unlinked clases (and with unresilved constants) back to scripts */ + orig_dtor = EG(class_table)->pDestructor; + EG(class_table)->pDestructor = NULL; + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + zend_string *key = zend_string_tolower(ce->name); + if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS) + && zend_hash_exists(EG(class_table), key)) { + zend_error_at( + E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, + "Can't preload already declared class %s", ZSTR_VAL(ce->name)); + } else { + const char *kind, *name; + get_unlinked_dependency(ce, &kind, &name); + zend_error_at( + E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, + "Can't preload unlinked class %s: %s%s", + ZSTR_VAL(ce->name), kind, name); + } + zend_string_release(key); + } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + const char *kind, *name; + get_unresolved_initializer(ce, &kind, &name); + zend_error_at( + E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, + "Can't preload class %s with unresolved initializer for %s%s", + ZSTR_VAL(ce->name), kind, name); + } else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + zend_error_at( + E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, + "Can't preload class %s with unresolved property types", + ZSTR_VAL(ce->name)); + } else { + continue; + } + ce->ce_flags &= ~ZEND_ACC_PRELOADED; + ZEND_HASH_FOREACH_PTR(&ce->function_table, function) { + if (EXPECTED(function->type == ZEND_USER_FUNCTION) + && function->common.scope == ce) { + function->common.fn_flags &= ~ZEND_ACC_PRELOADED; + } + } ZEND_HASH_FOREACH_END(); + script = zend_hash_find_ptr(preload_scripts, ce->info.user.filename); + ZEND_ASSERT(script); + zend_hash_add(&script->script.class_table, key, zv); + ZVAL_UNDEF(zv); + zend_string_release(key); + EG(class_table)->nNumOfElements--; + } ZEND_HASH_FOREACH_END(); + EG(class_table)->pDestructor = orig_dtor; + zend_hash_rehash(EG(class_table)); + + /* Remove DECLARE opcodes */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + zend_op_array *op_array = &script->script.main_op_array; + zend_op *opline = op_array->opcodes; + zend_op *end = opline + op_array->last; + + while (opline != end) { + switch (opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1); + if (!zend_hash_exists(&script->script.class_table, key)) { + MAKE_NOP(opline); + } + break; + } + opline++; + } + + if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { + script->script.first_early_binding_opline = zend_build_delayed_early_binding_list(op_array); + if (script->script.first_early_binding_opline == (uint32_t)-1) { + op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING; + } + } + } ZEND_HASH_FOREACH_END(); +} + +static inline int preload_update_class_constants(zend_class_entry *ce) { + /* This is a separate function to work around what appears to be a bug in GCC + * maybe-uninitialized analysis. */ + int result; + zend_try { + result = zend_update_class_constants(ce); + } zend_catch { + result = FAILURE; + } zend_end_try(); + return result; +} + +static zend_class_entry *preload_load_prop_type(zend_property_info *prop, zend_string *name) { + zend_class_entry *ce; + if (zend_string_equals_literal_ci(name, "self")) { + ce = prop->ce; + } else if (zend_string_equals_literal_ci(name, "parent")) { + ce = prop->ce->parent; + } else { + ce = zend_lookup_class(name); + } + if (ce) { + return ce; + } + + zend_error_noreturn(E_ERROR, + "Failed to load class %s used by typed property %s::$%s during preloading", + ZSTR_VAL(name), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name)); + return ce; +} + +static void preload_ensure_classes_loadable() { + /* Run this in a loop, because additional classes may be loaded while updating constants etc. */ + uint32_t checked_classes_idx = 0; + while (1) { + zend_class_entry *ce; + uint32_t num_classes = zend_hash_num_elements(EG(class_table)); + if (num_classes == checked_classes_idx) { + return; + } + + ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { + if (ce->type == ZEND_INTERNAL_CLASS || _idx == checked_classes_idx) { + break; + } + + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + /* Only require that already linked classes are loadable, we'll properly check + * things when linking additional classes. */ + continue; + } + + if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + if (preload_update_class_constants(ce) == FAILURE) { + zend_error_noreturn(E_ERROR, + "Failed to resolve initializers of class %s during preloading", + ZSTR_VAL(ce->name)); + } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED); + } + + if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_class_entry *ce = + preload_load_prop_type(prop, ZEND_TYPE_NAME(prop->type)); + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } + } ZEND_HASH_FOREACH_END(); + } + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + } + } ZEND_HASH_FOREACH_END(); + checked_classes_idx = num_classes; + } +} + +static zend_string *preload_resolve_path(zend_string *filename) +{ + if (is_stream_path(ZSTR_VAL(filename))) { + return NULL; + } + return zend_resolve_path(ZSTR_VAL(filename), ZSTR_LEN(filename)); +} + +static void preload_remove_empty_includes(void) +{ + zend_persistent_script *script; + zend_bool changed; + + /* mark all as empty */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + script->empty = 1; + } ZEND_HASH_FOREACH_END(); + + /* find non empty scripts */ + do { + changed = 0; + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + if (script->empty) { + int empty = 1; + zend_op *opline = script->script.main_op_array.opcodes; + zend_op *end = opline + script->script.main_op_array.last; + + while (opline < end) { + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + opline->extended_value != ZEND_EVAL && + opline->op1_type == IS_CONST && + Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) { + + zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + + if (resolved_path) { + zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path); + zend_string_release(resolved_path); + if (!incl || !incl->empty) { + empty = 0; + break; + } + } else { + empty = 0; + break; + } + } else if (opline->opcode != ZEND_NOP && + opline->opcode != ZEND_RETURN && + opline->opcode != ZEND_HANDLE_EXCEPTION) { + empty = 0; + break; + } + opline++; + } + if (!empty) { + script->empty = 0; + changed = 1; + } + } + } ZEND_HASH_FOREACH_END(); + } while (changed); + + /* remove empty includes */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + zend_op *opline = script->script.main_op_array.opcodes; + zend_op *end = opline + script->script.main_op_array.last; + + while (opline < end) { + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + opline->extended_value != ZEND_EVAL && + opline->op1_type == IS_CONST && + Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) { + + zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + + if (resolved_path) { + zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path); + if (incl && incl->empty) { + MAKE_NOP(opline); + } else { + if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) { + /* replace relative patch with absolute one */ + zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1))); + ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path); + } + } + zend_string_release(resolved_path); + } + } + opline++; + } + } ZEND_HASH_FOREACH_END(); +} + +static void preload_register_trait_methods(zend_class_entry *ce) { + zend_op_array *op_array; + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array); + } + } ZEND_HASH_FOREACH_END(); +} + +static void preload_fix_trait_methods(zend_class_entry *ce) +{ + zend_op_array *op_array; + + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) { + zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + if (orig_op_array) { + zend_class_entry *scope = op_array->scope; + uint32_t fn_flags = op_array->fn_flags; + zend_function *prototype = op_array->prototype; + HashTable *ht = op_array->static_variables; + *op_array = *orig_op_array; + op_array->scope = scope; + op_array->fn_flags = fn_flags; + op_array->prototype = prototype; + op_array->static_variables = ht; + } + } + } ZEND_HASH_FOREACH_END(); +} + +static int preload_optimize(zend_persistent_script *script) +{ + zend_class_entry *ce; + + zend_shared_alloc_init_xlat_table(); + + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_TRAIT) { + preload_register_trait_methods(ce); + } + } ZEND_HASH_FOREACH_END(); + + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_TRAIT) { + preload_register_trait_methods(ce); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + + if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) { + return FAILURE; + } + + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS) { + preload_fix_trait_methods(ce); + } + } ZEND_HASH_FOREACH_END(); + + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + if (ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS) { + preload_fix_trait_methods(ce); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + + zend_shared_alloc_destroy_xlat_table(); + + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) { + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + return SUCCESS; +} + +static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script) +{ + zend_accel_hash_entry *bucket; + uint32_t memory_used; + uint32_t checkpoint; + + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini."); + return NULL; + } + + checkpoint = zend_shared_alloc_checkpoint_xlat_table(); + + /* Calculate the required memory size */ + memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0, 1); + + /* Allocate shared memory */ +#if defined(__AVX__) || defined(__SSE2__) + /* Align to 64-byte boundary */ + ZCG(mem) = zend_shared_alloc(memory_used + 64); + if (ZCG(mem)) { + ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L); +#if defined(__x86_64__) + memset(ZCG(mem), 0, memory_used); +#elif defined(__AVX__) + { + char *p = (char*)ZCG(mem); + char *end = p + memory_used; + __m256i ymm0 = _mm256_setzero_si256(); + + while (p < end) { + _mm256_store_si256((__m256i*)p, ymm0); + _mm256_store_si256((__m256i*)(p+32), ymm0); + p += 64; + } + } +#else + { + char *p = (char*)ZCG(mem); + char *end = p + memory_used; + __m128i xmm0 = _mm_setzero_si128(); + + while (p < end) { + _mm_store_si128((__m128i*)p, xmm0); + _mm_store_si128((__m128i*)(p+16), xmm0); + _mm_store_si128((__m128i*)(p+32), xmm0); + _mm_store_si128((__m128i*)(p+48), xmm0); + p += 64; + } + } +#endif + } +#else + ZCG(mem) = zend_shared_alloc(memory_used); + if (ZCG(mem)) { + memset(ZCG(mem), 0, memory_used); + } +#endif + if (!ZCG(mem)) { + zend_accel_error(ACCEL_LOG_FATAL, "Not enough shared memory for preloading. Consider increasing the value for the opcache.memory_consumption directive in php.ini."); + return NULL; + } + + zend_shared_alloc_restore_xlat_table(checkpoint); + + /* Copy into shared memory */ + new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 1); + + new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename); + + /* Consistency check */ + if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) { + zend_accel_error( + ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING, + "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n", + ZSTR_VAL(new_persistent_script->script.filename), + (size_t)new_persistent_script->mem, + (size_t)((char *)new_persistent_script->mem + new_persistent_script->size), + (size_t)ZCG(mem)); + } + + new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script); + + /* store script structure in the hash table */ + bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script); + if (bucket) { + zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename)); + } + + new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size); + + return new_persistent_script; +} + +static void preload_load(void) +{ + /* Load into process tables */ + zend_script *script = &ZCSG(preload_script)->script; + if (zend_hash_num_elements(&script->function_table)) { + Bucket *p = script->function_table.arData; + Bucket *end = p + script->function_table.nNumUsed; + + zend_hash_extend(CG(function_table), + CG(function_table)->nNumUsed + script->function_table.nNumUsed, 0); + for (; p != end; p++) { + _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1); + } + } + + if (zend_hash_num_elements(&script->class_table)) { + Bucket *p = script->class_table.arData; + Bucket *end = p + script->class_table.nNumUsed; + + zend_hash_extend(CG(class_table), + CG(class_table)->nNumUsed + script->class_table.nNumUsed, 0); + for (; p != end; p++) { + _zend_hash_append_ex(CG(class_table), p->key, &p->val, 1); + } + } + + if (EG(zend_constants)) { + EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; + } + if (EG(function_table)) { + EG(persistent_functions_count) = EG(function_table)->nNumUsed; + } + if (EG(class_table)) { + EG(persistent_classes_count) = EG(class_table)->nNumUsed; + } + if (CG(map_ptr_last) != ZCSG(map_ptr_last)) { + size_t old_map_ptr_last = CG(map_ptr_last); + CG(map_ptr_last) = ZCSG(map_ptr_last); + CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096); + CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1); + memset((void **) CG(map_ptr_base) + old_map_ptr_last, 0, + (CG(map_ptr_last) - old_map_ptr_last) * sizeof(void *)); + } +} + +static int preload_autoload(zend_string *filename) +{ + zend_persistent_script *persistent_script; + zend_op_array *op_array; + zend_execute_data *old_execute_data; + zend_class_entry *old_fake_scope; + zend_bool do_bailout = 0; + int ret = SUCCESS; + + if (zend_hash_exists(&EG(included_files), filename)) { + return FAILURE; + } + + persistent_script = zend_accel_hash_find(&ZCSG(hash), filename); + if (!persistent_script) { + return FAILURE; + } + + zend_hash_add_empty_element(&EG(included_files), filename); + + if (persistent_script->ping_auto_globals_mask) { + zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask); + } + + op_array = zend_accel_load_script(persistent_script, 1); + if (!op_array) { + return FAILURE; + } + + /* Execute in global context */ + old_execute_data = EG(current_execute_data); + EG(current_execute_data) = NULL; + old_fake_scope = EG(fake_scope); + EG(fake_scope) = NULL; + zend_exception_save(); + + zend_try { + zend_execute(op_array, NULL); + } zend_catch { + do_bailout = 1; + } zend_end_try(); + + if (EG(exception)) { + ret = FAILURE; + } + + zend_exception_restore(); + EG(fake_scope) = old_fake_scope; + EG(current_execute_data) = old_execute_data; + while (old_execute_data) { + if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { + if (old_execute_data->symbol_table == &EG(symbol_table)) { + zend_attach_symbol_table(old_execute_data); + } + break; + } + old_execute_data = old_execute_data->prev_execute_data; + } + + destroy_op_array(op_array); + efree_size(op_array, sizeof(zend_op_array)); + + if (do_bailout) { + zend_bailout(); + } + + return ret; +} + +static int accel_preload(const char *config) +{ + zend_file_handle file_handle; + int ret; + char *orig_open_basedir; + size_t orig_map_ptr_last; + zval *zv; + + ZCG(enabled) = 0; + ZCG(accelerator_enabled) = 0; + orig_open_basedir = PG(open_basedir); + PG(open_basedir) = NULL; + preload_orig_compile_file = accelerator_orig_compile_file; + accelerator_orig_compile_file = preload_compile_file; + + orig_map_ptr_last = CG(map_ptr_last); + + /* Compile and execute proloading script */ + zend_stream_init_filename(&file_handle, (char *) config); + + preload_scripts = emalloc(sizeof(HashTable)); + zend_hash_init(preload_scripts, 0, NULL, NULL, 0); + + zend_try { + zend_op_array *op_array; + + ret = SUCCESS; + op_array = zend_compile_file(&file_handle, ZEND_REQUIRE); + if (file_handle.opened_path) { + zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path); + } + zend_destroy_file_handle(&file_handle); + if (op_array) { + zend_execute(op_array, NULL); + zend_exception_restore(); + if (UNEXPECTED(EG(exception))) { + if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { + zend_user_exception_handler(); + } + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + CG(unclean_shutdown) = 1; + ret = FAILURE; + } + } + destroy_op_array(op_array); + efree_size(op_array, sizeof(zend_op_array)); + } else { + CG(unclean_shutdown) = 1; + ret = FAILURE; + } + + if (ret == SUCCESS) { + preload_ensure_classes_loadable(); + } + } zend_catch { + ret = FAILURE; + } zend_end_try(); + + PG(open_basedir) = orig_open_basedir; + accelerator_orig_compile_file = preload_orig_compile_file; + ZCG(enabled) = 1; + + zend_destroy_file_handle(&file_handle); + + if (ret == SUCCESS) { + zend_persistent_script *script; + zend_string *filename; + int ping_auto_globals_mask; + int i; + zend_class_entry *ce; + zend_op_array *op_array; + + if (PG(auto_globals_jit)) { + ping_auto_globals_mask = zend_accel_get_auto_globals(); + } else { + ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit(); + } + + /* Cleanup executor */ + EG(flags) |= EG_FLAGS_IN_SHUTDOWN; + + php_call_shutdown_functions(); + zend_call_destructors(); + php_free_shutdown_functions(); + + /* Release stored values to avoid dangling pointers */ + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0); + +#if ZEND_DEBUG + if (gc_enabled() && !CG(unclean_shutdown)) { + gc_collect_cycles(); + } +#endif + + zend_objects_store_free_object_storage(&EG(objects_store), 1); + + /* Cleanup static variables of preloaded functions */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_op_array *op_array = Z_PTR_P(zv); + if (op_array->type == ZEND_INTERNAL_FUNCTION) { + break; + } + ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_PRELOADED); + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + ZEND_ASSERT(GC_REFCOUNT(ht) == 1); + zend_array_destroy(ht); + ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL); + } + } + } ZEND_HASH_FOREACH_END(); + + /* Cleanup static properties and variables of preloaded classes */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_PRELOADED); + if (ce->default_static_members_count) { + zend_cleanup_internal_class_data(ce); + if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) { + int i; + + for (i = 0; i < ce->default_static_members_count; i++) { + if (Z_TYPE(ce->default_static_members_table[i]) == IS_CONSTANT_AST) { + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + break; + } + } + } + } + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_op_array *op_array; + + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->type == ZEND_USER_FUNCTION) { + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht) { + if (GC_DELREF(ht) == 0) { + zend_array_destroy(ht); + } + ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL); + } + } + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); + + CG(map_ptr_last) = orig_map_ptr_last; + + if (EG(full_tables_cleanup)) { + zend_accel_error(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function."); + ret = FAILURE; + goto finish; + } + + /* Inheritance errors may be thrown during linking */ + zend_try { + preload_link(); + } zend_catch { + CG(map_ptr_last) = orig_map_ptr_last; + ret = FAILURE; + goto finish; + } zend_end_try(); + + preload_remove_empty_includes(); + + /* Don't preload constants */ + if (EG(zend_constants)) { + zend_string *key; + zval *zv; + + /* Remember __COMPILER_HALT_OFFSET__(s) */ + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + zend_execute_data *orig_execute_data = EG(current_execute_data); + zend_execute_data fake_execute_data; + zval *offset; + + memset(&fake_execute_data, 0, sizeof(fake_execute_data)); + fake_execute_data.func = (zend_function*)&script->script.main_op_array; + EG(current_execute_data) = &fake_execute_data; + if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { + script->compiler_halt_offset = Z_LVAL_P(offset); + } + EG(current_execute_data) = orig_execute_data; + } ZEND_HASH_FOREACH_END(); + + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) { + zend_constant *c = Z_PTR_P(zv); + if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) { + break; + } + EG(zend_constants)->pDestructor(zv); + zend_string_release(key); + } ZEND_HASH_FOREACH_END_DEL(); + } + + script = create_persistent_script(); + script->ping_auto_globals_mask = ping_auto_globals_mask; + + /* Store all functions and classes in a single pseudo-file */ + filename = zend_string_init("$PRELOAD$", sizeof("$PRELOAD$") - 1, 0); +#if ZEND_USE_ABS_CONST_ADDR + init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1); +#else + init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2); +#endif + script->script.main_op_array.last = 1; + script->script.main_op_array.last_literal = 1; +#if ZEND_USE_ABS_CONST_ADDR + script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval)); +#else + script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1); +#endif + ZVAL_NULL(script->script.main_op_array.literals); + memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op)); + script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN; + script->script.main_op_array.opcodes[0].op1_type = IS_CONST; + script->script.main_op_array.opcodes[0].op1.constant = 0; + ZEND_PASS_TWO_UPDATE_CONSTANT(&script->script.main_op_array, script->script.main_op_array.opcodes, script->script.main_op_array.opcodes[0].op1); + + script->script.main_op_array.filename = filename; + script->script.filename = zend_string_copy(filename); + + script->script.first_early_binding_opline = (uint32_t)-1; + + preload_move_user_functions(CG(function_table), &script->script.function_table); + preload_move_user_classes(CG(class_table), &script->script.class_table); + + zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0); + + if (preload_optimize(script) != SUCCESS) { + zend_accel_error(ACCEL_LOG_FATAL, "Optimization error during preloading!"); + return FAILURE; + } + + zend_shared_alloc_init_xlat_table(); + + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + /* Store method names first, because they may be shared between preloaded and non-preloaded classes */ + ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name); + + if (!new_name) { + new_name = accel_new_interned_string(op_array->function_name); + zend_shared_alloc_register_xlat_entry(op_array->function_name, new_name); + } + op_array->function_name = new_name; + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + + ZCSG(preload_script) = preload_script_in_shared_memory(script); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + zend_string_release(filename); + + ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0); + + preload_load(); + + /* Store individual scripts with unlinked classes */ + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + i = 0; + ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*)); + ZEND_HASH_FOREACH_PTR(preload_scripts, script) { + if (zend_hash_num_elements(&script->script.class_table) > 1) { + zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0); + } + ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script); + } ZEND_HASH_FOREACH_END(); + ZCSG(saved_scripts)[i] = NULL; + + zend_shared_alloc_save_state(); + accel_interned_strings_save_state(); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + zend_shared_alloc_destroy_xlat_table(); + + zend_preload_autoload = preload_autoload; + } else { + CG(map_ptr_last) = orig_map_ptr_last; + } + +finish: + zend_hash_destroy(preload_scripts); + efree(preload_scripts); + preload_scripts = NULL; + + return ret; +} + +static size_t preload_ub_write(const char *str, size_t str_length) +{ + return fwrite(str, 1, str_length, stdout); +} + +static void preload_flush(void *server_context) +{ + fflush(stdout); +} + +static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) +{ + return 0; +} + +static int preload_send_headers(sapi_headers_struct *sapi_headers) +{ + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +static void preload_send_header(sapi_header_struct *sapi_header, void *server_context) +{ +} + +static int accel_finish_startup(void) +{ + if (!ZCG(enabled) || !accel_startup_ok) { + return SUCCESS; + } + + if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) { +#ifdef ZEND_WIN32 + zend_accel_error(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); + return FAILURE; +#else + int in_child = 0; + int ret = SUCCESS; + int rc; + int orig_error_reporting; + + int (*orig_activate)(TSRMLS_D) = sapi_module.activate; + int (*orig_deactivate)(TSRMLS_D) = sapi_module.deactivate; + void (*orig_register_server_variables)(zval *track_vars_array TSRMLS_DC) = sapi_module.register_server_variables; + int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.header_handler; + int (*orig_send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.send_headers; + void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)= sapi_module.send_header; + char *(*orig_getenv)(char *name, size_t name_len TSRMLS_DC) = sapi_module.getenv; + size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; + void (*orig_flush)(void *server_context) = sapi_module.flush; + uint32_t orig_compiler_options = CG(compiler_options); +#ifdef ZEND_SIGNALS + zend_bool old_reset_signals = SIGG(reset); +#endif + + if (UNEXPECTED(file_cache_only)) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); + return SUCCESS; + } + + /* exclusive lock */ + zend_shared_alloc_lock(); + + if (ZCSG(preload_script)) { + /* Preloading was done in another process */ + preload_load(); + zend_shared_alloc_unlock(); + return SUCCESS; + } + + if (geteuid() == 0) { + pid_t pid; + struct passwd *pw; + + if (!ZCG(accel_directives).preload_user + || !*ZCG(accel_directives).preload_user) { + zend_shared_alloc_unlock(); + zend_accel_error(ACCEL_LOG_FATAL, "\"opcache.preload_user\" has not been defined"); + return FAILURE; + } + + pw = getpwnam(ZCG(accel_directives).preload_user); + if (pw == NULL) { + zend_shared_alloc_unlock(); + zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user); + return FAILURE; + } + + pid = fork(); + if (pid == -1) { + zend_shared_alloc_unlock(); + zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to fork()"); + return FAILURE; + } else if (pid == 0) { /* children */ + if (setgid(pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid); + exit(1); + } + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid); + exit(1); + } + if (setuid(pw->pw_uid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid); + exit(1); + } + in_child = 1; + } else { /* parent */ + int status; + + if (waitpid(pid, &status, 0) < 0) { + zend_shared_alloc_unlock(); + zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid); + return FAILURE; + } + + if (ZCSG(preload_script)) { + preload_load(); + } + + zend_shared_alloc_unlock(); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return SUCCESS; + } else { + return FAILURE; + } + } + } else { + if (ZCG(accel_directives).preload_user + && *ZCG(accel_directives).preload_user) { + zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); + } + } + + sapi_module.activate = NULL; + sapi_module.deactivate = NULL; + sapi_module.register_server_variables = NULL; + sapi_module.header_handler = preload_header_handler; + sapi_module.send_headers = preload_send_headers; + sapi_module.send_header = preload_send_header; + sapi_module.getenv = NULL; + sapi_module.ub_write = preload_ub_write; + sapi_module.flush = preload_flush; + + if (in_child) { + CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD; + } + CG(compiler_options) |= ZEND_COMPILE_PRELOAD; + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; + + zend_interned_strings_switch_storage(1); + +#ifdef ZEND_SIGNALS + SIGG(reset) = 0; +#endif + + orig_error_reporting = EG(error_reporting); + EG(error_reporting) = 0; + + rc = php_request_startup(); + + EG(error_reporting) = orig_error_reporting; + + if (rc == SUCCESS) { + zend_bool orig_report_memleaks; + + /* don't send headers */ + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + php_output_set_status(0); + + ZCG(auto_globals_mask) = 0; + ZCG(request_time) = (time_t)sapi_get_request_time(); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_key_len) = 0; + ZCG(include_path_check) = 1; + + ZCG(cwd) = NULL; + ZCG(cwd_key_len) = 0; + ZCG(cwd_check) = 1; + + if (accel_preload(ZCG(accel_directives).preload) != SUCCESS) { + ret = FAILURE; + } + + orig_report_memleaks = PG(report_memleaks); + PG(report_memleaks) = 0; +#ifdef ZEND_SIGNALS + /* We may not have registered signal handlers due to SIGG(reset)=0, so + * also disable the check that they are registered. */ + SIGG(check) = 0; +#endif + php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */ + PG(report_memleaks) = orig_report_memleaks; + } else { + zend_shared_alloc_unlock(); + ret = FAILURE; + } +#ifdef ZEND_SIGNALS + SIGG(reset) = old_reset_signals; +#endif + + CG(compiler_options) = orig_compiler_options; + + sapi_module.activate = orig_activate; + sapi_module.deactivate = orig_deactivate; + sapi_module.register_server_variables = orig_register_server_variables; + sapi_module.header_handler = orig_header_handler; + sapi_module.send_headers = orig_send_headers; + sapi_module.send_header = orig_send_header; + sapi_module.getenv = orig_getenv; + sapi_module.ub_write = orig_ub_write; + sapi_module.flush = orig_flush; + + sapi_activate(); + + if (in_child) { + if (ret == SUCCESS) { + exit(0); + } else { + exit(2); + } + } + + return ret; +#endif + } + + return SUCCESS; +} + ZEND_EXT_API zend_extension zend_extension_entry = { ACCELERATOR_PRODUCT_NAME, /* name */ PHP_VERSION, /* version */ "Zend Technologies", /* author */ "http://www.zend.com/", /* URL */ - "Copyright (c) 1999-2018", /* copyright */ + "Copyright (c)", /* copyright */ accel_startup, /* startup */ NULL, /* shutdown */ - accel_activate, /* per-script activation */ - accel_deactivate, /* per-script deactivation */ + NULL, /* per-script activation */ + NULL, /* per-script deactivation */ NULL, /* message handler */ NULL, /* op_array handler */ NULL, /* extended statement handler */ diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index fb626fb09a..c2f95d7c41 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -91,7 +91,7 @@ extern int lock_file; #endif -#if defined(HAVE_OPCACHE_FILE_CACHE) && defined(ZEND_WIN32) +#if defined(ZEND_WIN32) # define ENABLE_FILE_CACHE_FALLBACK 1 #else # define ENABLE_FILE_CACHE_FALLBACK 0 @@ -116,6 +116,7 @@ typedef struct _zend_persistent_script { accel_time_t timestamp; /* the script modification time */ zend_bool corrupted; zend_bool is_phar; + zend_bool empty; void *mem; /* shared memory area used by script structures */ size_t size; /* size of used shared memory */ @@ -174,24 +175,25 @@ typedef struct _zend_accel_directives { #ifndef ZEND_WIN32 char *lockfile_path; #endif -#ifdef HAVE_OPCACHE_FILE_CACHE char *file_cache; zend_bool file_cache_only; zend_bool file_cache_consistency_checks; -#endif #if ENABLE_FILE_CACHE_FALLBACK zend_bool file_cache_fallback; #endif #ifdef HAVE_HUGE_CODE_PAGES zend_bool huge_code_pages; #endif + char *preload; +#ifndef ZEND_WIN32 + char *preload_user; +#endif +#ifdef ZEND_WIN32 + char *cache_id; +#endif } zend_accel_directives; typedef struct _zend_accel_globals { - /* copy of CG(function_table) used for compilation scripts into cache */ - /* initially it contains only internal functions */ - HashTable function_table; - int internal_functions_count; int counted; /* the process uses shared memory */ zend_bool enabled; zend_bool locked; /* thread obtained exclusive lock */ @@ -210,7 +212,6 @@ typedef struct _zend_accel_globals { int auto_globals_mask; time_t request_time; time_t last_restart_time; /* used to synchronize SHM and in-process caches */ - char system_id[32]; HashTable xlat_table; #ifndef ZEND_WIN32 zend_ulong root_hash; @@ -219,6 +220,7 @@ typedef struct _zend_accel_globals { void *mem; void *arena_mem; zend_persistent_script *current_persistent_script; + zend_bool is_immutable_class; /* cache to save hash lookup on the same INCLUDE opcode */ const zend_op *cache_opline; zend_persistent_script *cache_persistent_script; @@ -246,6 +248,8 @@ typedef struct _zend_accel_shared_globals { zend_ulong manual_restarts; /* number of restarts scheduled by opcache_reset() */ zend_accel_hash hash; /* hash table for cached scripts */ + size_t map_ptr_last; + /* Directives & Maintenance */ time_t start_time; time_t last_restart_time; @@ -260,6 +264,10 @@ typedef struct _zend_accel_shared_globals { #endif zend_bool restart_in_progress; + /* Preloading */ + zend_persistent_script *preload_script; + zend_persistent_script **saved_scripts; + /* uninitialized HashTable Support */ uint32_t uninitialized_bucket[-HT_MIN_MASK]; @@ -267,10 +275,12 @@ typedef struct _zend_accel_shared_globals { zend_string_table interned_strings; } zend_accel_shared_globals; +extern char accel_system_id[32]; +#ifdef ZEND_WIN32 +extern char accel_uname_id[32]; +#endif extern zend_bool accel_startup_ok; -#ifdef HAVE_OPCACHE_FILE_CACHE extern zend_bool file_cache_only; -#endif #if ENABLE_FILE_CACHE_FALLBACK extern zend_bool fallback_process; #endif @@ -292,6 +302,7 @@ extern zend_accel_globals accel_globals; extern char *zps_api_failure_reason; void accel_shutdown(void); +int accel_activate(INIT_FUNC_ARGS); int accel_post_deactivate(void); void zend_accel_schedule_restart(zend_accel_restart_reason reason); void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason); diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 4810217c14..6c40cafc1c 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -1,30 +1,26 @@ -dnl config.m4 for extension opcache - -PHP_ARG_ENABLE(opcache, whether to enable Zend OPcache support, -[ --disable-opcache Disable Zend OPcache support], yes) - -PHP_ARG_ENABLE(opcache-file, whether to enable file based caching, -[ --disable-opcache-file Disable file based caching], yes, no) - -PHP_ARG_ENABLE(huge-code-pages, whether to enable copying PHP CODE pages into HUGE PAGES, -[ --disable-huge-code-pages - Disable copying PHP CODE pages into HUGE PAGES], yes, no) +PHP_ARG_ENABLE([opcache], + [whether to enable Zend OPcache support], + [AS_HELP_STRING([--disable-opcache], + [Disable Zend OPcache support])], + [yes]) + +PHP_ARG_ENABLE([huge-code-pages], + [whether to enable copying PHP CODE pages into HUGE PAGES], + [AS_HELP_STRING([--disable-huge-code-pages], + [Disable copying PHP CODE pages into HUGE PAGES])], + [yes], + [no]) if test "$PHP_OPCACHE" != "no"; then - if test "$PHP_OPCACHE_FILE" = "yes"; then - AC_DEFINE(HAVE_OPCACHE_FILE_CACHE, 1, [Define to enable file based caching (experimental)]) - fi + dnl Always build as shared extension + ext_shared=yes if test "$PHP_HUGE_CODE_PAGES" = "yes"; then AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE pages into HUGE PAGES (experimental)]) fi - AC_CHECK_FUNC(mprotect,[ - AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function]) - ]) - - AC_CHECK_HEADERS([unistd.h sys/uio.h]) + AC_CHECK_FUNCS([mprotect]) AC_MSG_CHECKING(for sysvipc shared memory support) AC_RUN_IFELSE([AC_LANG_SOURCE([[ @@ -148,61 +144,7 @@ int main() { msg=yes],[msg=no],[msg=no]) AC_MSG_RESULT([$msg]) - AC_MSG_CHECKING(for mmap() using /dev/zero shared memory support) - AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> - -#ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -#endif - -int main() { - pid_t pid; - int status; - int fd; - char *shm; - - fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); - if (fd == -1) { - return 1; - } - - shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (shm == MAP_FAILED) { - return 2; - } - - strcpy(shm, "hello"); - - pid = fork(); - if (pid < 0) { - return 5; - } else if (pid == 0) { - strcpy(shm, "bye"); - return 6; - } - if (wait(&status) != pid) { - return 7; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { - return 8; - } - if (strcmp(shm, "bye") != 0) { - return 9; - } - return 0; -} -]])],[dnl - AC_DEFINE(HAVE_SHM_MMAP_ZERO, 1, [Define if you have mmap("/dev/zero") SHM support]) - msg=yes],[msg=no],[msg=no]) - AC_MSG_RESULT([$msg]) - + PHP_CHECK_FUNC_LIB(shm_open, rt) AC_MSG_CHECKING(for mmap() using shm_open() shared memory support) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <sys/types.h> @@ -226,7 +168,7 @@ int main() { char *shm; char tmpname[4096]; - sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); + sprintf(tmpname,"/opcache.test.shm.%dXXXXXX", getpid()); if (mktemp(tmpname) == NULL) { return 1; } @@ -269,77 +211,13 @@ int main() { } ]])],[dnl AC_DEFINE(HAVE_SHM_MMAP_POSIX, 1, [Define if you have POSIX mmap() SHM support]) - msg=yes],[msg=no],[msg=no]) - AC_MSG_RESULT([$msg]) - - AC_MSG_CHECKING(for mmap() using regular file shared memory support) - AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -#endif - -int main() { - pid_t pid; - int status; - int fd; - char *shm; - char tmpname[4096]; - - sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); - if (mktemp(tmpname) == NULL) { - return 1; - } - fd = open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - if (fd == -1) { - return 2; - } - if (ftruncate(fd, 4096) < 0) { - close(fd); - unlink(tmpname); - return 3; - } - - shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (shm == MAP_FAILED) { - return 4; - } - unlink(tmpname); - close(fd); - - strcpy(shm, "hello"); - - pid = fork(); - if (pid < 0) { - return 5; - } else if (pid == 0) { - strcpy(shm, "bye"); - return 6; - } - if (wait(&status) != pid) { - return 7; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { - return 8; - } - if (strcmp(shm, "bye") != 0) { - return 9; - } - return 0; -} -]])],[dnl - AC_DEFINE(HAVE_SHM_MMAP_FILE, 1, [Define if you have mmap() SHM support]) - msg=yes],[msg=no],[msg=no]) - AC_MSG_RESULT([$msg]) + AC_MSG_RESULT([yes]) + PHP_CHECK_LIBRARY(rt, shm_unlink, [PHP_ADD_LIBRARY(rt,1,OPCACHE_SHARED_LIBADD)]) + ],[ + AC_MSG_RESULT([no]) + ],[ + AC_MSG_RESULT([no]) + ]) PHP_NEW_EXTENSION(opcache, ZendAccelerator.c \ @@ -381,4 +259,5 @@ int main() { PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1) PHP_ADD_EXTENSION_DEP(opcache, pcre) + PHP_SUBST(OPCACHE_SHARED_LIBADD) fi diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 index 1d78f7e548..fba1b1bef1 100644 --- a/ext/opcache/config.w32 +++ b/ext/opcache/config.w32 @@ -1,15 +1,9 @@ ARG_ENABLE("opcache", "whether to enable Zend OPcache support", "yes"); -ARG_ENABLE("opcache-file", "whether to enable file based caching", "yes"); - /* var PHP_OPCACHE_PGO = false; */ if (PHP_OPCACHE != "no") { - if (PHP_OPCACHE_FILE == "yes") { - AC_DEFINE('HAVE_OPCACHE_FILE_CACHE', 1, 'Define to enable file based caching (experimental)'); - } - ZEND_EXTENSION('opcache', "\ ZendAccelerator.c \ zend_accelerator_blacklist.c \ diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 121a2e4a40..dc02d038f5 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -32,6 +32,9 @@ #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) # define MAP_ANONYMOUS MAP_ANON #endif +#if defined(MAP_ALIGNED_SUPER) +# define MAP_HUGETLB MAP_ALIGNED_SUPER +#endif static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) { @@ -48,10 +51,14 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ #ifdef MAP_HUGETLB /* Try to allocate huge pages first to reduce dTLB misses. - * OS has to be configured properly + * OSes has to be configured properly + * on Linux * (e.g. https://wiki.debian.org/Hugepages#Enabling_HugeTlbPage) * You may verify huge page usage with the following command: * `grep "Huge" /proc/meminfo` + * on FreeBSD + * sysctl vm.pmap.pg_ps_enabled entry + * (boot time config only, but enabled by default on most arches). */ shared_segment->p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0); if (shared_segment->p != MAP_FAILED) { diff --git a/ext/opcache/shared_alloc_posix.c b/ext/opcache/shared_alloc_posix.c index 9d860a68ca..2db83aff42 100644 --- a/ext/opcache/shared_alloc_posix.c +++ b/ext/opcache/shared_alloc_posix.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c index 832593935b..29225bef21 100644 --- a/ext/opcache/shared_alloc_shm.c +++ b/ext/opcache/shared_alloc_shm.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -29,7 +29,6 @@ #include <sys/types.h> #include <sys/shm.h> #include <sys/ipc.h> -#include <dirent.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -61,13 +60,13 @@ static int create_segments(size_t requested_size, zend_shared_segment_shm ***sha int shmget_flags; zend_shared_segment_shm *shared_segments; - seg_allocate_size = SEG_ALLOC_SIZE_MAX; - /* determine segment size we _really_ need: - * no more than to include requested_size - */ - while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) { - seg_allocate_size >>= 1; - } + seg_allocate_size = SEG_ALLOC_SIZE_MAX; + /* determine segment size we _really_ need: + * no more than to include requested_size + */ + while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) { + seg_allocate_size >>= 1; + } shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL; diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c index 26bf0539ea..d2c31dd63b 100644 --- a/ext/opcache/shared_alloc_win32.c +++ b/ext/opcache/shared_alloc_win32.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -19,23 +19,24 @@ +----------------------------------------------------------------------+ */ +#include "php.h" #include "ZendAccelerator.h" #include "zend_shared_alloc.h" #include "zend_accelerator_util_funcs.h" #include "zend_execute.h" #include "SAPI.h" #include "tsrm_win32.h" +#include "win32/winutil.h" #include <winbase.h> #include <process.h> #include <LMCONS.H> #define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea" #define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex" +#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000 +#define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase" #define ACCEL_EVENT_SOURCE "Zend OPcache" -/* address of mapping base and address of execute_ex */ -#define ACCEL_BASE_POINTER_SIZE (2 * sizeof(void*)) - static HANDLE memfile = NULL, memory_mutex = NULL; static void *mapping_base; @@ -43,25 +44,13 @@ static void *mapping_base; static void zend_win_error_message(int type, char *msg, int err) { - LPVOID lpMsgBuf; HANDLE h; char *ev_msgs[2]; - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); + char *buf = php_win32_error_to_msg(err); h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE)); ev_msgs[0] = msg; - ev_msgs[1] = lpMsgBuf; + ev_msgs[1] = buf; ReportEvent(h, // event log handle EVENTLOG_ERROR_TYPE, // event type 0, // category zero @@ -73,25 +62,33 @@ static void zend_win_error_message(int type, char *msg, int err) NULL); // pointer to data DeregisterEventSource(h); - LocalFree( lpMsgBuf ); - zend_accel_error(type, "%s", msg); + + php_win32_error_msg_free(buf); } static char *create_name_with_username(char *name) { - static char newname[MAXPATHLEN + UNLEN + 4 + 1 + 32 + 21]; - char *uname; + static char newname[MAXPATHLEN + 32 + 4 + 1 + 32 + 21]; + snprintf(newname, sizeof(newname) - 1, "%s@%.32s@%.20s@%.32s", name, accel_uname_id, sapi_module.name, accel_system_id); + + return newname; +} + +static char *get_mmap_base_file(void) +{ + static char windir[MAXPATHLEN+ 32 + 3 + sizeof("\\\\@") + 1 + 32 + 21]; + int l; - uname = php_win32_get_username(); - if (!uname) { - return NULL; + GetTempPath(MAXPATHLEN, windir); + l = strlen(windir); + if ('\\' == windir[l-1]) { + l--; } - snprintf(newname, sizeof(newname) - 1, "%s@%s@%.20s@%.32s", name, uname, sapi_module.name, ZCG(system_id)); - free(uname); + snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%.32s@%.20s@%.32s", ACCEL_FILEMAP_BASE, accel_uname_id, sapi_module.name, accel_system_id); - return newname; + return windir; } void zend_shared_alloc_create_lock(void) @@ -122,20 +119,39 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in) { int err; void *wanted_mapping_base; + char *mmap_base_file = get_mmap_base_file(); + FILE *fp = fopen(mmap_base_file, "r"); MEMORY_BASIC_INFORMATION info; void *execute_ex_base; int execute_ex_moved; - mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, ACCEL_BASE_POINTER_SIZE, NULL); - if (mapping_base == NULL) { + if (!fp) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err); + *error_in="fopen"; + return ALLOC_FAILURE; + } + if (!fscanf(fp, "%p", &wanted_mapping_base)) { err = GetLastError(); zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err); *error_in="read mapping base"; + fclose(fp); + return ALLOC_FAILURE; + } + if (!fscanf(fp, "%p", &execute_ex_base)) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read execute_ex base address", err); + *error_in="read execute_ex base"; + fclose(fp); return ALLOC_FAILURE; } - wanted_mapping_base = ((void**)mapping_base)[0]; - execute_ex_base = ((void**)mapping_base)[1]; - UnmapViewOfFile(mapping_base); + fclose(fp); + + if (0 > win32_utime(mmap_base_file, NULL)) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + } execute_ex_moved = (void *)execute_ex != execute_ex_base; @@ -191,7 +207,7 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in) } return ALLOC_FAIL_MAPPING; } - smm_shared_globals = (zend_smm_shared_globals *) ((char*)mapping_base + ACCEL_BASE_POINTER_SIZE); + smm_shared_globals = (zend_smm_shared_globals *) mapping_base; return SUCCESSFULLY_REATTACHED; } @@ -309,9 +325,19 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ *error_in = "MapViewOfFile"; return ALLOC_FAILURE; } else { - ((void**)mapping_base)[0] = mapping_base; - ((void**)mapping_base)[1] = (void*)execute_ex; - ((char*)shared_segment->p) += ACCEL_BASE_POINTER_SIZE; + char *mmap_base_file = get_mmap_base_file(); + void *execute_ex_base = (void *)execute_ex; + FILE *fp = fopen(mmap_base_file, "w"); + if (!fp) { + err = GetLastError(); + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err); + return ALLOC_FAILURE; + } + fprintf(fp, "%p\n", mapping_base); + fprintf(fp, "%p\n", execute_ex_base); + fclose(fp); } shared_segment->pos = 0; diff --git a/ext/opcache/tests/blacklist-win32.phpt b/ext/opcache/tests/blacklist-win32.phpt index 70c87338b5..17d75119ae 100644 --- a/ext/opcache/tests/blacklist-win32.phpt +++ b/ext/opcache/tests/blacklist-win32.phpt @@ -13,8 +13,8 @@ opcache.file_cache_only=0 <?php $conf = opcache_get_configuration(); $conf = $conf['blacklist']; -$conf[3] = preg_replace("!^\\Q".dirname(__FILE__)."\\E!", "__DIR__", $conf[3]); -$conf[4] = preg_replace("!^\\Q".dirname(__FILE__)."\\E!", "__DIR__", $conf[4]); +$conf[3] = preg_replace("!^\\Q".__DIR__."\\E!", "__DIR__", $conf[3]); +$conf[4] = preg_replace("!^\\Q".__DIR__."\\E!", "__DIR__", $conf[4]); print_r($conf); include("blacklist.inc"); $status = opcache_get_status(); diff --git a/ext/opcache/tests/blacklist.phpt b/ext/opcache/tests/blacklist.phpt index da3ef47c99..21139b751d 100644 --- a/ext/opcache/tests/blacklist.phpt +++ b/ext/opcache/tests/blacklist.phpt @@ -6,21 +6,29 @@ opcache.enable_cli=1 opcache.blacklist_filename={PWD}/opcache-*.blacklist opcache.file_update_protection=0 opcache.file_cache_only=0 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> -<?php if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip not for Windows'); } ?> +<?php +if (substr(PHP_OS, 0, 3) == 'WIN') { + die('skip not for Windows'); +} +/* On macOS, `/tmp` is an alias to `/private/tmp` . + * So, we should write `%S/tmp/path` as `/tmp/path`, except for Windows. + */ +?> --FILE-- <?php $conf = opcache_get_configuration(); $conf = $conf['blacklist']; -$conf[3] = preg_replace("!^\\Q".dirname(__FILE__)."\\E!", "__DIR__", $conf[3]); -$conf[4] = preg_replace("!^\\Q".dirname(__FILE__)."\\E!", "__DIR__", $conf[4]); +$conf[3] = preg_replace("!^\\Q".__DIR__."\\E!", "__DIR__", $conf[3]); +$conf[4] = preg_replace("!^\\Q".__DIR__."\\E!", "__DIR__", $conf[4]); print_r($conf); include("blacklist.inc"); $status = opcache_get_status(); print_r(count($status['scripts'])); ?> ---EXPECT-- +--EXPECTF-- Array ( [0] => /path/to/foo @@ -28,8 +36,8 @@ Array [2] => /path/to/bar [3] => __DIR__/blacklist.inc [4] => __DIR__/current.php - [5] => /tmp/path/?nocache.inc - [6] => /tmp/path/*/somedir + [5] => %S/tmp/path/?nocache.inc + [6] => %S/tmp/path/*/somedir ) ok 1 diff --git a/ext/opcache/tests/bug64353.phpt b/ext/opcache/tests/bug64353.phpt index b1f0c6e713..42cb45c915 100644 --- a/ext/opcache/tests/bug64353.phpt +++ b/ext/opcache/tests/bug64353.phpt @@ -26,4 +26,5 @@ include "php://filter/read=bug.test/resource=data://text/plain,<?php\n"; echo "OK\n"; ?> --EXPECT-- +Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0 OK diff --git a/ext/opcache/tests/bug65510.phpt b/ext/opcache/tests/bug65510.phpt index ba19d27d6f..9043b58e12 100644 --- a/ext/opcache/tests/bug65510.phpt +++ b/ext/opcache/tests/bug65510.phpt @@ -17,4 +17,5 @@ function parseQuery() { parseQuery(); echo "ok\n"; --EXPECT-- +Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0 ok diff --git a/ext/opcache/tests/bug66338.phpt b/ext/opcache/tests/bug66338.phpt index 6553f83f29..5cd9693793 100644 --- a/ext/opcache/tests/bug66338.phpt +++ b/ext/opcache/tests/bug66338.phpt @@ -4,22 +4,24 @@ Bug #66338 (Optimization binding of class constants is not safely opcacheable) opcache.enable=0 --SKIPIF-- <?php if (!extension_loaded('Zend OPcache') || php_sapi_name() != "cli") die("skip CLI only"); ?> +--CONFLICTS-- +server --FILE-- <?php $root = str_replace('.php', "", __FILE__); $base = basename( $root ); -file_put_contents( "$root-Officials.inc", '<?php +file_put_contents( "$root-Officials.inc", '<?php class Officials { static function getLeader() { return LocalTerms::GOV_LEADER; } } ' ); -file_put_contents( "$root-clientUS.php", '<?php +file_put_contents( "$root-clientUS.php", '<?php class LocalTerms { const GOV_LEADER = "Barack Hussein Obama II"; } require \''.$root.'-Officials.inc\'; printf( "The President of the USA is %s\n", Officials::getLeader() ); ' ); -file_put_contents( "$root-clientUK.php", '<?php +file_put_contents( "$root-clientUK.php", '<?php class LocalTerms { const GOV_LEADER = "David William Donald Cameron"; } require \''.$root.'-Officials.inc\'; printf( "The Prime Minister of the UK is %s\n", Officials::getLeader() ); diff --git a/ext/opcache/tests/bug68644.phpt b/ext/opcache/tests/bug68644.phpt index b82615ac4a..1a967facaf 100644 --- a/ext/opcache/tests/bug68644.phpt +++ b/ext/opcache/tests/bug68644.phpt @@ -4,7 +4,6 @@ Bug #68644 strlen incorrect : mbstring + func_overload=2 + UTF-8 + Opcache opcache.enable=1 opcache.enable_cli=1 mbstring.func_overload=2 -mbstring.internal_encoding=UTF-8 --SKIPIF-- <?php if (!extension_loaded('Zend OPcache') || !extension_loaded("mbstring")) die("skip"); ?> --FILE-- diff --git a/ext/opcache/tests/bug695449.phpt b/ext/opcache/tests/bug69549.phpt index 53a143a229..53a143a229 100644 --- a/ext/opcache/tests/bug695449.phpt +++ b/ext/opcache/tests/bug69549.phpt diff --git a/ext/opcache/tests/bug78014.inc b/ext/opcache/tests/bug78014.inc new file mode 100644 index 0000000000..51a1196476 --- /dev/null +++ b/ext/opcache/tests/bug78014.inc @@ -0,0 +1,11 @@ +<?php +class A { + function foo() { return 0; } +} +class B extends A { + const X = UNRESOLVED; +} +class C extends B { + const X = 42; + function foo() { return 42; } +} diff --git a/ext/opcache/tests/bug78014.phpt b/ext/opcache/tests/bug78014.phpt new file mode 100644 index 0000000000..47ec05977c --- /dev/null +++ b/ext/opcache/tests/bug78014.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #78014 (Preloaded classes may depend on non-preloaded classes due to unresolved consts) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78014.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +class B extends A { + function foo(): int { return 24; } +} +$c = new C; +var_dump($c->foo()); +?> +--EXPECTF-- +Warning: Can't preload unlinked class C: Parent with unresolved initializers B in %s on line %d + +Warning: Can't preload class B with unresolved initializer for constant X in %s on line %d + +Fatal error: Uncaught Error: Class 'C' not found in %sbug78014.php:5 +Stack trace: +#0 {main} + thrown in %sbug78014.php on line 5 diff --git a/ext/opcache/tests/bug78034.phpt b/ext/opcache/tests/bug78034.phpt new file mode 100644 index 0000000000..b4ef93dff6 --- /dev/null +++ b/ext/opcache/tests/bug78034.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #78034: "pecl" tool fails with abort assertion in zend_ssa.c +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +function &ref() {} + +class Test { + function method($bool) { + if (!$bool) { + $this->foo = &ref(); + } + + $this->foo = &ref(); + } +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/opcache/tests/bug78106.phpt b/ext/opcache/tests/bug78106.phpt index 87a524851d..a46312baa0 100644 --- a/ext/opcache/tests/bug78106.phpt +++ b/ext/opcache/tests/bug78106.phpt @@ -2,6 +2,8 @@ Bug #78106: Path resolution fails if opcache disabled during request --SKIPIF-- <?php require_once('skipif.inc'); ?> +--CONFLICTS-- +server --FILE-- <?php diff --git a/ext/opcache/tests/bug78175.phpt b/ext/opcache/tests/bug78175.phpt new file mode 100644 index 0000000000..b477e04fe6 --- /dev/null +++ b/ext/opcache/tests/bug78175.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #78175 (Preloading segfaults at preload time and at runtime) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78175.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +OK +--EXPECT-- +Shutdown +OK diff --git a/ext/opcache/tests/bug78175_2.phpt b/ext/opcache/tests/bug78175_2.phpt new file mode 100644 index 0000000000..7b5ad850ae --- /dev/null +++ b/ext/opcache/tests/bug78175_2.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #78175.2 (Preloading segfaults at preload time and at runtime) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78175_2.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(get_class(Loader::getLoader())); +var_dump(Loader::getCounter()); +?> +OK +--EXPECT-- +string(6) "Loader" +int(0) +OK diff --git a/ext/opcache/tests/bug78376.phpt b/ext/opcache/tests/bug78376.phpt new file mode 100644 index 0000000000..5a8ed8b15a --- /dev/null +++ b/ext/opcache/tests/bug78376.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #78376 (Incorrect preloading of constant static properties) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78376.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(\A::$a); +?> +--EXPECT-- +string(4) "aaaa" diff --git a/ext/opcache/tests/bug78429.phpt b/ext/opcache/tests/bug78429.phpt new file mode 100644 index 0000000000..d2d1e43cdc --- /dev/null +++ b/ext/opcache/tests/bug78429.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #78429 (opcache_compile_file(__FILE__); segfaults) +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--INI-- +opcache.enable_cli=0 +--FILE-- +<?php +var_dump(opcache_compile_file(__FILE__)); +?> +--EXPECTF-- +Notice: Zend OPcache has not been properly started, can't compile file in %s on line %d +bool(false) diff --git a/ext/opcache/tests/bug78937_1.phpt b/ext/opcache/tests/bug78937_1.phpt new file mode 100644 index 0000000000..2745fde72c --- /dev/null +++ b/ext/opcache/tests/bug78937_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #78937.1 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +class Bar { +} +var_dump(foo()); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 +object(class@anonymous)#%d (0) { +} diff --git a/ext/opcache/tests/bug78937_2.phpt b/ext/opcache/tests/bug78937_2.phpt new file mode 100644 index 0000000000..a20c07d231 --- /dev/null +++ b/ext/opcache/tests/bug78937_2.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #78937.2 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +include(__DIR__ . "/preload_bug78937.inc"); +class Bar { +} +var_dump(foo()); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 +object(class@anonymous)#%d (0) { +} diff --git a/ext/opcache/tests/bug78937_3.phpt b/ext/opcache/tests/bug78937_3.phpt new file mode 100644 index 0000000000..16f7b80a40 --- /dev/null +++ b/ext/opcache/tests/bug78937_3.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #78937.3 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +include(__DIR__ . "/preload_bug78937.inc"); +var_dump(foo()); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 + +Fatal error: Uncaught Error: Class 'Bar' not found in %spreload_bug78937.inc:3 +Stack trace: +#0 %sbug78937_3.php(3): foo() +#1 {main} + thrown in %spreload_bug78937.inc on line 3 diff --git a/ext/opcache/tests/bug78937_4.phpt b/ext/opcache/tests/bug78937_4.phpt new file mode 100644 index 0000000000..3652d6de52 --- /dev/null +++ b/ext/opcache/tests/bug78937_4.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #78937.4 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +class Bar { +} +bar(); +var_dump(new Foo); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 +object(Foo)#%d (0) { +} diff --git a/ext/opcache/tests/bug78937_5.phpt b/ext/opcache/tests/bug78937_5.phpt new file mode 100644 index 0000000000..3502699750 --- /dev/null +++ b/ext/opcache/tests/bug78937_5.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #78937.5 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +include(__DIR__ . "/preload_bug78937.inc"); +class Bar { +} +bar(); +var_dump(new Foo); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 +object(Foo)#%d (0) { +} diff --git a/ext/opcache/tests/bug78937_6.phpt b/ext/opcache/tests/bug78937_6.phpt new file mode 100644 index 0000000000..ec1cc2d277 --- /dev/null +++ b/ext/opcache/tests/bug78937_6.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #78937.6 (Preloading unlinkable anonymous class can segfault) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_bug78937.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +include(__DIR__ . "/preload_bug78937.inc"); +bar(); +var_dump(new Foo); +?> +--EXPECTF-- +Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6 + +Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3 + +Fatal error: Uncaught Error: Class 'Bar' not found in %spreload_bug78937.inc:6 +Stack trace: +#0 %sbug78937_6.php(3): bar() +#1 {main} + thrown in %spreload_bug78937.inc on line 6 diff --git a/ext/opcache/tests/bug78961.phpt b/ext/opcache/tests/bug78961.phpt new file mode 100644 index 0000000000..736a747f43 --- /dev/null +++ b/ext/opcache/tests/bug78961.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #78961 (erroneous optimization of re-assigned $GLOBALS) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +$GLOBALS = array(); +$GLOBALS['td'] = array(); +$GLOBALS['td']['nsno'] = 3; +var_dump($GLOBALS['td']['nsno']); +?> +--EXPECT-- +int(3) diff --git a/ext/opcache/tests/bug78986.phpt b/ext/opcache/tests/bug78986.phpt new file mode 100644 index 0000000000..9479460f76 --- /dev/null +++ b/ext/opcache/tests/bug78986.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #78986: Opcache segfaults when inheriting ctor from immutable into mutable class +--FILE-- +<?php + +define('TEST_TEST', 1); + +class TestClass2 { + function __construct() {} +} + +class TestClass extends TestClass2 { + var $test = [ + TEST_TEST => 'test' + ]; +} + +var_dump(new TestClass()); + +?> +--EXPECT-- +object(TestClass)#1 (1) { + ["test"]=> + array(1) { + [1]=> + string(4) "test" + } +} diff --git a/ext/opcache/tests/bug79193.phpt b/ext/opcache/tests/bug79193.phpt new file mode 100644 index 0000000000..c500400363 --- /dev/null +++ b/ext/opcache/tests/bug79193.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #79193: Incorrect type inference for self::$field =& $field +--FILE-- +<?php + +class Test { + public static $foo; + public static function method($bar) { + Test::$foo =& $bar; + var_dump(is_int($bar)); + } +} + +Test::method(1); + +?> +--EXPECT-- +bool(true) diff --git a/ext/opcache/tests/issue0115.phpt b/ext/opcache/tests/issue0115.phpt index 26d99080eb..bc86d0f4fc 100644 --- a/ext/opcache/tests/issue0115.phpt +++ b/ext/opcache/tests/issue0115.phpt @@ -8,6 +8,8 @@ phar.readonly=0 <?php require_once('skipif.inc'); ?> <?php if (!extension_loaded("phar")) die("skip"); ?> <?php if (php_sapi_name() != "cli") die("skip CLI only"); ?> +--CONFLICTS-- +server --FILE-- <?php $stub = '<?php diff --git a/ext/opcache/tests/issue0140.phpt b/ext/opcache/tests/issue0140.phpt index 97fc11b3c7..3ca9b50d3c 100644 --- a/ext/opcache/tests/issue0140.phpt +++ b/ext/opcache/tests/issue0140.phpt @@ -11,7 +11,7 @@ opcache.file_update_protection=0 <?php if (getenv("SKIP_SLOW_TESTS")) die("skip slow tests excluded by request") ?> --FILE-- <?php -define("FILENAME", dirname(__FILE__) . "/issuer0140.inc.php"); +define("FILENAME", __DIR__ . "/issuer0140.inc.php"); file_put_contents(FILENAME, "1\n"); var_dump(is_readable(FILENAME)); diff --git a/ext/opcache/tests/issue0149.phpt b/ext/opcache/tests/issue0149.phpt index ba57623fce..da3b778ef5 100644 --- a/ext/opcache/tests/issue0149.phpt +++ b/ext/opcache/tests/issue0149.phpt @@ -8,6 +8,8 @@ phar.readonly=0 <?php require_once('skipif.inc'); ?> <?php if (!extension_loaded("phar")) die("skip"); ?> <?php if (php_sapi_name() != "cli") die("skip CLI only"); ?> +--CONFLICTS-- +server --FILE-- <?php $stub = "<?php header('Content-Type: text/plain;'); diff --git a/ext/opcache/tests/log_verbosity_bug.phpt b/ext/opcache/tests/log_verbosity_bug.phpt index e23bb8758f..a2c2f4f30a 100644 --- a/ext/opcache/tests/log_verbosity_bug.phpt +++ b/ext/opcache/tests/log_verbosity_bug.phpt @@ -12,7 +12,10 @@ opcache.file_cache_fallback=0 opcache.memory_consumption=999999999 opcache.log_verbosity_level=-1 --SKIPIF-- -<?php require_once('skipif.inc'); ?> +<?php +require_once('skipif.inc'); +if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); +?> --FILE-- <?php var_dump("Script should fail"); diff --git a/ext/opcache/tests/opt/dce_001.phpt b/ext/opcache/tests/opt/dce_001.phpt index 475dc8191c..1afc3eba06 100644 --- a/ext/opcache/tests/opt/dce_001.phpt +++ b/ext/opcache/tests/opt/dce_001.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_002.phpt b/ext/opcache/tests/opt/dce_002.phpt index 0d71a7d809..fb1c070b35 100644 --- a/ext/opcache/tests/opt/dce_002.phpt +++ b/ext/opcache/tests/opt/dce_002.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_003.phpt b/ext/opcache/tests/opt/dce_003.phpt index 017f2c4040..0de39c042c 100644 --- a/ext/opcache/tests/opt/dce_003.phpt +++ b/ext/opcache/tests/opt/dce_003.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_004.phpt b/ext/opcache/tests/opt/dce_004.phpt index 795c938c2c..90bfc52d97 100644 --- a/ext/opcache/tests/opt/dce_004.phpt +++ b/ext/opcache/tests/opt/dce_004.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_005.phpt b/ext/opcache/tests/opt/dce_005.phpt index b7f5437a20..3888ccd53f 100644 --- a/ext/opcache/tests/opt/dce_005.phpt +++ b/ext/opcache/tests/opt/dce_005.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_006.phpt b/ext/opcache/tests/opt/dce_006.phpt index debac5e731..c84805247a 100644 --- a/ext/opcache/tests/opt/dce_006.phpt +++ b/ext/opcache/tests/opt/dce_006.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- @@ -32,6 +33,8 @@ L3 (6): CV1($a) = QM_ASSIGN V2 L4 (7): ASSIGN_OBJ CV1($a) string("foo") L5 (7): OP_DATA CV0($x) L6 (8): RETURN null +LIVE RANGES: + 2: L2 - L3 (new) A::__destruct: ; (lines=1, args=0, vars=0, tmps=0) ; (after optimizer) diff --git a/ext/opcache/tests/opt/dce_007.phpt b/ext/opcache/tests/opt/dce_007.phpt index ce601705c5..a3ae04edd9 100644 --- a/ext/opcache/tests/opt/dce_007.phpt +++ b/ext/opcache/tests/opt/dce_007.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/dce_008.phpt b/ext/opcache/tests/opt/dce_008.phpt index f1a29d4d8e..3b697ee203 100644 --- a/ext/opcache/tests/opt/dce_008.phpt +++ b/ext/opcache/tests/opt/dce_008.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/prop_types.phpt b/ext/opcache/tests/opt/prop_types.phpt new file mode 100644 index 0000000000..33cb5fa033 --- /dev/null +++ b/ext/opcache/tests/opt/prop_types.phpt @@ -0,0 +1,116 @@ +--TEST-- +Property types in inference +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x200000 +opcache.preload= +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +class Test { + public bool $public; + protected int $protected; + private float $private; + + public function inTest() { + var_dump($this->public, $this->protected, $this->private); + } + + public function inTestWithTest2(Test2 $test2) { + var_dump($test2->public, $test2->protected, $test2->private); + } +} + +class Test2 extends Test { + private array $private; + + public function inTest2() { + var_dump($this->public, $this->protected, $this->private); + } +} + +function noScope(Test $test) { + var_dump($test->public, $test->protected, $test->private); +} + +?> +--EXPECTF-- +$_main: ; (lines=1, args=0, vars=0, tmps=0, ssa_vars=0, no_loops) + ; (before dfa pass) + ; %s + ; return [long] RANGE[1..1] +BB0: start exit lines=[0-0] + ; level=0 + RETURN int(1) + +noScope: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] + ; #0.CV0($test) NOVAL [undef] +BB0: start exit lines=[0-9] + ; level=0 + #1.CV0($test) [object (instanceof Test)] = RECV 1 + INIT_FCALL 3 %d string("var_dump") + #2.T1 [bool] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("public") + SEND_VAL #2.T1 [bool] 1 + #3.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("protected") + SEND_VAL #3.T1 [any] 2 + #4.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("private") + SEND_VAL #4.T1 [any] 3 + DO_ICALL + RETURN null + +Test::inTest: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] +BB0: start exit lines=[0-8] + ; level=0 + INIT_FCALL 3 %d string("var_dump") + #0.T0 [bool] = FETCH_OBJ_R THIS string("public") + SEND_VAL #0.T0 [bool] 1 + #1.T0 [long] = FETCH_OBJ_R THIS string("protected") + SEND_VAL #1.T0 [long] 2 + #2.T0 [double] = FETCH_OBJ_R THIS string("private") + SEND_VAL #2.T0 [double] 3 + DO_ICALL + RETURN null + +Test::inTestWithTest2: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] + ; #0.CV0($test2) NOVAL [undef] +BB0: start exit lines=[0-9] + ; level=0 + #1.CV0($test2) [object (instanceof Test2)] = RECV 1 + INIT_FCALL 3 %d string("var_dump") + #2.T1 [bool] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("public") + SEND_VAL #2.T1 [bool] 1 + #3.T1 [long] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("protected") + SEND_VAL #3.T1 [long] 2 + #4.T1 [double] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("private") + SEND_VAL #4.T1 [double] 3 + DO_ICALL + RETURN null + +Test2::inTest2: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] +BB0: start exit lines=[0-8] + ; level=0 + INIT_FCALL 3 %d string("var_dump") + #0.T0 [bool] = FETCH_OBJ_R THIS string("public") + SEND_VAL #0.T0 [bool] 1 + #1.T0 [long] = FETCH_OBJ_R THIS string("protected") + SEND_VAL #1.T0 [long] 2 + #2.T0 [array of [any, ref]] = FETCH_OBJ_R THIS string("private") + SEND_VAL #2.T0 [array of [any, ref]] 3 + DO_ICALL + RETURN null diff --git a/ext/opcache/tests/opt/sccp_001.phpt b/ext/opcache/tests/opt/sccp_001.phpt index 9b2421a632..1f67f985e3 100644 --- a/ext/opcache/tests/opt/sccp_001.phpt +++ b/ext/opcache/tests/opt/sccp_001.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_002.phpt b/ext/opcache/tests/opt/sccp_002.phpt index 748358c894..0fd10f2ac5 100644 --- a/ext/opcache/tests/opt/sccp_002.phpt +++ b/ext/opcache/tests/opt/sccp_002.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_003.phpt b/ext/opcache/tests/opt/sccp_003.phpt index 555c8efb75..282a5788e6 100644 --- a/ext/opcache/tests/opt/sccp_003.phpt +++ b/ext/opcache/tests/opt/sccp_003.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_004.phpt b/ext/opcache/tests/opt/sccp_004.phpt index 05987f6660..d82212e0ef 100644 --- a/ext/opcache/tests/opt/sccp_004.phpt +++ b/ext/opcache/tests/opt/sccp_004.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_005.phpt b/ext/opcache/tests/opt/sccp_005.phpt index aeef1beb96..7fbb062922 100644 --- a/ext/opcache/tests/opt/sccp_005.phpt +++ b/ext/opcache/tests/opt/sccp_005.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_006.phpt b/ext/opcache/tests/opt/sccp_006.phpt index 88b4cd7717..60fa5f2f01 100644 --- a/ext/opcache/tests/opt/sccp_006.phpt +++ b/ext/opcache/tests/opt/sccp_006.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_007.phpt b/ext/opcache/tests/opt/sccp_007.phpt index 027f1c7281..82feb04e22 100644 --- a/ext/opcache/tests/opt/sccp_007.phpt +++ b/ext/opcache/tests/opt/sccp_007.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_008.phpt b/ext/opcache/tests/opt/sccp_008.phpt index 5b886d02ba..d652964a66 100644 --- a/ext/opcache/tests/opt/sccp_008.phpt +++ b/ext/opcache/tests/opt/sccp_008.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_009.phpt b/ext/opcache/tests/opt/sccp_009.phpt index 2f32cb8276..1b049810c1 100644 --- a/ext/opcache/tests/opt/sccp_009.phpt +++ b/ext/opcache/tests/opt/sccp_009.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_010.phpt b/ext/opcache/tests/opt/sccp_010.phpt index 298c65efd7..e88bf579f7 100644 --- a/ext/opcache/tests/opt/sccp_010.phpt +++ b/ext/opcache/tests/opt/sccp_010.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_011.phpt b/ext/opcache/tests/opt/sccp_011.phpt index 2d817310d5..281e3dca2e 100644 --- a/ext/opcache/tests/opt/sccp_011.phpt +++ b/ext/opcache/tests/opt/sccp_011.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_012.phpt b/ext/opcache/tests/opt/sccp_012.phpt index 3380331922..5d2f3e9a01 100644 --- a/ext/opcache/tests/opt/sccp_012.phpt +++ b/ext/opcache/tests/opt/sccp_012.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_016.phpt b/ext/opcache/tests/opt/sccp_016.phpt index 0af0d64ab2..bceb33e2a0 100644 --- a/ext/opcache/tests/opt/sccp_016.phpt +++ b/ext/opcache/tests/opt/sccp_016.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_017.phpt b/ext/opcache/tests/opt/sccp_017.phpt index eb2694769c..c2bc6f35fb 100644 --- a/ext/opcache/tests/opt/sccp_017.phpt +++ b/ext/opcache/tests/opt/sccp_017.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_019.phpt b/ext/opcache/tests/opt/sccp_019.phpt index eb2a431b55..5a32bd2205 100644 --- a/ext/opcache/tests/opt/sccp_019.phpt +++ b/ext/opcache/tests/opt/sccp_019.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_022.phpt b/ext/opcache/tests/opt/sccp_022.phpt index 2b79707ea5..03a8d565f2 100644 --- a/ext/opcache/tests/opt/sccp_022.phpt +++ b/ext/opcache/tests/opt/sccp_022.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_024.phpt b/ext/opcache/tests/opt/sccp_024.phpt index f0b34f077b..e6e2e311ef 100644 --- a/ext/opcache/tests/opt/sccp_024.phpt +++ b/ext/opcache/tests/opt/sccp_024.phpt @@ -5,6 +5,7 @@ opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.opt_debug_level=0x20000 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/tests/opt/sccp_026.phpt b/ext/opcache/tests/opt/sccp_026.phpt new file mode 100644 index 0000000000..132c9ddef3 --- /dev/null +++ b/ext/opcache/tests/opt/sccp_026.phpt @@ -0,0 +1,38 @@ +--TEST-- +SCCP 026: Elimination of dead code due to conflicting type checks +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x20000 +opcache.preload= +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +function test($var) { + if (!is_string($var) || (is_object($var) && !method_exists($var, '__toString'))) { + return; + } + + var_dump($username); +} +?> +--EXPECTF-- +$_main: ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +L0 (10): RETURN int(1) + +test: ; (lines=9, args=1, vars=2, tmps=1) + ; (after optimizer) + ; %s:2-8 +L0 (2): CV0($var) = RECV 1 +L1 (3): T2 = TYPE_CHECK (string) CV0($var) +L2 (3): JMPZ T2 L4 +L3 (3): JMP L5 +L4 (4): RETURN null +L5 (7): INIT_FCALL 1 %d string("var_dump") +L6 (7): SEND_VAR CV1($username) 1 +L7 (7): DO_ICALL +L8 (8): RETURN null diff --git a/ext/opcache/tests/opt/sccp_027.phpt b/ext/opcache/tests/opt/sccp_027.phpt new file mode 100644 index 0000000000..38189592b0 --- /dev/null +++ b/ext/opcache/tests/opt/sccp_027.phpt @@ -0,0 +1,27 @@ +--TEST-- +SCCP 027: Support for ASSIGN_OBJ_REF +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload= +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +class Foo { + protected $arr; + public function init($a) { + $this->arr =& $a; + if (isset($a[0])) { + return 1; + } else { + return 2; + } + } +} +$x = new Foo(); +var_dump($x->init([1])); +?> +--EXPECT-- +int(1) diff --git a/ext/opcache/tests/opt/sccp_028.phpt b/ext/opcache/tests/opt/sccp_028.phpt new file mode 100644 index 0000000000..a916b3af14 --- /dev/null +++ b/ext/opcache/tests/opt/sccp_028.phpt @@ -0,0 +1,25 @@ +--TEST-- +SCCP 028: Don't propagate typed properties +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload= +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +class Foo { + public int $bar = 1; +} +function test() { + $foo = new Foo(); + $foo->bar = "10"; + var_dump($foo->bar); +} +test(); + +?> +--EXPECT-- +int(10) diff --git a/ext/opcache/tests/opt/sccp_029.phpt b/ext/opcache/tests/opt/sccp_029.phpt new file mode 100644 index 0000000000..3a16477711 --- /dev/null +++ b/ext/opcache/tests/opt/sccp_029.phpt @@ -0,0 +1,27 @@ +--TEST-- +SCCP 029: Don't propagate assignments to references to typed properties +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload= +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +class Test { + public string $x = "x"; +} +function test() { + $test = new Test(); + $ref = "y"; + $test->x =& $ref; + $ref = 42; + var_dump($ref); +} +test(); + +?> +--EXPECT-- +string(2) "42" diff --git a/ext/opcache/tests/optimize_static_001.phpt b/ext/opcache/tests/optimize_static_001.phpt new file mode 100644 index 0000000000..d4e2c58062 --- /dev/null +++ b/ext/opcache/tests/optimize_static_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +Keep BIND_STATIC when initial value refer to unresolved constants +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +function foo() { + static $a = UNDEFINED_CONST; +} +foo(); +?> +OK +--EXPECTF-- +Warning: Use of undefined constant UNDEFINED_CONST - assumed 'UNDEFINED_CONST' (this will throw an Error in a future version of PHP) in %s on line %d +OK
\ No newline at end of file diff --git a/ext/opcache/tests/phi_remove_001.phpt b/ext/opcache/tests/phi_remove_001.phpt index 4be8dcfd6a..3a76a9da5c 100644 --- a/ext/opcache/tests/phi_remove_001.phpt +++ b/ext/opcache/tests/phi_remove_001.phpt @@ -31,7 +31,7 @@ function getOnlyMPEGaudioInfoBruteForce($info) { if ($MPEGaudioHeaderLengthCache[$head4] > 4) { $WhereWeWere = mftell(); $next4 = test(4); - if ($next4{0} == "\xFF") { + if ($next4[0] == "\xFF") { if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { $MPEGaudioHeaderDecodeCache[$next4] = MPEGaudioHeaderDecode($next4); } diff --git a/ext/opcache/tests/php_cli_server.inc b/ext/opcache/tests/php_cli_server.inc index 70ef14e585..e32cf9f97f 100644 --- a/ext/opcache/tests/php_cli_server.inc +++ b/ext/opcache/tests/php_cli_server.inc @@ -7,25 +7,17 @@ function php_cli_server_start($ini = "") { $php_executable = getenv('TEST_PHP_EXECUTABLE'); $doc_root = __DIR__; - if (substr(PHP_OS, 0, 3) == 'WIN') { - $descriptorspec = array( - 0 => STDIN, - 1 => STDOUT, - 2 => array("pipe", "w"), - ); - - $cmd = "{$php_executable} -t {$doc_root} $ini -S " . PHP_CLI_SERVER_ADDRESS; - $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true, "suppress_errors" => true)); - } else { - $descriptorspec = array( - 0 => STDIN, - 1 => STDOUT, - 2 => STDERR, - ); - - $cmd = "exec {$php_executable} -t {$doc_root} $ini -S " . PHP_CLI_SERVER_ADDRESS . " 2>/dev/null"; - $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root); - } + $ini_array = preg_split('/\s+/', trim($ini)); + $ini_array = array_map(function($arg) { + return trim($arg, '\'"'); + }, $ini_array); + $cmd = [$php_executable, '-t', $doc_root, '-n', ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS]; + $descriptorspec = array( + 0 => STDIN, + 1 => STDOUT, + 2 => array("null"), + ); + $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root, null, array("suppress_errors" => true)); // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.' // it might not be listening yet...need to wait until fsockopen() call returns diff --git a/ext/opcache/tests/preload.inc b/ext/opcache/tests/preload.inc new file mode 100644 index 0000000000..10de9390d0 --- /dev/null +++ b/ext/opcache/tests/preload.inc @@ -0,0 +1,58 @@ +<?php +function f1() { +} + +if (isset($rt)) { + function f2() { + } +} + +interface a { + function foo(); + function bar(); +} +interface b { + function foo(); +} + +abstract class c { + function bar() { } +} + +class x extends c implements a, b { + function foo() { } +} + +trait T1 { + static function foo() { + var_dump(__METHOD__); + } +} +trait T2 { + use T1; + static function bar() { + var_dump(__METHOD__); + } +} +class Y { + use T2; +} + +class Z { + public $foo; + public a $bar; +} + +class Z2 extends Z {} + +function get_anon() { + return new class {}; +} + +if (!isset($rt)) { + eval("class Foo {}"); + + class Bar extends Foo {} + + eval("function f3() {} "); +} diff --git a/ext/opcache/tests/preload_001.phpt b/ext/opcache/tests/preload_001.phpt new file mode 100644 index 0000000000..56ed196e99 --- /dev/null +++ b/ext/opcache/tests/preload_001.phpt @@ -0,0 +1,27 @@ +--TEST-- +Preloading basic test +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(function_exists("f1")); +var_dump(function_exists("f2")); + +$rt = true; +include(__DIR__ . "/preload.inc"); +var_dump(function_exists("f2")); +?> +OK +--EXPECTF-- +bool(true) +bool(false) +bool(true) +OK diff --git a/ext/opcache/tests/preload_002.phpt b/ext/opcache/tests/preload_002.phpt new file mode 100644 index 0000000000..0115ff0564 --- /dev/null +++ b/ext/opcache/tests/preload_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +Preloading prototypes +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump((new ReflectionMethod('x', 'foo'))->getPrototype()->class); +var_dump((new ReflectionMethod('x', 'bar'))->getPrototype()->class); +?> +OK +--EXPECT-- +string(1) "b" +string(1) "a" +OK diff --git a/ext/opcache/tests/preload_003.phpt b/ext/opcache/tests/preload_003.phpt new file mode 100644 index 0000000000..9ecec31aae --- /dev/null +++ b/ext/opcache/tests/preload_003.phpt @@ -0,0 +1,22 @@ +--TEST-- +Preloading classes linked with traits +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +Y::foo(); +Y::bar(); +?> +OK +--EXPECT-- +string(7) "T1::foo" +string(7) "T2::bar" +OK diff --git a/ext/opcache/tests/preload_004.phpt b/ext/opcache/tests/preload_004.phpt new file mode 100644 index 0000000000..be83c45369 --- /dev/null +++ b/ext/opcache/tests/preload_004.phpt @@ -0,0 +1,20 @@ +--TEST-- +Preloading class with undefined class constant access +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_undef_const.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(class_exists('Foo')); +?> +--EXPECTF-- +Fatal error: Undefined class constant 'self::DOES_NOT_EXIST' in Unknown on line 0 + +Fatal error: Failed to resolve initializers of class Foo during preloading in Unknown on line 0 diff --git a/ext/opcache/tests/preload_005.phpt b/ext/opcache/tests/preload_005.phpt new file mode 100644 index 0000000000..67e44610bc --- /dev/null +++ b/ext/opcache/tests/preload_005.phpt @@ -0,0 +1,19 @@ +--TEST-- +Handling of auto globals during preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_globals.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +$x = 123; +var_dump(get_x()); +?> +--EXPECT-- +int(123) diff --git a/ext/opcache/tests/preload_006.phpt b/ext/opcache/tests/preload_006.phpt new file mode 100644 index 0000000000..76d9629774 --- /dev/null +++ b/ext/opcache/tests/preload_006.phpt @@ -0,0 +1,19 @@ +--TEST-- +Handling of errors during linking +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_inheritance_error_ind.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); +?> +--FILE-- +<?php +echo "Foobar\n"; +?> +--EXPECTF-- +Fatal error: Declaration of B::foo($bar) must be compatible with A::foo() in %spreload_inheritance_error.inc on line 8 diff --git a/ext/opcache/tests/preload_007.phpt b/ext/opcache/tests/preload_007.phpt new file mode 100644 index 0000000000..79c2a6737a --- /dev/null +++ b/ext/opcache/tests/preload_007.phpt @@ -0,0 +1,18 @@ +--TEST-- +Handling of includes that were not executed +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_include.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +echo "Foobar"; +?> +--EXPECTF-- +Foobar diff --git a/ext/opcache/tests/preload_008.phpt b/ext/opcache/tests/preload_008.phpt new file mode 100644 index 0000000000..8d7b42cab5 --- /dev/null +++ b/ext/opcache/tests/preload_008.phpt @@ -0,0 +1,19 @@ +--TEST-- +Preloading of anonymous class +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(get_anon()); +?> +--EXPECT-- +object(class@anonymous)#1 (0) { +} diff --git a/ext/opcache/tests/preload_009.phpt b/ext/opcache/tests/preload_009.phpt new file mode 100644 index 0000000000..f3a28675e3 --- /dev/null +++ b/ext/opcache/tests/preload_009.phpt @@ -0,0 +1,21 @@ +--TEST-- +Preloading class using trait with undefined class constant access +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_undef_const_2.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(trait_exists('T')); +var_dump(class_exists('Foo')); +?> +--EXPECTF-- +Warning: Use of undefined constant UNDEF - assumed 'UNDEF' (this will throw an Error in a future version of PHP) in Unknown on line 0 +bool(true) +bool(true) diff --git a/ext/opcache/tests/preload_010.phpt b/ext/opcache/tests/preload_010.phpt new file mode 100644 index 0000000000..4e5121f23c --- /dev/null +++ b/ext/opcache/tests/preload_010.phpt @@ -0,0 +1,18 @@ +--TEST-- +Initializer of overwritten property should be resolved against the correct class +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_overwritten_prop_init.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump((new Bar)->prop); +?> +--EXPECT-- +int(42) diff --git a/ext/opcache/tests/preload_011.phpt b/ext/opcache/tests/preload_011.phpt new file mode 100644 index 0000000000..59be0493b4 --- /dev/null +++ b/ext/opcache/tests/preload_011.phpt @@ -0,0 +1,33 @@ +--TEST-- +Argument/return types must be available for preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_variance_ind.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +interface K {} +interface L extends K {} +require __DIR__ . '/preload_variance.inc'; + +$a = new A; +$b = new B; +$d = new D; +$f = new F; +$g = new G; + +?> +===DONE=== +--EXPECTF-- +Warning: Can't preload unlinked class H: Unknown type dependencies in %s on line %d + +Warning: Can't preload unlinked class B: Unknown type dependencies in %s on line %d + +Warning: Can't preload unlinked class A: Unknown type dependencies in %s on line %d +===DONE=== diff --git a/ext/opcache/tests/preload_012.phpt b/ext/opcache/tests/preload_012.phpt new file mode 100644 index 0000000000..bd3fd81bbd --- /dev/null +++ b/ext/opcache/tests/preload_012.phpt @@ -0,0 +1,17 @@ +--TEST-- +No autoloading during constant resolution +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_const_autoload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +===DONE=== +--EXPECTF-- +Warning: Can't preload class Test with unresolved initializer for constant C in %s on line %d +===DONE=== diff --git a/ext/opcache/tests/preload_013.phpt b/ext/opcache/tests/preload_013.phpt new file mode 100644 index 0000000000..925a1fc5ef --- /dev/null +++ b/ext/opcache/tests/preload_013.phpt @@ -0,0 +1,21 @@ +--TEST-- +Nested function definition +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_nested_function.inc +opcache.interned_strings_buffer=0 +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +test(); +test2(); +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/opcache/tests/preload_bug78014.inc b/ext/opcache/tests/preload_bug78014.inc new file mode 100644 index 0000000000..84cc1d90c8 --- /dev/null +++ b/ext/opcache/tests/preload_bug78014.inc @@ -0,0 +1,2 @@ +<?php +opcache_compile_file(__DIR__ . "/bug78014.inc"); diff --git a/ext/opcache/tests/preload_bug78175.inc b/ext/opcache/tests/preload_bug78175.inc new file mode 100644 index 0000000000..bf3f33890c --- /dev/null +++ b/ext/opcache/tests/preload_bug78175.inc @@ -0,0 +1,4 @@ +<?php +register_shutdown_function(function() { + echo "Shutdown\n"; +}); diff --git a/ext/opcache/tests/preload_bug78175_2.inc b/ext/opcache/tests/preload_bug78175_2.inc new file mode 100644 index 0000000000..a960e1b08c --- /dev/null +++ b/ext/opcache/tests/preload_bug78175_2.inc @@ -0,0 +1,23 @@ +<?php +class Loader { + static private $loader; + + static function getLoader() { + if (null !== self::$loader) { + return self::$loader; + } + return self::$loader = new Loader(); + } + + static function getCounter() { + static $counter = 0; + return $counter++; + } +} + +class ExtLoader extends Loader { +} + +Loader::getLoader(); +Loader::getCounter(); +Loader::getCounter(); diff --git a/ext/opcache/tests/preload_bug78376.inc b/ext/opcache/tests/preload_bug78376.inc new file mode 100644 index 0000000000..c482e0a821 --- /dev/null +++ b/ext/opcache/tests/preload_bug78376.inc @@ -0,0 +1,6 @@ +<?php +const CNST = 'aaaa'; +class A { + public static $a = CNST; +} +$a = \A::$a; diff --git a/ext/opcache/tests/preload_bug78937.inc b/ext/opcache/tests/preload_bug78937.inc new file mode 100644 index 0000000000..7da2dc3227 --- /dev/null +++ b/ext/opcache/tests/preload_bug78937.inc @@ -0,0 +1,8 @@ +<?php +function foo() { + return new class extends Bar {}; +} +function bar() { + class Foo extends Bar { + } +} diff --git a/ext/opcache/tests/preload_bug79114.phpt b/ext/opcache/tests/preload_bug79114.phpt new file mode 100644 index 0000000000..345a6bd83a --- /dev/null +++ b/ext/opcache/tests/preload_bug79114.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #79114 (Eval class during preload causes class to be only half available) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(class_exists(Foo::class)); +var_dump(class_exists(Bar::class)); +new Bar(); +var_dump(class_parents('Bar')); +new Foo(); +f3(); +?> +--EXPECTF-- +bool(true) +bool(true) +array(1) { + ["Foo"]=> + string(3) "Foo" +} diff --git a/ext/opcache/tests/preload_class_alias.inc b/ext/opcache/tests/preload_class_alias.inc new file mode 100644 index 0000000000..2aaa5adafc --- /dev/null +++ b/ext/opcache/tests/preload_class_alias.inc @@ -0,0 +1,3 @@ +<?php +class A {} +class_alias(A::class, 'B'); diff --git a/ext/opcache/tests/preload_class_alias.phpt b/ext/opcache/tests/preload_class_alias.phpt new file mode 100644 index 0000000000..3c615c409a --- /dev/null +++ b/ext/opcache/tests/preload_class_alias.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #78918: Class alias during preloading causes assertion failure +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_class_alias.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(class_exists('A')); +var_dump(class_exists('B')); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/ext/opcache/tests/preload_class_alias_2.inc b/ext/opcache/tests/preload_class_alias_2.inc new file mode 100644 index 0000000000..3d0a70d882 --- /dev/null +++ b/ext/opcache/tests/preload_class_alias_2.inc @@ -0,0 +1,4 @@ +<?php +interface I {} +class B implements I {} +class_alias('B', 'C'); diff --git a/ext/opcache/tests/preload_class_alias_2.phpt b/ext/opcache/tests/preload_class_alias_2.phpt new file mode 100644 index 0000000000..82167afa9b --- /dev/null +++ b/ext/opcache/tests/preload_class_alias_2.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #78918.2: Class alias during preloading causes assertion failure +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_class_alias_2.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(class_exists('B')); +var_dump(class_exists('C')); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/ext/opcache/tests/preload_const_autoload.inc b/ext/opcache/tests/preload_const_autoload.inc new file mode 100644 index 0000000000..b9634de49a --- /dev/null +++ b/ext/opcache/tests/preload_const_autoload.inc @@ -0,0 +1,7 @@ +<?php + +spl_autoload_register(function($class) { + var_dump($class); + new Abc; +}); +opcache_compile_file('preload_const_autoload_2.inc'); diff --git a/ext/opcache/tests/preload_const_autoload_2.inc b/ext/opcache/tests/preload_const_autoload_2.inc new file mode 100644 index 0000000000..9367902adf --- /dev/null +++ b/ext/opcache/tests/preload_const_autoload_2.inc @@ -0,0 +1,5 @@ +<?php + +class Test { + const C = Foo::BAR; +} diff --git a/ext/opcache/tests/preload_globals.inc b/ext/opcache/tests/preload_globals.inc new file mode 100644 index 0000000000..213dc60fab --- /dev/null +++ b/ext/opcache/tests/preload_globals.inc @@ -0,0 +1,5 @@ +<?php + +function get_x() { + return $GLOBALS["x"]; +} diff --git a/ext/opcache/tests/preload_include.inc b/ext/opcache/tests/preload_include.inc new file mode 100644 index 0000000000..95369e7b6a --- /dev/null +++ b/ext/opcache/tests/preload_include.inc @@ -0,0 +1,7 @@ +<?php + +$a = 1; +$b = 2; +if ($a == $b) { + include __DIR__ . "/preload_include_dummy.inc"; +} diff --git a/ext/opcache/tests/preload_include_dummy.inc b/ext/opcache/tests/preload_include_dummy.inc new file mode 100644 index 0000000000..8fba09747a --- /dev/null +++ b/ext/opcache/tests/preload_include_dummy.inc @@ -0,0 +1,3 @@ +<?php + +echo "Dummy\n"; diff --git a/ext/opcache/tests/preload_inheritance_error.inc b/ext/opcache/tests/preload_inheritance_error.inc new file mode 100644 index 0000000000..b0b8274060 --- /dev/null +++ b/ext/opcache/tests/preload_inheritance_error.inc @@ -0,0 +1,9 @@ +<?php + +interface A { + public function foo(); +} + +class B implements A { + public function foo($bar) {} +} diff --git a/ext/opcache/tests/preload_inheritance_error_ind.inc b/ext/opcache/tests/preload_inheritance_error_ind.inc new file mode 100644 index 0000000000..f8de05230e --- /dev/null +++ b/ext/opcache/tests/preload_inheritance_error_ind.inc @@ -0,0 +1,2 @@ +<?php +opcache_compile_file(__DIR__ . '/preload_inheritance_error.inc'); diff --git a/ext/opcache/tests/preload_loadable_classes_1.inc b/ext/opcache/tests/preload_loadable_classes_1.inc new file mode 100644 index 0000000000..b2bdabae1b --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_1.inc @@ -0,0 +1,18 @@ +<?php + +spl_autoload_register(function($class) { + if ($class == 'Bar') { + class Bar { + const BAZ = 42; + + public self $x; + public Foo $y; + } + } else if ($class == 'Foo') { + class Foo {} + } +}); + +class Test { + const FOO = Bar::BAZ; +} diff --git a/ext/opcache/tests/preload_loadable_classes_1.phpt b/ext/opcache/tests/preload_loadable_classes_1.phpt new file mode 100644 index 0000000000..c89633343c --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_1.phpt @@ -0,0 +1,22 @@ +--TEST-- +Preloading: Loadable class checking (1) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_loadable_classes_1.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump(class_exists('Test')); +var_dump(class_exists('Bar')); +var_dump(class_exists('Foo')); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/opcache/tests/preload_loadable_classes_2.inc b/ext/opcache/tests/preload_loadable_classes_2.inc new file mode 100644 index 0000000000..d21384dcd4 --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_2.inc @@ -0,0 +1,6 @@ +<?php + +class Test { + const X = UNDEF; + const Y = Foo::UNDEF; +} diff --git a/ext/opcache/tests/preload_loadable_classes_2.phpt b/ext/opcache/tests/preload_loadable_classes_2.phpt new file mode 100644 index 0000000000..d561837bd0 --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_2.phpt @@ -0,0 +1,20 @@ +--TEST-- +Preloading: Loadable class checking (2) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_loadable_classes_2.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +Unreachable +--EXPECTF-- +Warning: Use of undefined constant UNDEF - assumed 'UNDEF' (this will throw an Error in a future version of PHP) in Unknown on line 0 + +Fatal error: Class 'Foo' not found in Unknown on line 0 + +Fatal error: Failed to resolve initializers of class Test during preloading in Unknown on line 0 diff --git a/ext/opcache/tests/preload_loadable_classes_3.inc b/ext/opcache/tests/preload_loadable_classes_3.inc new file mode 100644 index 0000000000..d5c550f8c4 --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_3.inc @@ -0,0 +1,5 @@ +<?php + +class Test { + protected Foo $prop; +} diff --git a/ext/opcache/tests/preload_loadable_classes_3.phpt b/ext/opcache/tests/preload_loadable_classes_3.phpt new file mode 100644 index 0000000000..78efb16804 --- /dev/null +++ b/ext/opcache/tests/preload_loadable_classes_3.phpt @@ -0,0 +1,16 @@ +--TEST-- +Preloading: Loadable class checking (3) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_loadable_classes_3.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +Unreachable +--EXPECTF-- +Fatal error: Failed to load class Foo used by typed property Test::$prop during preloading in Unknown on line 0 diff --git a/ext/opcache/tests/preload_nested_function.inc b/ext/opcache/tests/preload_nested_function.inc new file mode 100644 index 0000000000..534e81348d --- /dev/null +++ b/ext/opcache/tests/preload_nested_function.inc @@ -0,0 +1,7 @@ +<?php + +function test() +{ + function test2() { } +} + diff --git a/ext/opcache/tests/preload_overwritten_prop_init.inc b/ext/opcache/tests/preload_overwritten_prop_init.inc new file mode 100644 index 0000000000..9d2c602075 --- /dev/null +++ b/ext/opcache/tests/preload_overwritten_prop_init.inc @@ -0,0 +1,10 @@ +<?php + +class Foo { + public $prop; +} + +class Bar extends Foo { + public $prop = self::FOOBAR; + const FOOBAR = 42; +} diff --git a/ext/opcache/tests/preload_static_var_inheritance.inc b/ext/opcache/tests/preload_static_var_inheritance.inc new file mode 100644 index 0000000000..56ed65c2c0 --- /dev/null +++ b/ext/opcache/tests/preload_static_var_inheritance.inc @@ -0,0 +1,9 @@ +<?php + +class A { + public function test() { + static $foo; + } +} + +class B extends A {} diff --git a/ext/opcache/tests/preload_static_var_inheritance.phpt b/ext/opcache/tests/preload_static_var_inheritance.phpt new file mode 100644 index 0000000000..dd5e4308a9 --- /dev/null +++ b/ext/opcache/tests/preload_static_var_inheritance.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #79548: Preloading segfault with inherited method using static variable +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_static_var_inheritance.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +var_dump((new B)->test()); +?> +--EXPECT-- +NULL diff --git a/ext/opcache/tests/preload_trait_static.inc b/ext/opcache/tests/preload_trait_static.inc new file mode 100644 index 0000000000..a2acff2ddb --- /dev/null +++ b/ext/opcache/tests/preload_trait_static.inc @@ -0,0 +1,12 @@ +<?php + +trait Foo { + public function test() { + static $bar; + var_dump($bar); + } +} + +class Bar { + use Foo; +} diff --git a/ext/opcache/tests/preload_trait_static.phpt b/ext/opcache/tests/preload_trait_static.phpt new file mode 100644 index 0000000000..c767ad6a9b --- /dev/null +++ b/ext/opcache/tests/preload_trait_static.phpt @@ -0,0 +1,19 @@ +--TEST-- +Preload trait with static variables in method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_trait_static.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +<?php +$bar = new Bar; +$bar->test(); +?> +--EXPECT-- +NULL diff --git a/ext/opcache/tests/preload_undef_const.inc b/ext/opcache/tests/preload_undef_const.inc new file mode 100644 index 0000000000..8d199e0e83 --- /dev/null +++ b/ext/opcache/tests/preload_undef_const.inc @@ -0,0 +1,4 @@ +<?php +class Foo { + const A = self::DOES_NOT_EXIST; +} diff --git a/ext/opcache/tests/preload_undef_const_2.inc b/ext/opcache/tests/preload_undef_const_2.inc new file mode 100644 index 0000000000..8c1c19c49b --- /dev/null +++ b/ext/opcache/tests/preload_undef_const_2.inc @@ -0,0 +1,11 @@ +<?php +trait T { + public function test() { + return 123; + } +} + +class Foo { + const C = UNDEF; + use T; +} diff --git a/ext/opcache/tests/preload_unresolved_prop_type.inc b/ext/opcache/tests/preload_unresolved_prop_type.inc new file mode 100644 index 0000000000..05f4ee06a3 --- /dev/null +++ b/ext/opcache/tests/preload_unresolved_prop_type.inc @@ -0,0 +1,2 @@ +<?php +opcache_compile_file(__DIR__ . "/preload_unresolved_prop_type_2.inc"); diff --git a/ext/opcache/tests/preload_unresolved_prop_type.phpt b/ext/opcache/tests/preload_unresolved_prop_type.phpt new file mode 100644 index 0000000000..3e2accd19e --- /dev/null +++ b/ext/opcache/tests/preload_unresolved_prop_type.phpt @@ -0,0 +1,17 @@ +--TEST-- +Preload: Unresolved property type +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_unresolved_prop_type.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); +?> +--FILE-- +===DONE=== +--EXPECTF-- +Warning: Can't preload class Test with unresolved property types in %s on line %d +===DONE=== diff --git a/ext/opcache/tests/preload_unresolved_prop_type_2.inc b/ext/opcache/tests/preload_unresolved_prop_type_2.inc new file mode 100644 index 0000000000..c5557d1117 --- /dev/null +++ b/ext/opcache/tests/preload_unresolved_prop_type_2.inc @@ -0,0 +1,5 @@ +<?php + +class Test { + public Unknown $prop; +} diff --git a/ext/opcache/tests/preload_variance.inc b/ext/opcache/tests/preload_variance.inc new file mode 100644 index 0000000000..7b2dd7b8ff --- /dev/null +++ b/ext/opcache/tests/preload_variance.inc @@ -0,0 +1,43 @@ +<?php + +// Requires X, delay to runtime. +// TODO: It is not actually required, because we don't need X to check inheritance in this case. +class A extends Z { + public function method(X $a) {} +} +class B extends Z { + public function method($a) : X {} +} + +// Works. +class C extends Z { + public function method($a): self {} + public function method2($a): C {} +} +class D extends C { + public function method($a): self {} + public function method2($a): D {} +} + +// Works. +interface I {} +interface J extends I {} +class E { + public function method($a): I {} +} +class F extends E { + public function method($a): J {} +} + +// Requires K & L, delay to runtime. +class G { + public function method($a): K {} +} +class H extends G { + public function method($a): L {} +} + +// Early-binding preventer. +class Z { + public function method(X $a) {} +} diff --git a/ext/opcache/tests/preload_variance_ind.inc b/ext/opcache/tests/preload_variance_ind.inc new file mode 100644 index 0000000000..6e331dd48c --- /dev/null +++ b/ext/opcache/tests/preload_variance_ind.inc @@ -0,0 +1,2 @@ +<?php +opcache_compile_file(__DIR__ . '/preload_variance.inc'); diff --git a/ext/opcache/tests/preload_windows.phpt b/ext/opcache/tests/preload_windows.phpt new file mode 100644 index 0000000000..0414cfa0b5 --- /dev/null +++ b/ext/opcache/tests/preload_windows.phpt @@ -0,0 +1,16 @@ +--TEST-- +Preloading is not supported on Windows +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload.inc +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (PHP_OS_FAMILY != 'Windows') die('skip Windows only test'); +?> +--FILE-- +Unreachable +--EXPECTF-- +%s: Error Preloading is not supported on Windows diff --git a/ext/opcache/tests/revalidate_path_01.phpt b/ext/opcache/tests/revalidate_path_01.phpt index 8261633334..d982613103 100644 --- a/ext/opcache/tests/revalidate_path_01.phpt +++ b/ext/opcache/tests/revalidate_path_01.phpt @@ -7,9 +7,11 @@ opcache.revalidate_path=1 --SKIPIF-- <?php require_once('skipif.inc'); ?> <?php if (php_sapi_name() != "cli") die("skip CLI only"); ?> +--CONFLICTS-- +server --FILE-- <?php -$dir = dirname(__FILE__); +$dir = __DIR__; $dir1 = "$dir/test1"; $dir2 = "$dir/test2"; $link = "$dir/test"; @@ -54,7 +56,7 @@ echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/main.php'); ?> --CLEAN-- <?php -$dir = dirname(__FILE__); +$dir = __DIR__; $dir1 = "$dir/test1"; $dir2 = "$dir/test2"; $link = "$dir/test"; diff --git a/ext/opcache/tests/sccp_loop_var_free.phpt b/ext/opcache/tests/sccp_loop_var_free.phpt new file mode 100644 index 0000000000..5166823b0b --- /dev/null +++ b/ext/opcache/tests/sccp_loop_var_free.phpt @@ -0,0 +1,18 @@ +--TEST-- +Check that SCCP correctly handles non-terminating frees of loop variables +--FILE-- +<?php +function test() { + $arr = []; + foreach ($arr as $item) { + if (!empty($result)) { + return $result; + } + } + return 2; +} + +var_dump(test()); +?> +--EXPECT-- +int(2) diff --git a/ext/opcache/tests/verify_return_instanceof.phpt b/ext/opcache/tests/verify_return_instanceof.phpt new file mode 100644 index 0000000000..14323d533b --- /dev/null +++ b/ext/opcache/tests/verify_return_instanceof.phpt @@ -0,0 +1,19 @@ +--TEST-- +Instanceof checks in VERIFY_RETURN_TYPE optimization may deal with unlinked classes +--FILE-- +<?php +interface foo { } + +interface biz {} + +class qux implements foo { + public function bar(): biz { + $x = $this; + return $x; + } +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/opcache/tests/zzz_basic_logging.phpt b/ext/opcache/tests/zzz_basic_logging.phpt index 778857df7c..bf04b50861 100644 --- a/ext/opcache/tests/zzz_basic_logging.phpt +++ b/ext/opcache/tests/zzz_basic_logging.phpt @@ -10,6 +10,7 @@ opcache.file_cache_only=0 opcache.error_log= opcache.log_verbosity_level=4 opcache.huge_code_pages=0 +opcache.preload= --SKIPIF-- <?php require_once('skipif.inc'); ?> --FILE-- diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c index 25dc3ecd6a..889fcabd79 100644 --- a/ext/opcache/zend_accelerator_blacklist.c +++ b/ext/opcache/zend_accelerator_blacklist.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -232,11 +232,7 @@ static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) } } -#ifdef HAVE_GLOB static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filename) -#else -void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) -#endif { char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1], *blacklist_path = NULL; FILE *fp; @@ -315,12 +311,11 @@ void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) if (blacklist_path) { free(blacklist_path); } - zend_accel_blacklist_update_regexp(blacklist); } -#ifdef HAVE_GLOB void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) { +#ifdef HAVE_GLOB glob_t globbuf; int ret; unsigned int i; @@ -340,8 +335,11 @@ void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) } globfree(&globbuf); } -} +#else + zend_accel_blacklist_loadone(blacklist, filename); #endif + zend_accel_blacklist_update_regexp(blacklist); +} zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len) { diff --git a/ext/opcache/zend_accelerator_blacklist.h b/ext/opcache/zend_accelerator_blacklist.h index 2b8e1856b7..16c75bb396 100644 --- a/ext/opcache/zend_accelerator_blacklist.h +++ b/ext/opcache/zend_accelerator_blacklist.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_debug.c b/ext/opcache/zend_accelerator_debug.c index bd0a5bdb0a..991f2329da 100644 --- a/ext/opcache/zend_accelerator_debug.c +++ b/ext/opcache/zend_accelerator_debug.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_debug.h b/ext/opcache/zend_accelerator_debug.h index 833cffd9d0..234d0367de 100644 --- a/ext/opcache/zend_accelerator_debug.h +++ b/ext/opcache/zend_accelerator_debug.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_hash.c b/ext/opcache/zend_accelerator_hash.c index 85581eaa96..7cd9ef1ef4 100644 --- a/ext/opcache/zend_accelerator_hash.c +++ b/ext/opcache/zend_accelerator_hash.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_hash.h b/ext/opcache/zend_accelerator_hash.h index 83258c2f5a..dc18ca54e1 100644 --- a/ext/opcache/zend_accelerator_hash.h +++ b/ext/opcache/zend_accelerator_hash.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 24af035916..88f3dd70be 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -246,7 +246,6 @@ static ZEND_INI_MH(OnEnable) } } -#ifdef HAVE_OPCACHE_FILE_CACHE static ZEND_INI_MH(OnUpdateFileCache) { if (new_value) { @@ -271,7 +270,6 @@ static ZEND_INI_MH(OnUpdateFileCache) OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); return SUCCESS; } -#endif ZEND_INI_BEGIN() STD_PHP_INI_BOOLEAN("opcache.enable" , "1", PHP_INI_ALL, OnEnable, enabled , zend_accel_globals, accel_globals) @@ -313,17 +311,22 @@ ZEND_INI_BEGIN() STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals) #endif -#ifdef HAVE_OPCACHE_FILE_CACHE STD_PHP_INI_ENTRY("opcache.file_cache" , NULL , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache, zend_accel_globals, accel_globals) STD_PHP_INI_BOOLEAN("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals) STD_PHP_INI_BOOLEAN("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals) -#endif #if ENABLE_FILE_CACHE_FALLBACK STD_PHP_INI_BOOLEAN("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals) #endif #ifdef HAVE_HUGE_CODE_PAGES STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals) #endif + STD_PHP_INI_ENTRY("opcache.preload" , "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.preload, zend_accel_globals, accel_globals) +#ifndef ZEND_WIN32 + STD_PHP_INI_ENTRY("opcache.preload_user" , "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.preload_user, zend_accel_globals, accel_globals) +#endif +#if ZEND_WIN32 + STD_PHP_INI_ENTRY("opcache.cache_id" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.cache_id, zend_accel_globals, accel_globals) +#endif ZEND_INI_END() static int filename_is_in_cache(zend_string *filename) @@ -335,12 +338,9 @@ static int filename_is_in_cache(zend_string *filename) if (key != NULL) { zend_persistent_script *persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length); if (persistent_script && !persistent_script->corrupted) { - zend_file_handle handle = {{0}, NULL, NULL, 0, 0}; - - handle.filename = ZSTR_VAL(filename); - handle.type = ZEND_HANDLE_FILENAME; - if (ZCG(accel_directives).validate_timestamps) { + zend_file_handle handle; + zend_stream_init_filename(&handle, ZSTR_VAL(filename)); return validate_timestamp_and_record_ex(persistent_script, &handle) == SUCCESS; } @@ -404,12 +404,10 @@ void zend_accel_override_file_functions(void) { zend_function *old_function; if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) { -#ifdef HAVE_OPCACHE_FILE_CACHE if (file_cache_only) { zend_accel_error(ACCEL_LOG_WARNING, "file_override_enabled has no effect when file_cache_only is set"); return; } -#endif /* override file_exists */ if ((old_function = zend_hash_str_find_ptr(CG(function_table), "file_exists", sizeof("file_exists")-1)) != NULL) { orig_file_exists = old_function->internal_function.handler; @@ -439,13 +437,7 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) { php_info_print_table_start(); - if ( -#ifdef HAVE_OPCACHE_FILE_CACHE - (ZCG(accelerator_enabled) || file_cache_only) -#else - (ZCG(accelerator_enabled)) -#endif - ) { + if (ZCG(accelerator_enabled) || file_cache_only) { php_info_print_table_row(2, "Opcode Caching", "Up and Running"); } else { php_info_print_table_row(2, "Opcode Caching", "Disabled"); @@ -455,7 +447,6 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) } else { php_info_print_table_row(2, "Optimization", "Disabled"); } -#ifdef HAVE_OPCACHE_FILE_CACHE if (!file_cache_only) { php_info_print_table_row(2, "SHM Cache", "Enabled"); } else { @@ -473,7 +464,6 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) php_info_print_table_row(2, "Startup", "OK"); } } else -#endif if (ZCG(enabled)) { if (!accel_startup_ok || zps_api_failure_reason) { php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason); @@ -522,7 +512,7 @@ static zend_module_entry accel_module_entry = { accel_functions, ZEND_MINIT(zend_accelerator), ZEND_MSHUTDOWN(zend_accelerator), - NULL, + accel_activate, NULL, zend_accel_info, PHP_VERSION, @@ -611,7 +601,6 @@ static ZEND_FUNCTION(opcache_get_status) /* Trivia */ add_assoc_bool(return_value, "opcache_enabled", ZCG(accelerator_enabled)); -#ifdef HAVE_OPCACHE_FILE_CACHE if (ZCG(accel_directives).file_cache) { add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache); } @@ -619,7 +608,6 @@ static ZEND_FUNCTION(opcache_get_status) add_assoc_bool(return_value, "file_cache_only", 1); return; } -#endif add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted)); add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending)); @@ -662,6 +650,49 @@ static ZEND_FUNCTION(opcache_get_status) add_assoc_double(&statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0); add_assoc_zval(return_value, "opcache_statistics", &statistics); + if (ZCSG(preload_script)) { + array_init(&statistics); + + add_assoc_long(&statistics, "memory_consumption", ZCSG(preload_script)->dynamic_members.memory_consumption); + + if (zend_hash_num_elements(&ZCSG(preload_script)->script.function_table)) { + zend_op_array *op_array; + + array_init(&scripts); + ZEND_HASH_FOREACH_PTR(&ZCSG(preload_script)->script.function_table, op_array) { + add_next_index_str(&scripts, op_array->function_name); + } ZEND_HASH_FOREACH_END(); + add_assoc_zval(&statistics, "functions", &scripts); + } + + if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) { + zend_class_entry *ce; + zend_string *key; + + array_init(&scripts); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ZCSG(preload_script)->script.class_table, key, ce) { + if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { + add_next_index_str(&scripts, key); + } else { + add_next_index_str(&scripts, ce->name); + } + } ZEND_HASH_FOREACH_END(); + add_assoc_zval(&statistics, "classes", &scripts); + } + + if (ZCSG(saved_scripts)) { + zend_persistent_script **p = ZCSG(saved_scripts); + + array_init(&scripts); + while (*p) { + add_next_index_str(&scripts, (*p)->script.filename); + p++; + } + add_assoc_zval(&statistics, "scripts", &scripts); + } + add_assoc_zval(return_value, "preload_statistics", &statistics); + } + if (fetch_scripts) { /* accelerated scripts */ if (accelerator_get_scripts(&scripts)) { @@ -729,11 +760,9 @@ static ZEND_FUNCTION(opcache_get_configuration) add_assoc_string(&directives, "opcache.mmap_base", STRING_NOT_NULL(ZCG(accel_directives).mmap_base)); #endif -#ifdef HAVE_OPCACHE_FILE_CACHE add_assoc_string(&directives, "opcache.file_cache", ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : ""); add_assoc_bool(&directives, "opcache.file_cache_only", ZCG(accel_directives).file_cache_only); add_assoc_bool(&directives, "opcache.file_cache_consistency_checks", ZCG(accel_directives).file_cache_consistency_checks); -#endif #if ENABLE_FILE_CACHE_FALLBACK add_assoc_bool(&directives, "opcache.file_cache_fallback", ZCG(accel_directives).file_cache_fallback); #endif @@ -744,6 +773,13 @@ static ZEND_FUNCTION(opcache_get_configuration) #ifdef HAVE_HUGE_CODE_PAGES add_assoc_bool(&directives, "opcache.huge_code_pages", ZCG(accel_directives).huge_code_pages); #endif + add_assoc_string(&directives, "opcache.preload", STRING_NOT_NULL(ZCG(accel_directives).preload)); +#ifndef ZEND_WIN32 + add_assoc_string(&directives, "opcache.preload_user", STRING_NOT_NULL(ZCG(accel_directives).preload_user)); +#endif +#if ZEND_WIN32 + add_assoc_string(&directives, "opcache.cache_id", STRING_NOT_NULL(ZCG(accel_directives).cache_id)); +#endif add_assoc_zval(return_value, "directives", &directives); @@ -816,29 +852,38 @@ static ZEND_FUNCTION(opcache_compile_file) zend_file_handle handle; zend_op_array *op_array = NULL; zend_execute_data *orig_execute_data = NULL; + uint32_t orig_compiler_options; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &script_name, &script_name_len) == FAILURE) { return; } - if (!ZCG(accelerator_enabled)) { - zend_error(E_NOTICE, ACCELERATOR_PRODUCT_NAME " seems to be disabled, can't compile file"); + if (!accel_startup_ok) { + zend_error(E_NOTICE, ACCELERATOR_PRODUCT_NAME " has not been properly started, can't compile file"); RETURN_FALSE; } - handle.filename = script_name; - handle.free_filename = 0; - handle.opened_path = NULL; - handle.type = ZEND_HANDLE_FILENAME; + zend_stream_init_filename(&handle, script_name); orig_execute_data = EG(current_execute_data); + orig_compiler_options = CG(compiler_options); + CG(compiler_options) |= ZEND_COMPILE_WITHOUT_EXECUTION; - zend_try { + if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { + /* During preloading, a failure in opcache_compile_file() should result in an overall + * preloading failure. Otherwise we may include partially compiled files in the preload + * state. */ op_array = persistent_compile_file(&handle, ZEND_INCLUDE); - } zend_catch { - EG(current_execute_data) = orig_execute_data; - zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename); - } zend_end_try(); + } else { + zend_try { + op_array = persistent_compile_file(&handle, ZEND_INCLUDE); + } zend_catch { + EG(current_execute_data) = orig_execute_data; + zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename); + } zend_end_try(); + } + + CG(compiler_options) = orig_compiler_options; if(op_array != NULL) { destroy_op_array(op_array); diff --git a/ext/opcache/zend_accelerator_module.h b/ext/opcache/zend_accelerator_module.h index 6454699db3..f7a186708e 100644 --- a/ext/opcache/zend_accelerator_module.h +++ b/ext/opcache/zend_accelerator_module.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index faa2b68409..53efa865c7 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -26,7 +26,7 @@ #include "zend_shared_alloc.h" #if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG -/* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */ +/* If sizeof(void*) == sizeof(zend_ulong) we can use zend_hash index functions */ # define accel_xlat_set(old, new) zend_hash_index_add_new_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old), (new)) # define accel_xlat_get(old) zend_hash_index_find_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old)) #else @@ -34,58 +34,34 @@ # define accel_xlat_get(old) zend_hash_str_find_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*)) #endif +#define IN_ARENA(ptr) \ + ((void*)(ptr) >= ZCG(current_persistent_script)->arena_mem && \ + (void*)(ptr) < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) + #define ARENA_REALLOC(ptr) \ (void*)(((char*)(ptr)) + ((char*)ZCG(arena_mem) - (char*)ZCG(current_persistent_script)->arena_mem)) typedef int (*id_function_t)(void *, void *); typedef void (*unique_copy_ctor_func_t)(void *pElement); -static void zend_accel_destroy_zend_function(zval *zv) -{ - zend_function *function = Z_PTR_P(zv); - - if (function->type == ZEND_USER_FUNCTION) { - if (function->op_array.static_variables) { - if (!(GC_FLAGS(function->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { - if (GC_DELREF(function->op_array.static_variables) == 0) { - FREE_HASHTABLE(function->op_array.static_variables); - } - } - function->op_array.static_variables = NULL; - } - } - - zend_function_dtor(zv); -} - -static void zend_accel_destroy_zend_class(zval *zv) -{ - zend_class_entry *ce = Z_PTR_P(zv); - ce->function_table.pDestructor = zend_accel_destroy_zend_function; - destroy_zend_class(zv); -} - zend_persistent_script* create_persistent_script(void) { zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script)); memset(persistent_script, 0, sizeof(zend_persistent_script)); - zend_hash_init(&persistent_script->script.function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0); + zend_hash_init(&persistent_script->script.function_table, 0, NULL, ZEND_FUNCTION_DTOR, 0); /* class_table is usually destroyed by free_persistent_script() that * overrides destructor. ZEND_CLASS_DTOR may be used by standard * PHP compiler */ - zend_hash_init(&persistent_script->script.class_table, 16, NULL, ZEND_CLASS_DTOR, 0); + zend_hash_init(&persistent_script->script.class_table, 0, NULL, ZEND_CLASS_DTOR, 0); return persistent_script; } void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements) { - if (destroy_elements) { - persistent_script->script.function_table.pDestructor = zend_accel_destroy_zend_function; - persistent_script->script.class_table.pDestructor = zend_accel_destroy_zend_class; - } else { + if (!destroy_elements) { persistent_script->script.function_table.pDestructor = NULL; persistent_script->script.class_table.pDestructor = NULL; } @@ -100,238 +76,177 @@ void free_persistent_script(zend_persistent_script *persistent_script, int destr efree(persistent_script); } -static int is_not_internal_function(zval *zv) -{ - zend_function *function = Z_PTR_P(zv); - return(function->type != ZEND_INTERNAL_FUNCTION); -} - -void zend_accel_free_user_functions(HashTable *ht) +void zend_accel_move_user_functions(HashTable *src, uint32_t count, zend_script *script) { - dtor_func_t orig_dtor = ht->pDestructor; - - ht->pDestructor = NULL; - zend_hash_apply(ht, (apply_func_t) is_not_internal_function); - ht->pDestructor = orig_dtor; -} + Bucket *p, *end; + HashTable *dst; + zend_string *filename; + dtor_func_t orig_dtor; + zend_function *function; -void zend_accel_move_user_functions(HashTable *src, HashTable *dst) -{ - Bucket *p; - dtor_func_t orig_dtor = src->pDestructor; + if (!count) { + return; + } + dst = &script->function_table; + filename = script->main_op_array.filename; + orig_dtor = src->pDestructor; src->pDestructor = NULL; - zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); - ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { - zend_function *function = Z_PTR(p->val); - - if (EXPECTED(function->type == ZEND_USER_FUNCTION)) { + zend_hash_extend(dst, count, 0); + end = src->arData + src->nNumUsed; + p = end - count; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; + function = Z_PTR(p->val); + if (EXPECTED(function->type == ZEND_USER_FUNCTION) + && EXPECTED(function->op_array.filename == filename)) { _zend_hash_append_ptr(dst, p->key, function); zend_hash_del_bucket(src, p); - } else { - break; } - } ZEND_HASH_FOREACH_END(); + } src->pDestructor = orig_dtor; } -void zend_accel_copy_internal_functions(void) +void zend_accel_move_user_classes(HashTable *src, uint32_t count, zend_script *script) { - zend_string *key; - zval *val; - - ZEND_HASH_FOREACH_STR_KEY_VAL(CG(function_table), key, val) { - zend_internal_function *function = Z_PTR_P(val); - if (function->type == ZEND_INTERNAL_FUNCTION) { - zend_hash_add_new_ptr(&ZCG(function_table), key, function); - } - } ZEND_HASH_FOREACH_END(); - ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table)); -} + Bucket *p, *end; + HashTable *dst; + zend_string *filename; + dtor_func_t orig_dtor; + zend_class_entry *ce; -static inline void zend_clone_zval(zval *src) -{ - void *ptr; + if (!count) { + return; + } - if (Z_TYPE_P(src) == IS_REFERENCE) { - ptr = accel_xlat_get(Z_REF_P(src)); - if (ptr != NULL) { - Z_REF_P(src) = ptr; - return; - } else { - zend_reference *old = Z_REF_P(src); - ZVAL_NEW_REF(src, &old->val); - Z_REF_P(src)->gc = old->gc; - accel_xlat_set(old, Z_REF_P(src)); - src = Z_REFVAL_P(src); + dst = &script->class_table; + filename = script->main_op_array.filename; + orig_dtor = src->pDestructor; + src->pDestructor = NULL; + zend_hash_extend(dst, count, 0); + end = src->arData + src->nNumUsed; + p = end - count; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; + ce = Z_PTR(p->val); + if (EXPECTED(ce->type == ZEND_USER_CLASS) + && EXPECTED(ce->info.user.filename == filename)) { + _zend_hash_append_ptr(dst, p->key, ce); + zend_hash_del_bucket(src, p); } } + src->pDestructor = orig_dtor; } -static void zend_hash_clone_constants(HashTable *ht, HashTable *source) +static void zend_hash_clone_constants(HashTable *ht) { - Bucket *p, *q, *end; - zend_ulong nIndex; + Bucket *p, *end; zend_class_constant *c; - ht->nTableSize = source->nTableSize; - ht->nTableMask = source->nTableMask; - ht->nNumUsed = 0; - ht->nNumOfElements = source->nNumOfElements; - ht->nNextFreeElement = source->nNextFreeElement; - ht->pDestructor = NULL; - HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = 0; - - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { - ht->arData = source->arData; + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { return; } - ZEND_ASSERT((HT_FLAGS(source) & HASH_FLAG_PACKED) == 0); - HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); - HT_HASH_RESET(ht); + p = emalloc(HT_SIZE(ht)); + memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, p); - p = source->arData; - end = p + source->nNumUsed; + p = ht->arData; + end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - nIndex = p->h | ht->nTableMask; + c = Z_PTR(p->val); + if (IN_ARENA(c)) { + c = ARENA_REALLOC(c); + Z_PTR(p->val) = c; - /* Insert into hash collision list */ - q = ht->arData + ht->nNumUsed; - Z_NEXT(q->val) = HT_HASH(ht, nIndex); - HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); - - /* Initialize key */ - q->h = p->h; - q->key = p->key; - - /* Copy data */ - c = ARENA_REALLOC(Z_PTR(p->val)); - ZVAL_PTR(&q->val, c); - - zend_clone_zval(&c->value); - if ((void*)c->ce >= ZCG(current_persistent_script)->arena_mem && - (void*)c->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { - c->ce = ARENA_REALLOC(c->ce); + if (IN_ARENA(c->ce)) { + c->ce = ARENA_REALLOC(c->ce); + } } } } -static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce) +static void zend_hash_clone_methods(HashTable *ht) { - Bucket *p, *q, *end; - zend_ulong nIndex; + Bucket *p, *end; zend_op_array *new_entry; - ht->nTableSize = source->nTableSize; - ht->nTableMask = source->nTableMask; - ht->nNumUsed = 0; - ht->nNumOfElements = source->nNumOfElements; - ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZEND_FUNCTION_DTOR; - HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = 0; - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { - ht->arData = source->arData; + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { return; } - ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_PACKED)); - HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); - HT_HASH_RESET(ht); + p = emalloc(HT_SIZE(ht)); + memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, p); - p = source->arData; - end = p + source->nNumUsed; + p = ht->arData; + end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); + new_entry = Z_PTR(p->val); + if (IN_ARENA(new_entry)) { + new_entry = ARENA_REALLOC(new_entry); + Z_PTR(p->val) = new_entry; - nIndex = p->h | ht->nTableMask; - - /* Insert into hash collision list */ - q = ht->arData + ht->nNumUsed; - Z_NEXT(q->val) = HT_HASH(ht, nIndex); - HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); - - /* Initialize key */ - q->h = p->h; - ZEND_ASSERT(p->key != NULL); - q->key = p->key; - - /* Copy data */ - ZVAL_PTR(&q->val, ARENA_REALLOC(Z_PTR(p->val))); - new_entry = (zend_op_array*)Z_PTR(q->val); - - if ((void*)new_entry->scope >= ZCG(current_persistent_script)->arena_mem && - (void*)new_entry->scope < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { + if (IN_ARENA(new_entry->scope)) { + new_entry->scope = ARENA_REALLOC(new_entry->scope); - new_entry->scope = ARENA_REALLOC(new_entry->scope); - - /* update prototype */ - if (new_entry->prototype) { - new_entry->prototype = ARENA_REALLOC(new_entry->prototype); + /* update prototype */ + if (IN_ARENA(new_entry->prototype)) { + new_entry->prototype = ARENA_REALLOC(new_entry->prototype); + } + } + if (IN_ARENA(ZEND_MAP_PTR(new_entry->run_time_cache))) { + ZEND_MAP_PTR_INIT(new_entry->run_time_cache, ARENA_REALLOC(ZEND_MAP_PTR(new_entry->run_time_cache))); } + ZEND_MAP_PTR_INIT(new_entry->static_variables_ptr, &new_entry->static_variables); } } } -static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce) +static void zend_hash_clone_prop_info(HashTable *ht) { - Bucket *p, *q, *end; - zend_ulong nIndex; + Bucket *p, *end; zend_property_info *prop_info; - ht->nTableSize = source->nTableSize; - ht->nTableMask = source->nTableMask; - ht->nNumUsed = 0; - ht->nNumOfElements = source->nNumOfElements; - ht->nNextFreeElement = source->nNextFreeElement; - ht->pDestructor = NULL; - HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = 0; - - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { - ht->arData = source->arData; + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { return; } - ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_PACKED)); - HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); - HT_HASH_RESET(ht); + p = emalloc(HT_SIZE(ht)); + memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, p); - p = source->arData; - end = p + source->nNumUsed; + p = ht->arData; + end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); + prop_info = Z_PTR(p->val); + if (IN_ARENA(prop_info)) { + prop_info = ARENA_REALLOC(prop_info); + Z_PTR(p->val) = prop_info; - nIndex = p->h | ht->nTableMask; - - /* Insert into hash collision list */ - q = ht->arData + ht->nNumUsed; - Z_NEXT(q->val) = HT_HASH(ht, nIndex); - HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); - - /* Initialize key */ - q->h = p->h; - ZEND_ASSERT(p->key != NULL); - q->key = p->key; - - /* Copy data */ - prop_info = ARENA_REALLOC(Z_PTR(p->val)); - ZVAL_PTR(&q->val, prop_info); + if (IN_ARENA(prop_info->ce)) { + prop_info->ce = ARENA_REALLOC(prop_info->ce); + } - if ((void*)prop_info->ce >= ZCG(current_persistent_script)->arena_mem && - (void*)prop_info->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { - prop_info->ce = ARENA_REALLOC(prop_info->ce); + if (ZEND_TYPE_IS_CE(prop_info->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop_info->type); + if (IN_ARENA(ce)) { + ce = ARENA_REALLOC(ce); + prop_info->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop_info->type)); + } + } } } } #define zend_update_inherited_handler(handler) \ { \ - if (ce->handler != NULL) { \ + if (ce->handler != NULL && IN_ARENA(ce->handler)) { \ ce->handler = ARENA_REALLOC(ce->handler); \ } \ } @@ -340,70 +255,93 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla static void zend_class_copy_ctor(zend_class_entry **pce) { zend_class_entry *ce = *pce; - zend_class_entry *old_ce = ce; zval *src, *dst, *end; - *pce = ce = ARENA_REALLOC(old_ce); + *pce = ce = ARENA_REALLOC(ce); ce->refcount = 1; - if (ce->parent) { + if ((ce->ce_flags & ZEND_ACC_LINKED) && IN_ARENA(ce->parent)) { ce->parent = ARENA_REALLOC(ce->parent); } - if (old_ce->default_properties_table) { - ce->default_properties_table = emalloc(sizeof(zval) * old_ce->default_properties_count); - src = old_ce->default_properties_table; - end = src + old_ce->default_properties_count; - dst = ce->default_properties_table; + if (ce->default_properties_table) { + dst = emalloc(sizeof(zval) * ce->default_properties_count); + src = ce->default_properties_table; + end = src + ce->default_properties_count; + ce->default_properties_table = dst; for (; src != end; src++, dst++) { - ZVAL_COPY_VALUE(dst, src); - zend_clone_zval(dst); + ZVAL_COPY_VALUE_PROP(dst, src); } } - zend_hash_clone_methods(&ce->function_table, &old_ce->function_table, old_ce, ce); + zend_hash_clone_methods(&ce->function_table); /* static members */ - if (old_ce->default_static_members_table) { + if (ce->default_static_members_table) { int i, end; - zend_class_entry *parent = ce->parent; + zend_class_entry *parent = !(ce->ce_flags & ZEND_ACC_LINKED) ? NULL : ce->parent; - ce->default_static_members_table = emalloc(sizeof(zval) * old_ce->default_static_members_count); + dst = emalloc(sizeof(zval) * ce->default_static_members_count); + src = ce->default_static_members_table; + ce->default_static_members_table = dst; i = ce->default_static_members_count - 1; /* Copy static properties in this class */ end = parent ? parent->default_static_members_count : 0; for (; i >= end; i--) { - zval *p = &ce->default_static_members_table[i]; - ZVAL_COPY_VALUE(p, &old_ce->default_static_members_table[i]); - zend_clone_zval(p); + zval *p = &dst[i]; + ZVAL_COPY_VALUE(p, &src[i]); } /* Create indirections to static properties from parent classes */ 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]; + zval *p = &dst[i]; ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); } parent = parent->parent; } } - ce->static_members_table = ce->default_static_members_table; + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); /* properties_info */ - zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce); + zend_hash_clone_prop_info(&ce->properties_info); /* constants table */ - zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table); + zend_hash_clone_constants(&ce->constants_table); + + if (ce->properties_info_table) { + int i; + ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table); + for (i = 0; i < ce->default_properties_count; i++) { + if (IN_ARENA(ce->properties_info_table[i])) { + ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]); + } + } + } - /* interfaces aren't really implemented, so we create a new table */ if (ce->num_interfaces) { - ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces); - memset(ce->interfaces, 0, sizeof(zend_class_entry *) * ce->num_interfaces); - } else { - ce->interfaces = NULL; + zend_class_name *interface_names; + + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + interface_names = emalloc(sizeof(zend_class_name) * ce->num_interfaces); + memcpy(interface_names, ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces); + ce->interface_names = interface_names; + } else { + zend_class_entry **interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces); + uint32_t i; + + for (i = 0; i < ce->num_interfaces; i++) { + if (IN_ARENA(ce->interfaces[i])) { + interfaces[i] = ARENA_REALLOC(ce->interfaces[i]); + } else { + interfaces[i] = ce->interfaces[i]; + } + } + ce->interfaces = interfaces; + } } zend_update_inherited_handler(constructor); @@ -425,40 +363,47 @@ static void zend_class_copy_ctor(zend_class_entry **pce) zend_update_inherited_handler(__debugInfo); /* 5.4 traits */ - if (ce->trait_aliases) { - zend_trait_alias **trait_aliases; - int i = 0; + if (ce->num_traits) { + zend_class_name *trait_names = emalloc(sizeof(zend_class_name) * ce->num_traits); - while (ce->trait_aliases[i]) { - i++; - } - trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1)); - i = 0; - while (ce->trait_aliases[i]) { - trait_aliases[i] = emalloc(sizeof(zend_trait_alias)); - memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias)); - i++; - } - trait_aliases[i] = NULL; - ce->trait_aliases = trait_aliases; - } + memcpy(trait_names, ce->trait_names, sizeof(zend_class_name) * ce->num_traits); + ce->trait_names = trait_names; - if (ce->trait_precedences) { - zend_trait_precedence **trait_precedences; - int i = 0; + if (ce->trait_aliases) { + zend_trait_alias **trait_aliases; + int i = 0; - while (ce->trait_precedences[i]) { - i++; + while (ce->trait_aliases[i]) { + i++; + } + trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1)); + i = 0; + while (ce->trait_aliases[i]) { + trait_aliases[i] = emalloc(sizeof(zend_trait_alias)); + memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias)); + i++; + } + trait_aliases[i] = NULL; + ce->trait_aliases = trait_aliases; } - trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1)); - i = 0; - while (ce->trait_precedences[i]) { - trait_precedences[i] = emalloc(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); - memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); - i++; + + if (ce->trait_precedences) { + zend_trait_precedence **trait_precedences; + int i = 0; + + while (ce->trait_precedences[i]) { + i++; + } + trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1)); + i = 0; + while (ce->trait_precedences[i]) { + trait_precedences[i] = emalloc(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); + memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); + i++; + } + trait_precedences[i] = NULL; + ce->trait_precedences = trait_precedences; } - trait_precedences[i] = NULL; - ce->trait_precedences = trait_precedences; } } @@ -477,8 +422,16 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key */ - t = zend_hash_update(target, p->key, &p->val); + /* Runtime definition key. There are two circumstances under which the key can + * already be defined: + * 1. The file has been re-included without being changed in the meantime. In + * this case we can keep the old value, because we know that the definition + * hasn't changed. + * 2. The file has been changed in the meantime, but the RTD key ends up colliding. + * This would be a bug. + * As we can't distinguish these cases, we assume that it is 1. and keep the old + * value. */ + continue; } else { goto failure; } @@ -521,8 +474,8 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key */ - zend_hash_update_ptr(target, p->key, Z_PTR(p->val)); + /* See comment in zend_accel_function_hash_copy(). */ + continue; } else { goto failure; } @@ -564,7 +517,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source) t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key - ignore and wait for runtime */ + /* See comment in zend_accel_function_hash_copy(). */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { zend_class_entry *ce1 = Z_PTR(p->val); @@ -601,7 +554,7 @@ static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *so t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key - ignore and wait for runtime */ + /* See comment in zend_accel_function_hash_copy(). */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { zend_class_entry *ce1 = Z_PTR(p->val); @@ -618,14 +571,18 @@ static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *so } } else { t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); - zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t)); + if (!(((zend_class_entry*)Z_PTR_P(t))->ce_flags & ZEND_ACC_IMMUTABLE)) { + zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t)); + } } } target->nInternalPointer = 0; return; } -#if defined(__AVX__) +#if __has_feature(memory_sanitizer) +# define fast_memcpy memcpy +#elif defined(__AVX__) # include <nmmintrin.h> # if defined(__GNUC__) && defined(__i386__) static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) @@ -779,6 +736,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); *op_array = persistent_script->script.main_op_array; + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); if (EXPECTED(from_shared_memory)) { zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0); @@ -823,6 +781,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, zend_hash_destroy(&ZCG(bind_hash)); ZCG(current_persistent_script) = NULL; + zend_map_ptr_extend(ZCSG(map_ptr_last)); } else /* if (!from_shared_memory) */ { if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) { zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table); diff --git a/ext/opcache/zend_accelerator_util_funcs.h b/ext/opcache/zend_accelerator_util_funcs.h index b8c0fd8a5b..331be541cc 100644 --- a/ext/opcache/zend_accelerator_util_funcs.h +++ b/ext/opcache/zend_accelerator_util_funcs.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -25,13 +25,11 @@ #include "zend.h" #include "ZendAccelerator.h" -void zend_accel_copy_internal_functions(void); - zend_persistent_script* create_persistent_script(void); void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements); -void zend_accel_free_user_functions(HashTable *ht); -void zend_accel_move_user_functions(HashTable *str, HashTable *dst); +void zend_accel_move_user_functions(HashTable *str, uint32_t count, zend_script *script); +void zend_accel_move_user_classes(HashTable *str, uint32_t count, zend_script *script); zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory); @@ -42,10 +40,3 @@ unsigned int zend_adler32(unsigned int checksum, unsigned char *buf, uint32_t le unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script); #endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - */ diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index ac6f84b7d1..b46edcb065 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -27,8 +27,6 @@ #include "ext/standard/md5.h" #endif -#ifdef HAVE_OPCACHE_FILE_CACHE - #include "ZendAccelerator.h" #include "zend_file_cache.h" #include "zend_shared_alloc.h" @@ -51,6 +49,10 @@ # include <sys/file.h> #endif +#if __has_feature(memory_sanitizer) +# include <sanitizer/msan_interface.h> +#endif + #ifndef ZEND_WIN32 #define zend_file_cache_unlink unlink #define zend_file_cache_open open @@ -272,7 +274,7 @@ static void zend_file_cache_serialize_hash(HashTable *ht, { Bucket *p, *end; - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { ht->arData = NULL; return; } @@ -366,6 +368,33 @@ static void zend_file_cache_serialize_zval(zval *zv, zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf); } break; + case IS_INDIRECT: + /* Used by static properties. */ + SERIALIZE_PTR(Z_INDIRECT_P(zv)); + break; + } +} + +/* Adjust serialized pointer to conform to zend_type assumptions: + * Pointer must have low two bits unset and be larger than 0x400. */ +#define ZEND_TYPE_PTR_ENCODE(ptr) \ + (void*)(((uintptr_t)ptr << 2) + 0x400) +#define ZEND_TYPE_PTR_DECODE(ptr) \ + (void*)(((uintptr_t)ptr - 0x400) >> 2) + +static void zend_file_cache_serialize_type( + zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf) +{ + if (ZEND_TYPE_IS_NAME(*type)) { + zend_string *name = ZEND_TYPE_NAME(*type); + SERIALIZE_STR(name); + name = ZEND_TYPE_PTR_ENCODE(name); + *type = ZEND_TYPE_ENCODE_CLASS(name, ZEND_TYPE_ALLOW_NULL(*type)); + } else if (ZEND_TYPE_IS_CE(*type)) { + zend_class_entry *ce = ZEND_TYPE_CE(*type); + SERIALIZE_PTR(ce); + ce = ZEND_TYPE_PTR_ENCODE(ce); + *type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(*type)); } } @@ -468,8 +497,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(opline->op2.jmp_addr); } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_SWITCH_LONG: @@ -498,16 +525,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra if (!IS_SERIALIZED(p->name)) { SERIALIZE_STR(p->name); } - if (ZEND_TYPE_IS_CLASS(p->type)) { - zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(p->type); - zend_string *type_name = ZEND_TYPE_NAME(p->type); - - SERIALIZE_STR(type_name); - p->type = - (Z_UL(1) << (sizeof(zend_type)*8-1)) | /* type is class */ - (allow_null ? (Z_UL(1) << (sizeof(zend_type)*8-2)) : Z_UL(0)) | /* type allow null */ - (zend_type)type_name; - } + zend_file_cache_serialize_type(&p->type, script, info, buf); p++; } } @@ -534,6 +552,13 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_STR(op_array->doc_comment); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); + + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); + } else { + SERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache)); + } } } @@ -570,6 +595,7 @@ static void zend_file_cache_serialize_prop_info(zval *zv, SERIALIZE_STR(prop->doc_comment); } } + zend_file_cache_serialize_type(&prop->type, script, info, buf); } } @@ -610,6 +636,13 @@ static void zend_file_cache_serialize_class(zval *zv, UNSERIALIZE_PTR(ce); SERIALIZE_STR(ce->name); + if (ce->parent) { + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + SERIALIZE_STR(ce->parent_name); + } else { + SERIALIZE_PTR(ce->parent); + } + } zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func); if (ce->default_properties_table) { zval *p, *end; @@ -624,16 +657,13 @@ static void zend_file_cache_serialize_class(zval *zv, } } if (ce->default_static_members_table) { - zval *table, *p, *end; + zval *p, *end; SERIALIZE_PTR(ce->default_static_members_table); - table = ce->default_static_members_table; - UNSERIALIZE_PTR(table); + p = ce->default_static_members_table; + UNSERIALIZE_PTR(p); - /* Serialize only static properties in this class. - * Static properties from parent classes will be handled in class_copy_ctor */ - p = table + (ce->parent ? ce->parent->default_static_members_count : 0); - end = table + ce->default_static_members_count; + end = p + ce->default_static_members_count; while (p < end) { zend_file_cache_serialize_zval(p, script, info, buf); p++; @@ -644,60 +674,102 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_STR(ce->info.user.doc_comment); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); - if (ce->trait_aliases) { - zend_trait_alias **p, *q; + if (ce->properties_info_table) { + uint32_t i; + zend_property_info **table; - SERIALIZE_PTR(ce->trait_aliases); - p = ce->trait_aliases; - UNSERIALIZE_PTR(p); + SERIALIZE_PTR(ce->properties_info_table); + table = ce->properties_info_table; + UNSERIALIZE_PTR(table); - while (*p) { - SERIALIZE_PTR(*p); - q = *p; - UNSERIALIZE_PTR(q); + for (i = 0; i < ce->default_properties_count; i++) { + SERIALIZE_PTR(table[i]); + } + } - if (q->trait_method.method_name) { - SERIALIZE_STR(q->trait_method.method_name); - } - if (q->trait_method.class_name) { - SERIALIZE_STR(q->trait_method.class_name); - } + if (ce->num_interfaces) { + uint32_t i; + zend_class_name *interface_names; - if (q->alias) { - SERIALIZE_STR(q->alias); - } - p++; + ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); + + SERIALIZE_PTR(ce->interface_names); + interface_names = ce->interface_names; + UNSERIALIZE_PTR(interface_names); + + for (i = 0; i < ce->num_interfaces; i++) { + SERIALIZE_STR(interface_names[i].name); + SERIALIZE_STR(interface_names[i].lc_name); } } - if (ce->trait_precedences) { - zend_trait_precedence **p, *q; - int j; + if (ce->num_traits) { + uint32_t i; + zend_class_name *trait_names; - SERIALIZE_PTR(ce->trait_precedences); - p = ce->trait_precedences; - UNSERIALIZE_PTR(p); + SERIALIZE_PTR(ce->trait_names); + trait_names = ce->trait_names; + UNSERIALIZE_PTR(trait_names); - while (*p) { - SERIALIZE_PTR(*p); - q = *p; - UNSERIALIZE_PTR(q); + for (i = 0; i < ce->num_traits; i++) { + SERIALIZE_STR(trait_names[i].name); + SERIALIZE_STR(trait_names[i].lc_name); + } - if (q->trait_method.method_name) { - SERIALIZE_STR(q->trait_method.method_name); - } - if (q->trait_method.class_name) { - SERIALIZE_STR(q->trait_method.class_name); + if (ce->trait_aliases) { + zend_trait_alias **p, *q; + + SERIALIZE_PTR(ce->trait_aliases); + p = ce->trait_aliases; + UNSERIALIZE_PTR(p); + + while (*p) { + SERIALIZE_PTR(*p); + q = *p; + UNSERIALIZE_PTR(q); + + if (q->trait_method.method_name) { + SERIALIZE_STR(q->trait_method.method_name); + } + if (q->trait_method.class_name) { + SERIALIZE_STR(q->trait_method.class_name); + } + + if (q->alias) { + SERIALIZE_STR(q->alias); + } + p++; } + } - for (j = 0; j < q->num_excludes; j++) { - SERIALIZE_STR(q->exclude_class_names[j]); + if (ce->trait_precedences) { + zend_trait_precedence **p, *q; + uint32_t j; + + SERIALIZE_PTR(ce->trait_precedences); + p = ce->trait_precedences; + UNSERIALIZE_PTR(p); + + while (*p) { + SERIALIZE_PTR(*p); + q = *p; + UNSERIALIZE_PTR(q); + + if (q->trait_method.method_name) { + SERIALIZE_STR(q->trait_method.method_name); + } + if (q->trait_method.class_name) { + SERIALIZE_STR(q->trait_method.class_name); + } + + for (j = 0; j < q->num_excludes; j++) { + SERIALIZE_STR(q->exclude_class_names[j]); + } + p++; } - p++; } } - SERIALIZE_PTR(ce->parent); SERIALIZE_PTR(ce->constructor); SERIALIZE_PTR(ce->destructor); SERIALIZE_PTR(ce->clone); @@ -711,6 +783,18 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_PTR(ce->__tostring); SERIALIZE_PTR(ce->__callstatic); SERIALIZE_PTR(ce->__debugInfo); + + if (ce->iterator_funcs_ptr) { + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next); + SERIALIZE_PTR(ce->iterator_funcs_ptr); + } + + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } static void zend_file_cache_serialize(zend_persistent_script *script, @@ -720,7 +804,7 @@ static void zend_file_cache_serialize(zend_persistent_script *script, zend_persistent_script *new_script; memcpy(info->magic, "OPCACHE", 8); - memcpy(info->system_id, ZCG(system_id), 32); + memcpy(info->system_id, accel_system_id, 32); info->mem_size = script->size; info->str_size = 0; info->script_offset = (char*)script - (char*)script->mem; @@ -749,39 +833,21 @@ static char *zend_file_cache_get_bin_file_path(zend_string *script_path) filename = emalloc(len + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX)); memcpy(filename, ZCG(accel_directives).file_cache, len); filename[len] = '/'; - memcpy(filename + len + 1, ZCG(system_id), 32); + memcpy(filename + len + 1, accel_system_id, 32); memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path)); memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX)); #else - PHP_MD5_CTX ctx; - char md5uname[32]; - unsigned char digest[16], c; - size_t i; - char *uname = php_win32_get_username(); - - PHP_MD5Init(&ctx); - PHP_MD5Update(&ctx, uname, strlen(uname)); - PHP_MD5Final(digest, &ctx); - for (i = 0; i < 16; i++) { - c = digest[i] >> 4; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5uname[i * 2] = c; - c = digest[i] & 0x0f; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5uname[(i * 2) + 1] = c; - } - len = strlen(ZCG(accel_directives).file_cache); filename = emalloc(len + 33 + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX)); memcpy(filename, ZCG(accel_directives).file_cache, len); filename[len] = '\\'; - memcpy(filename + 1 + len, md5uname, 32); + memcpy(filename + 1 + len, accel_uname_id, 32); len += 1 + 32; filename[len] = '\\'; - memcpy(filename + len + 1, ZCG(system_id), 32); + memcpy(filename + len + 1, accel_system_id, 32); if (ZSTR_LEN(script_path) >= 7 && ':' == ZSTR_VAL(script_path)[4] && '/' == ZSTR_VAL(script_path)[5] && '/' == ZSTR_VAL(script_path)[6]) { /* phar:// or file:// */ @@ -807,7 +873,6 @@ static char *zend_file_cache_get_bin_file_path(zend_string *script_path) memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path)); memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX)); } - free(uname); #endif return filename; @@ -869,6 +934,14 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm) info.checksum = zend_adler32(ADLER32_INIT, buf, script->size); info.checksum = zend_adler32(info.checksum, (unsigned char*)ZSTR_VAL((zend_string*)ZCG(mem)), info.str_size); +#if __has_feature(memory_sanitizer) + /* The buffer may contain uninitialized regions. However, the uninitialized parts will not be + * used when reading the cache. We should probably still try to get things fully initialized + * for reproducibility, but for now ignore this issue. */ + __msan_unpoison(&info, sizeof(info)); + __msan_unpoison(buf, script->size); +#endif + #ifdef HAVE_SYS_UIO_H vec[0].iov_base = &info; vec[0].iov_len = sizeof(info); @@ -922,7 +995,7 @@ static void zend_file_cache_unserialize_hash(HashTable *ht, Bucket *p, *end; ht->pDestructor = dtor; - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { if (EXPECTED(!file_cache_only)) { HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); } else { @@ -1007,6 +1080,26 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf); } break; + case IS_INDIRECT: + /* Used by static properties. */ + UNSERIALIZE_PTR(Z_INDIRECT_P(zv)); + break; + } +} + +static void zend_file_cache_unserialize_type( + zend_type *type, zend_persistent_script *script, void *buf) +{ + if (ZEND_TYPE_IS_NAME(*type)) { + zend_string *name = ZEND_TYPE_NAME(*type); + name = ZEND_TYPE_PTR_DECODE(name); + UNSERIALIZE_STR(name); + *type = ZEND_TYPE_ENCODE_CLASS(name, ZEND_TYPE_ALLOW_NULL(*type)); + } else if (ZEND_TYPE_IS_CE(*type)) { + zend_class_entry *ce = ZEND_TYPE_CE(*type); + ce = ZEND_TYPE_PTR_DECODE(ce); + UNSERIALIZE_PTR(ce); + *type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(*type)); } } @@ -1098,8 +1191,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(opline->op2.jmp_addr); } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_SWITCH_LONG: @@ -1127,13 +1218,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr if (!IS_UNSERIALIZED(p->name)) { UNSERIALIZE_STR(p->name); } - if (p->type & (Z_UL(1) << (sizeof(zend_type)*8-1))) { /* type is class */ - zend_bool allow_null = (p->type & (Z_UL(1) << (sizeof(zend_type)*8-2))) != 0; /* type allow null */ - zend_string *type_name = (zend_string*)(p->type & ~(((Z_UL(1) << (sizeof(zend_type)*8-1))) | ((Z_UL(1) << (sizeof(zend_type)*8-2))))); - - UNSERIALIZE_STR(type_name); - p->type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null); - } + zend_file_cache_unserialize_type(&p->type, script, buf); p++; } } @@ -1159,6 +1244,26 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_STR(op_array->doc_comment); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); + + if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { + if (op_array->static_variables) { + ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); + } else { + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + } + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + } else { + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + if (ZEND_MAP_PTR(op_array->run_time_cache)) { + if (script->corrupted) { + /* Not in SHM: Use serialized arena pointer. */ + UNSERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache)); + } else { + /* In SHM: Allocate new pointer. */ + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + } + } + } } } @@ -1191,6 +1296,7 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, UNSERIALIZE_STR(prop->doc_comment); } } + zend_file_cache_unserialize_type(&prop->type, script, buf); } } @@ -1227,7 +1333,13 @@ static void zend_file_cache_unserialize_class(zval *zv, ce = Z_PTR_P(zv); UNSERIALIZE_STR(ce->name); - UNSERIALIZE_PTR(ce->parent); + if (ce->parent) { + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + UNSERIALIZE_STR(ce->parent_name); + } else { + UNSERIALIZE_PTR(ce->parent); + } + } zend_file_cache_unserialize_hash(&ce->function_table, script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR); if (ce->default_properties_table) { @@ -1242,14 +1354,10 @@ static void zend_file_cache_unserialize_class(zval *zv, } } if (ce->default_static_members_table) { - zval *table, *p, *end; - - /* Unserialize only static properties in this class. - * Static properties from parent classes will be handled in class_copy_ctor */ + zval *p, *end; UNSERIALIZE_PTR(ce->default_static_members_table); - table = ce->default_static_members_table; - p = table + (ce->parent ? ce->parent->default_static_members_count : 0); - end = table + ce->default_static_members_count; + p = ce->default_static_members_table; + end = p + ce->default_static_members_count; while (p < end) { zend_file_cache_unserialize_zval(p, script, buf); p++; @@ -1262,52 +1370,84 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); - if (ce->trait_aliases) { - zend_trait_alias **p, *q; + if (ce->properties_info_table) { + uint32_t i; + UNSERIALIZE_PTR(ce->properties_info_table); - UNSERIALIZE_PTR(ce->trait_aliases); - p = ce->trait_aliases; + for (i = 0; i < ce->default_properties_count; i++) { + UNSERIALIZE_PTR(ce->properties_info_table[i]); + } + } - while (*p) { - UNSERIALIZE_PTR(*p); - q = *p; + if (ce->num_interfaces) { + uint32_t i; - if (q->trait_method.method_name) { - UNSERIALIZE_STR(q->trait_method.method_name); - } - if (q->trait_method.class_name) { - UNSERIALIZE_STR(q->trait_method.class_name); - } + ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); + UNSERIALIZE_PTR(ce->interface_names); - if (q->alias) { - UNSERIALIZE_STR(q->alias); - } - p++; + for (i = 0; i < ce->num_interfaces; i++) { + UNSERIALIZE_STR(ce->interface_names[i].name); + UNSERIALIZE_STR(ce->interface_names[i].lc_name); } } - if (ce->trait_precedences) { - zend_trait_precedence **p, *q; - int j; + if (ce->num_traits) { + uint32_t i; + + UNSERIALIZE_PTR(ce->trait_names); - UNSERIALIZE_PTR(ce->trait_precedences); - p = ce->trait_precedences; + for (i = 0; i < ce->num_traits; i++) { + UNSERIALIZE_STR(ce->trait_names[i].name); + UNSERIALIZE_STR(ce->trait_names[i].lc_name); + } - while (*p) { - UNSERIALIZE_PTR(*p); - q = *p; + if (ce->trait_aliases) { + zend_trait_alias **p, *q; - if (q->trait_method.method_name) { - UNSERIALIZE_STR(q->trait_method.method_name); - } - if (q->trait_method.class_name) { - UNSERIALIZE_STR(q->trait_method.class_name); + UNSERIALIZE_PTR(ce->trait_aliases); + p = ce->trait_aliases; + + while (*p) { + UNSERIALIZE_PTR(*p); + q = *p; + + if (q->trait_method.method_name) { + UNSERIALIZE_STR(q->trait_method.method_name); + } + if (q->trait_method.class_name) { + UNSERIALIZE_STR(q->trait_method.class_name); + } + + if (q->alias) { + UNSERIALIZE_STR(q->alias); + } + p++; } + } + + if (ce->trait_precedences) { + zend_trait_precedence **p, *q; + uint32_t j; + + UNSERIALIZE_PTR(ce->trait_precedences); + p = ce->trait_precedences; - for (j = 0; j < q->num_excludes; j++) { - UNSERIALIZE_STR(q->exclude_class_names[j]); + while (*p) { + UNSERIALIZE_PTR(*p); + q = *p; + + if (q->trait_method.method_name) { + UNSERIALIZE_STR(q->trait_method.method_name); + } + if (q->trait_method.class_name) { + UNSERIALIZE_STR(q->trait_method.class_name); + } + + for (j = 0; j < q->num_excludes; j++) { + UNSERIALIZE_STR(q->exclude_class_names[j]); + } + p++; } - p++; } } @@ -1329,6 +1469,22 @@ static void zend_file_cache_unserialize_class(zval *zv, ce->serialize = zend_class_serialize_deny; ce->unserialize = zend_class_unserialize_deny; } + + if (ce->iterator_funcs_ptr) { + UNSERIALIZE_PTR(ce->iterator_funcs_ptr); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next); + } + + if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + } } static void zend_file_cache_unserialize(zend_persistent_script *script, @@ -1395,7 +1551,7 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl efree(filename); return NULL; } - if (memcmp(info.system_id, ZCG(system_id), 32) != 0) { + if (memcmp(info.system_id, accel_system_id, 32) != 0) { zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); @@ -1492,6 +1648,7 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl goto use_process_mem; } memcpy(buf, mem, info.mem_size); + zend_map_ptr_extend(ZCSG(map_ptr_last)); } else { use_process_mem: buf = mem; @@ -1522,6 +1679,7 @@ use_process_mem: script->corrupted = 0; if (cache_it) { + ZCSG(map_ptr_last) = CG(map_ptr_last); script->dynamic_members.checksum = zend_accel_script_checksum(script); script->dynamic_members.last_used = ZCG(request_time); @@ -1546,5 +1704,3 @@ void zend_file_cache_invalidate(zend_string *full_path) zend_file_cache_unlink(filename); efree(filename); } - -#endif /* HAVE_OPCACHE_FILE_CACHE */ diff --git a/ext/opcache/zend_file_cache.h b/ext/opcache/zend_file_cache.h index f04a3af29f..eb71e2cef5 100644 --- a/ext/opcache/zend_file_cache.h +++ b/ext/opcache/zend_file_cache.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 9ddcad0551..dd4c199ef7 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -27,13 +27,8 @@ #include "zend_vm.h" #include "zend_constants.h" #include "zend_operators.h" +#include "zend_interfaces.h" -#define zend_accel_store(p, size) \ - (p = _zend_shared_memdup((void*)p, size, 1)) -#define zend_accel_memdup(p, size) \ - _zend_shared_memdup((void*)p, size, 0) - -#ifdef HAVE_OPCACHE_FILE_CACHE #define zend_set_str_gc_flags(str) do { \ if (file_cache_only) { \ GC_TYPE_INFO(str) = IS_STRING | (IS_STR_INTERNED << GC_FLAGS_SHIFT); \ @@ -41,11 +36,6 @@ GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \ } \ } while (0) -#else -#define zend_set_str_gc_flags(str) do {\ - GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \ -} while (0) -#endif #define zend_accel_store_string(str) do { \ zend_string *new_str = zend_shared_alloc_get_xlat_entry(str); \ @@ -53,18 +43,24 @@ zend_string_release_ex(str, 0); \ str = new_str; \ } else { \ - new_str = zend_accel_memdup((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ + new_str = zend_shared_memdup_put((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ zend_string_release_ex(str, 0); \ - str = new_str; \ - zend_string_hash_val(str); \ - zend_set_str_gc_flags(str); \ + str = new_str; \ + zend_string_hash_val(str); \ + zend_set_str_gc_flags(str); \ } \ } while (0) #define zend_accel_memdup_string(str) do { \ - str = zend_accel_memdup(str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ - zend_string_hash_val(str); \ - zend_set_str_gc_flags(str); \ - } while (0) + zend_string *new_str = zend_shared_alloc_get_xlat_entry(str); \ + if (new_str) { \ + str = new_str; \ + } else { \ + new_str = zend_shared_memdup_put((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ + str = new_str; \ + zend_string_hash_val(str); \ + zend_set_str_gc_flags(str); \ + } \ + } while (0) #define zend_accel_store_interned_string(str) do { \ if (!IS_ACCEL_INTERNED(str)) { \ zend_accel_store_string(str); \ @@ -83,15 +79,16 @@ static void zend_persist_zval(zval *z); static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; -static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement) +static void zend_hash_persist(HashTable *ht) { uint32_t idx, nIndex; Bucket *p; HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; ht->pDestructor = NULL; + ht->nInternalPointer = 0; - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { + if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); } else { @@ -107,26 +104,22 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement } else { HT_SET_DATA_ADDR(ht, &uninitialized_bucket); } - HT_FLAGS(ht) &= ~HASH_FLAG_INITIALIZED; + HT_FLAGS(ht) |= HASH_FLAG_UNINITIALIZED; return; } if (HT_FLAGS(ht) & HASH_FLAG_PACKED) { void *data = HT_GET_DATA_ADDR(ht); - zend_accel_store(data, HT_USED_SIZE(ht)); + data = zend_shared_memdup_free(data, HT_USED_SIZE(ht)); HT_SET_DATA_ADDR(ht, data); - } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { + } else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { /* compact table */ void *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; uint32_t hash_size; - if (ht->nNumUsed <= HT_MIN_SIZE) { - hash_size = HT_MIN_SIZE * 2; - } else { - hash_size = (uint32_t)(-(int32_t)ht->nTableMask); - while (hash_size >> 2 > ht->nNumUsed) { - hash_size >>= 1; - } + hash_size = (uint32_t)(-(int32_t)ht->nTableMask); + while (hash_size >> 2 > ht->nNumUsed) { + hash_size >>= 1; } ht->nTableMask = (uint32_t)(-(int32_t)hash_size); ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ @@ -136,23 +129,14 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); efree(old_data); + /* rehash */ for (idx = 0; idx < ht->nNumUsed; idx++) { p = ht->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - - /* persist bucket and key */ - if (p->key) { - zend_accel_store_interned_string(p->key); - } - - /* persist the data itself */ - pPersistElement(&p->val); - nIndex = p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); } - return; } else { void *data = ZCG(mem); void *old_data = HT_GET_DATA_ADDR(ht); @@ -163,109 +147,6 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement efree(old_data); HT_SET_DATA_ADDR(ht, data); } - - for (idx = 0; idx < ht->nNumUsed; idx++) { - p = ht->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - - /* persist bucket and key */ - if (p->key) { - zend_accel_store_interned_string(p->key); - } - - /* persist the data itself */ - pPersistElement(&p->val); - } -} - -static void zend_hash_persist_immutable(HashTable *ht) -{ - uint32_t idx, nIndex; - Bucket *p; - - HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; - ht->pDestructor = NULL; - - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { - if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { - HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); - } else { - HT_SET_DATA_ADDR(ht, &uninitialized_bucket); - } - return; - } - if (ht->nNumUsed == 0) { - efree(HT_GET_DATA_ADDR(ht)); - ht->nTableMask = HT_MIN_MASK; - if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { - HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); - } else { - HT_SET_DATA_ADDR(ht, &uninitialized_bucket); - } - HT_FLAGS(ht) &= ~HASH_FLAG_INITIALIZED; - return; - } - if (HT_FLAGS(ht) & HASH_FLAG_PACKED) { - HT_SET_DATA_ADDR(ht, zend_accel_memdup(HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht))); - } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { - /* compact table */ - void *old_data = HT_GET_DATA_ADDR(ht); - Bucket *old_buckets = ht->arData; - uint32_t hash_size; - - if (ht->nNumUsed <= HT_MIN_SIZE) { - hash_size = HT_MIN_SIZE * 2; - } else { - hash_size = (uint32_t)(-(int32_t)ht->nTableMask); - while (hash_size >> 2 > ht->nNumUsed) { - hash_size >>= 1; - } - } - ht->nTableMask = (uint32_t)(-(int32_t)hash_size); - ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ - HT_SET_DATA_ADDR(ht, ZCG(mem)); - ZCG(mem) = (void*)((char*)ZCG(mem) + (hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket))); - HT_HASH_RESET(ht); - memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); - efree(old_data); - - for (idx = 0; idx < ht->nNumUsed; idx++) { - p = ht->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - - /* persist bucket and key */ - if (p->key) { - zend_accel_memdup_interned_string(p->key); - } - - /* persist the data itself */ - zend_persist_zval(&p->val); - - nIndex = p->h | ht->nTableMask; - Z_NEXT(p->val) = HT_HASH(ht, nIndex); - HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); - } - return; - } else { - void *data = ZCG(mem); - - ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ - ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht))); - memcpy(data, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); - HT_SET_DATA_ADDR(ht, data); - } - for (idx = 0; idx < ht->nNumUsed; idx++) { - p = ht->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - - /* persist bucket and key */ - if (p->key) { - zend_accel_memdup_interned_string(p->key); - } - - /* persist the data itself */ - zend_persist_zval(&p->val); - } } static zend_ast *zend_persist_ast(zend_ast *ast) @@ -274,12 +155,12 @@ static zend_ast *zend_persist_ast(zend_ast *ast) zend_ast *node; if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) { - zend_ast_zval *copy = zend_accel_memdup(ast, sizeof(zend_ast_zval)); + zend_ast_zval *copy = zend_shared_memdup(ast, sizeof(zend_ast_zval)); zend_persist_zval(©->val); node = (zend_ast *) copy; } else if (zend_ast_is_list(ast)) { zend_ast_list *list = zend_ast_get_list(ast); - zend_ast_list *copy = zend_accel_memdup(ast, + zend_ast_list *copy = zend_shared_memdup(ast, sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children); for (i = 0; i < list->children; i++) { if (copy->child[i]) { @@ -289,7 +170,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast) node = (zend_ast *) copy; } else { uint32_t children = zend_ast_get_num_children(ast); - node = zend_accel_memdup(ast, sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children); + node = zend_shared_memdup(ast, sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children); for (i = 0; i < children; i++) { if (node->child[i]) { node->child[i] = zend_persist_ast(node->child[i]); @@ -315,13 +196,27 @@ static void zend_persist_zval(zval *z) Z_ARR_P(z) = new_ptr; Z_TYPE_FLAGS_P(z) = 0; } else { + Bucket *p; + if (!Z_REFCOUNTED_P(z)) { - Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array)); - zend_hash_persist_immutable(Z_ARRVAL_P(z)); + Z_ARR_P(z) = zend_shared_memdup_put(Z_ARR_P(z), sizeof(zend_array)); + zend_hash_persist(Z_ARRVAL_P(z)); + ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { + if (p->key) { + zend_accel_memdup_interned_string(p->key); + } + zend_persist_zval(&p->val); + } ZEND_HASH_FOREACH_END(); } else { GC_REMOVE_FROM_BUFFER(Z_ARR_P(z)); - zend_accel_store(Z_ARR_P(z), sizeof(zend_array)); - zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval); + Z_ARR_P(z) = zend_shared_memdup_put_free(Z_ARR_P(z), sizeof(zend_array)); + zend_hash_persist(Z_ARRVAL_P(z)); + ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { + if (p->key) { + zend_accel_store_interned_string(p->key); + } + zend_persist_zval(&p->val); + } ZEND_HASH_FOREACH_END(); /* make immutable array */ Z_TYPE_FLAGS_P(z) = 0; GC_SET_REFCOUNT(Z_COUNTED_P(z), 2); @@ -334,7 +229,7 @@ static void zend_persist_zval(zval *z) if (new_ptr) { Z_REF_P(z) = new_ptr; } else { - zend_accel_store(Z_REF_P(z), sizeof(zend_reference)); + Z_REF_P(z) = zend_shared_memdup_put_free(Z_REF_P(z), sizeof(zend_reference)); zend_persist_zval(Z_REFVAL_P(z)); } break; @@ -345,19 +240,22 @@ static void zend_persist_zval(zval *z) Z_TYPE_FLAGS_P(z) = 0; } else { zend_ast_ref *old_ref = Z_AST_P(z); - Z_ARR_P(z) = zend_accel_memdup(Z_AST_P(z), sizeof(zend_ast_ref)); + Z_AST_P(z) = zend_shared_memdup_put(Z_AST_P(z), sizeof(zend_ast_ref)); zend_persist_ast(GC_AST(old_ref)); Z_TYPE_FLAGS_P(z) = 0; GC_SET_REFCOUNT(Z_COUNTED_P(z), 1); efree(old_ref); } break; + default: + ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT); + ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE); + break; } } static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) { - int already_stored = 0; zend_op *persist_ptr; zval *orig_literals = NULL; @@ -380,50 +278,115 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc EG(current_execute_data) = orig_execute_data; } - if (op_array->static_variables) { - HashTable *stored = zend_shared_alloc_get_xlat_entry(op_array->static_variables); + if (op_array->scope) { + zend_class_entry *scope = zend_shared_alloc_get_xlat_entry(op_array->scope); - if (stored) { - op_array->static_variables = stored; - } else { - zend_hash_persist(op_array->static_variables, zend_persist_zval); - zend_accel_store(op_array->static_variables, sizeof(HashTable)); - /* make immutable array */ - GC_SET_REFCOUNT(op_array->static_variables, 2); - GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT); + if (scope) { + op_array->scope = scope; + } + if (op_array->prototype) { + zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype); + + if (ptr) { + op_array->prototype = ptr; + } } + + persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + if (persist_ptr) { + op_array->opcodes = persist_ptr; + if (op_array->static_variables) { + op_array->static_variables = zend_shared_alloc_get_xlat_entry(op_array->static_variables); + ZEND_ASSERT(op_array->static_variables != NULL); + } + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + if (op_array->literals) { + op_array->literals = zend_shared_alloc_get_xlat_entry(op_array->literals); + ZEND_ASSERT(op_array->literals != NULL); + } + if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) { + op_array->function_name = zend_shared_alloc_get_xlat_entry(op_array->function_name); + ZEND_ASSERT(op_array->function_name != NULL); + } + if (op_array->filename) { + op_array->filename = zend_shared_alloc_get_xlat_entry(op_array->filename); + ZEND_ASSERT(op_array->filename != NULL); + } + if (op_array->arg_info) { + zend_arg_info *arg_info = op_array->arg_info; + if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + arg_info--; + } + arg_info = zend_shared_alloc_get_xlat_entry(arg_info); + ZEND_ASSERT(arg_info != NULL); + if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + arg_info++; + } + op_array->arg_info = arg_info; + } + if (op_array->live_range) { + op_array->live_range = zend_shared_alloc_get_xlat_entry(op_array->live_range); + ZEND_ASSERT(op_array->live_range != NULL); + } + if (op_array->doc_comment) { + if (ZCG(accel_directives).save_comments) { + op_array->doc_comment = zend_shared_alloc_get_xlat_entry(op_array->doc_comment); + ZEND_ASSERT(op_array->doc_comment != NULL); + } else { + op_array->doc_comment = NULL; + } + } + if (op_array->try_catch_array) { + op_array->try_catch_array = zend_shared_alloc_get_xlat_entry(op_array->try_catch_array); + ZEND_ASSERT(op_array->try_catch_array != NULL); + } + if (op_array->vars) { + op_array->vars = zend_shared_alloc_get_xlat_entry(op_array->vars); + ZEND_ASSERT(op_array->vars != NULL); + } + ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); + return; + } + } else { + /* "prototype" may be undefined if "scope" isn't set */ + op_array->prototype = NULL; } - if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { - already_stored = 1; + if (op_array->static_variables) { + Bucket *p; + + zend_hash_persist(op_array->static_variables); + ZEND_HASH_FOREACH_BUCKET(op_array->static_variables, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_zval(&p->val); + } ZEND_HASH_FOREACH_END(); + op_array->static_variables = zend_shared_memdup_put_free(op_array->static_variables, sizeof(HashTable)); + /* make immutable array */ + GC_SET_REFCOUNT(op_array->static_variables, 2); + GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT); } + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); if (op_array->literals) { - if (already_stored) { - orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals); - ZEND_ASSERT(orig_literals != NULL); - op_array->literals = orig_literals; - } else { - zval *p = zend_accel_memdup(op_array->literals, sizeof(zval) * op_array->last_literal); - zval *end = p + op_array->last_literal; - orig_literals = op_array->literals; - op_array->literals = p; - while (p < end) { - zend_persist_zval(p); - p++; - } + zval *p, *end; + + orig_literals = op_array->literals; #if ZEND_USE_ABS_CONST_ADDR - efree(orig_literals); + p = zend_shared_memdup_put_free(op_array->literals, sizeof(zval) * op_array->last_literal); +#else + p = zend_shared_memdup_put(op_array->literals, sizeof(zval) * op_array->last_literal); #endif + end = p + op_array->last_literal; + op_array->literals = p; + while (p < end) { + zend_persist_zval(p); + p++; } } - if (already_stored) { - persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes); - ZEND_ASSERT(persist_ptr != NULL); - op_array->opcodes = persist_ptr; - } else { - zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last); + { + zend_op *new_opcodes = zend_shared_memdup_put(op_array->opcodes, sizeof(zend_op) * op_array->last); zend_op *opline = new_opcodes; zend_op *end = new_opcodes + op_array->last; int offset = 0; @@ -436,7 +399,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc || opline->opcode == ZEND_SEND_VAL_EX || opline->opcode == ZEND_QM_ASSIGN) { /* Update handlers to eliminate REFCOUNTED check */ - zend_vm_set_opcode_handler_ex(opline, 0, 0, 0); + zend_vm_set_opcode_handler_ex(opline, 1 << Z_TYPE_P(opline->op1.zv), 0, 0); } } if (opline->op2_type == IS_CONST) { @@ -490,8 +453,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes]; } break; - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_SWITCH_LONG: @@ -505,22 +466,11 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc efree(op_array->opcodes); op_array->opcodes = new_opcodes; - - if (op_array->run_time_cache) { - efree(op_array->run_time_cache); - op_array->run_time_cache = NULL; - } + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); } if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) { - zend_string *new_name; - if (already_stored) { - new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name); - ZEND_ASSERT(new_name != NULL); - op_array->function_name = new_name; - } else { - zend_accel_store_interned_string(op_array->function_name); - } + zend_accel_store_interned_string(op_array->function_name); } if (op_array->filename) { @@ -531,32 +481,26 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc if (op_array->arg_info) { zend_arg_info *arg_info = op_array->arg_info; uint32_t num_args = op_array->num_args; + uint32_t i; if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { arg_info--; num_args++; } - if (already_stored) { - arg_info = zend_shared_alloc_get_xlat_entry(arg_info); - ZEND_ASSERT(arg_info != NULL); - } else { - uint32_t i; - - if (op_array->fn_flags & ZEND_ACC_VARIADIC) { - num_args++; + if (op_array->fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + arg_info = zend_shared_memdup_put_free(arg_info, sizeof(zend_arg_info) * num_args); + for (i = 0; i < num_args; i++) { + if (arg_info[i].name) { + zend_accel_store_interned_string(arg_info[i].name); } - zend_accel_store(arg_info, sizeof(zend_arg_info) * num_args); - for (i = 0; i < num_args; i++) { - if (arg_info[i].name) { - zend_accel_store_interned_string(arg_info[i].name); - } - if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) { - zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type); - zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type); + if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) { + zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type); + zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type); - zend_accel_store_interned_string(type_name); - arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null); - } + zend_accel_store_interned_string(type_name); + arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null); } } if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { @@ -566,54 +510,28 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } if (op_array->live_range) { - zend_accel_store(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); - } - - if (op_array->scope) { - op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope); + op_array->live_range = zend_shared_memdup_put_free(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); } if (op_array->doc_comment) { if (ZCG(accel_directives).save_comments) { - if (already_stored) { - op_array->doc_comment = zend_shared_alloc_get_xlat_entry(op_array->doc_comment); - ZEND_ASSERT(op_array->doc_comment != NULL); - } else { - zend_accel_store_interned_string(op_array->doc_comment); - } + zend_accel_store_interned_string(op_array->doc_comment); } else { - if (!already_stored) { - zend_string_release_ex(op_array->doc_comment, 0); - } + zend_string_release_ex(op_array->doc_comment, 0); op_array->doc_comment = NULL; } } if (op_array->try_catch_array) { - zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + op_array->try_catch_array = zend_shared_memdup_put_free(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } if (op_array->vars) { - if (already_stored) { - persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars); - ZEND_ASSERT(persist_ptr != NULL); - op_array->vars = (zend_string**)persist_ptr; - } else { - int i; - zend_accel_store(op_array->vars, sizeof(zend_string*) * op_array->last_var); - for (i = 0; i < op_array->last_var; i++) { - zend_accel_store_interned_string(op_array->vars[i]); - } - } - } - - /* "prototype" may be undefined if "scope" isn't set */ - if (op_array->scope && op_array->prototype) { - if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { - op_array->prototype = (union _zend_function*)persist_ptr; + int i; + op_array->vars = zend_shared_memdup_put_free(op_array->vars, sizeof(zend_string*) * op_array->last_var); + for (i = 0; i < op_array->last_var; i++) { + zend_accel_store_interned_string(op_array->vars[i]); } - } else { - op_array->prototype = NULL; } ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); @@ -624,11 +542,19 @@ static void zend_persist_op_array(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); - memcpy(ZCG(mem), Z_PTR_P(zv), sizeof(zend_op_array)); - Z_PTR_P(zv) = ZCG(mem); - ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array))); - zend_persist_op_array_ex(Z_PTR_P(zv), NULL); - ((zend_op_array*)Z_PTR_P(zv))->fn_flags |= ZEND_ACC_IMMUTABLE; + op_array = Z_PTR_P(zv) = zend_shared_memdup(Z_PTR_P(zv), sizeof(zend_op_array)); + zend_persist_op_array_ex(op_array, NULL); + if (!ZCG(current_persistent_script)->corrupted) { + op_array->fn_flags |= ZEND_ACC_IMMUTABLE; + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + if (op_array->static_variables) { + ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); + } + } else { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem)); + ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*))); + ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); + } } static void zend_persist_class_method(zval *zv) @@ -636,7 +562,35 @@ static void zend_persist_class_method(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); zend_op_array *old_op_array; - ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); + if (op_array->type != ZEND_USER_FUNCTION) { + ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION); + if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) { + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); + if (old_op_array) { + Z_PTR_P(zv) = old_op_array; + } else { + if (ZCG(is_immutable_class)) { + op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_internal_function)); + } else { + op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_internal_function)); + } + if (op_array->scope) { + void *persist_ptr; + + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->scope))) { + op_array->scope = (zend_class_entry*)persist_ptr; + } + if (op_array->prototype) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { + op_array->prototype = (zend_function*)persist_ptr; + } + } + } + } + } + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (old_op_array) { Z_PTR_P(zv) = old_op_array; @@ -645,26 +599,43 @@ static void zend_persist_class_method(zval *zv) } return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_op_array)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array))); - zend_persist_op_array_ex(Z_PTR_P(zv), NULL); + if (ZCG(is_immutable_class)) { + op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_op_array)); + } else { + op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_op_array)); + } + zend_persist_op_array_ex(op_array, NULL); + if (ZCG(is_immutable_class)) { + op_array->fn_flags |= ZEND_ACC_IMMUTABLE; + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + if (op_array->static_variables) { + ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); + } + } else { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem)); + ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*))); + ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); + } } static void zend_persist_property_info(zval *zv) { zend_property_info *prop = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + zend_class_entry *ce; if (prop) { Z_PTR_P(zv) = prop; return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_property_info)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - prop = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_property_info))); - prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); + if (ZCG(is_immutable_class)) { + prop = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_property_info)); + } else { + prop = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_property_info)); + } + ce = zend_shared_alloc_get_xlat_entry(prop->ce); + if (ce) { + prop->ce = ce; + } zend_accel_store_interned_string(prop->name); if (prop->doc_comment) { if (ZCG(accel_directives).save_comments) { @@ -677,22 +648,33 @@ static void zend_persist_property_info(zval *zv) prop->doc_comment = NULL; } } + + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *class_name = ZEND_TYPE_NAME(prop->type); + zend_accel_store_interned_string(class_name); + prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } } static void zend_persist_class_constant(zval *zv) { zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + zend_class_entry *ce; if (c) { Z_PTR_P(zv) = c; return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - c = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant))); + if (ZCG(is_immutable_class)) { + c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); + } else { + c = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_class_constant)); + } zend_persist_zval(&c->value); - c->ce = zend_shared_alloc_get_xlat_entry(c->ce); + ce = zend_shared_alloc_get_xlat_entry(c->ce); + if (ce) { + c->ce = ce; + } if (c->doc_comment) { if (ZCG(accel_directives).save_comments) { zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); @@ -714,37 +696,72 @@ static void zend_persist_class_constant(zval *zv) static void zend_persist_class_entry(zval *zv) { + Bucket *p; zend_class_entry *ce = Z_PTR_P(zv); if (ce->type == ZEND_USER_CLASS) { - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_entry)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - ce = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_entry))); + /* The same zend_class_entry may be reused by class_alias */ + zend_class_entry *new_ce = zend_shared_alloc_get_xlat_entry(ce); + if (new_ce) { + Z_PTR_P(zv) = new_ce; + return; + } + if ((ce->ce_flags & ZEND_ACC_LINKED) + && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) + && (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) + && !ZCG(current_persistent_script)->corrupted) { + ZCG(is_immutable_class) = 1; + ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); + ce->ce_flags |= ZEND_ACC_IMMUTABLE; + } else { + ZCG(is_immutable_class) = 0; + ce = Z_PTR_P(zv) = zend_shared_memdup_arena_put(ce, sizeof(zend_class_entry)); + } zend_accel_store_interned_string(ce->name); - zend_hash_persist(&ce->function_table, zend_persist_class_method); + if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { + zend_accel_store_interned_string(ce->parent_name); + } + zend_hash_persist(&ce->function_table); + ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_class_method(&p->val); + } ZEND_HASH_FOREACH_END(); + HT_FLAGS(&ce->function_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); if (ce->default_properties_table) { int i; - zend_accel_store(ce->default_properties_table, sizeof(zval) * ce->default_properties_count); + ce->default_properties_table = zend_shared_memdup_free(ce->default_properties_table, sizeof(zval) * ce->default_properties_count); for (i = 0; i < ce->default_properties_count; i++) { zend_persist_zval(&ce->default_properties_table[i]); } } if (ce->default_static_members_table) { int i; - zend_accel_store(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count); + ce->default_static_members_table = zend_shared_memdup_free(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count); /* Persist only static properties in this class. * Static properties from parent classes will be handled in class_copy_ctor */ - i = ce->parent ? ce->parent->default_static_members_count : 0; + i = (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) ? ce->parent->default_static_members_count : 0; for (; i < ce->default_static_members_count; i++) { zend_persist_zval(&ce->default_static_members_table[i]); } + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + } + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } - ce->static_members_table = NULL; - zend_hash_persist(&ce->constants_table, zend_persist_class_constant); + zend_hash_persist(&ce->constants_table); + ZEND_HASH_FOREACH_BUCKET(&ce->constants_table, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_class_constant(&p->val); + } ZEND_HASH_FOREACH_END(); + HT_FLAGS(&ce->constants_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); if (ce->info.user.filename) { /* do not free! PHP has centralized filename storage, compiler will free it */ @@ -761,135 +778,284 @@ static void zend_persist_class_entry(zval *zv) ce->info.user.doc_comment = NULL; } } - zend_hash_persist(&ce->properties_info, zend_persist_property_info); - if (ce->num_interfaces && ce->interfaces) { - efree(ce->interfaces); + zend_hash_persist(&ce->properties_info); + ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_property_info(&p->val); + } ZEND_HASH_FOREACH_END(); + HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); + + if (ce->properties_info_table) { + int i; + + size_t size = sizeof(zend_property_info *) * ce->default_properties_count; + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED); + if (ZCG(is_immutable_class)) { + ce->properties_info_table = zend_shared_memdup( + ce->properties_info_table, size); + } else { + ce->properties_info_table = zend_shared_memdup_arena( + ce->properties_info_table, size); + } + + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->properties_info_table[i]) { + ce->properties_info_table[i] = zend_shared_alloc_get_xlat_entry( + ce->properties_info_table[i]); + } + } } - ce->interfaces = NULL; /* will be filled in on fetch */ - if (ce->num_traits && ce->traits) { - efree(ce->traits); + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { + uint32_t i = 0; + + for (i = 0; i < ce->num_interfaces; i++) { + zend_accel_store_interned_string(ce->interface_names[i].name); + zend_accel_store_interned_string(ce->interface_names[i].lc_name); + } + ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces); } - ce->traits = NULL; - if (ce->trait_aliases) { - int i = 0; - while (ce->trait_aliases[i]) { - if (ce->trait_aliases[i]->trait_method.method_name) { - zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.method_name); - } - if (ce->trait_aliases[i]->trait_method.class_name) { - zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.class_name); - } + if (ce->num_traits) { + uint32_t i = 0; - if (ce->trait_aliases[i]->alias) { - zend_accel_store_interned_string(ce->trait_aliases[i]->alias); + for (i = 0; i < ce->num_traits; i++) { + zend_accel_store_interned_string(ce->trait_names[i].name); + zend_accel_store_interned_string(ce->trait_names[i].lc_name); + } + ce->trait_names = zend_shared_memdup_free(ce->trait_names, sizeof(zend_class_name) * ce->num_traits); + + i = 0; + if (ce->trait_aliases) { + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method.method_name) { + zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.method_name); + } + if (ce->trait_aliases[i]->trait_method.class_name) { + zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.class_name); + } + + if (ce->trait_aliases[i]->alias) { + zend_accel_store_interned_string(ce->trait_aliases[i]->alias); + } + + ce->trait_aliases[i] = zend_shared_memdup_free(ce->trait_aliases[i], sizeof(zend_trait_alias)); + i++; } - zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias)); - i++; + ce->trait_aliases = zend_shared_memdup_free(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1)); } - zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1)); - } + if (ce->trait_precedences) { + uint32_t j; - if (ce->trait_precedences) { - int i = 0; - int j; + i = 0; + while (ce->trait_precedences[i]) { + zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.method_name); + zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.class_name); - while (ce->trait_precedences[i]) { - zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.method_name); - zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.class_name); + for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) { + zend_accel_store_interned_string(ce->trait_precedences[i]->exclude_class_names[j]); + } - for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) { - zend_accel_store_interned_string(ce->trait_precedences[i]->exclude_class_names[j]); + ce->trait_precedences[i] = zend_shared_memdup_free(ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); + i++; } - - zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); - i++; + ce->trait_precedences = zend_shared_memdup_free( + ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1)); } - zend_accel_store( - ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1)); + } + + if (ce->iterator_funcs_ptr) { + ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs)); } } } -//static int zend_update_property_info_ce(zval *zv) -//{ -// zend_property_info *prop = Z_PTR_P(zv); -// -// prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); -// return 0; -//} - -static int zend_update_parent_ce(zval *zv) +static void zend_update_parent_ce(zend_class_entry *ce) { - zend_class_entry *ce = Z_PTR_P(zv); + 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 (p) { + ce->parent = parent = p; + } + } + + /* 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]; + ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); + } + + parent = parent->parent; + } + } - if (ce->parent) { - ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent); + if (ce->num_interfaces) { + uint32_t i = 0; + + ce->interfaces = zend_shared_memdup_free(ce->interfaces, sizeof(zend_class_entry*) * ce->num_interfaces); + for (i = 0; i < ce->num_interfaces; i++) { + if (ce->interfaces[i]->type == ZEND_USER_CLASS) { + zend_class_entry *tmp = zend_shared_alloc_get_xlat_entry(ce->interfaces[i]); + if (tmp != NULL) { + ce->interfaces[i] = tmp; + } + } + } + } + + if (ce->iterator_funcs_ptr) { + memset(ce->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs)); + if (instanceof_function_ex(ce, zend_ce_aggregate, 1)) { + ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1); + } + if (instanceof_function_ex(ce, zend_ce_iterator, 1)) { + ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1); + ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1); + ce->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&ce->function_table, "key", sizeof("key") - 1); + ce->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&ce->function_table, "current", sizeof("current") - 1); + ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1); + } + } + } + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_CE(prop->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop->type); + if (ce->type == ZEND_USER_CLASS) { + ce = zend_shared_alloc_get_xlat_entry(ce); + if (ce) { + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } + } + } + } ZEND_HASH_FOREACH_END(); } /* update methods */ if (ce->constructor) { - ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor); + if (tmp != NULL) { + ce->constructor = tmp; + } } if (ce->destructor) { - ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->destructor); + if (tmp != NULL) { + ce->destructor = tmp; + } } if (ce->clone) { - ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->clone); + if (tmp != NULL) { + ce->clone = tmp; + } } if (ce->__get) { - ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__get); + if (tmp != NULL) { + ce->__get = tmp; + } } if (ce->__set) { - ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__set); + if (tmp != NULL) { + ce->__set = tmp; + } } if (ce->__call) { - ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__call); + if (tmp != NULL) { + ce->__call = tmp; + } } if (ce->serialize_func) { - ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + if (tmp != NULL) { + ce->serialize_func = tmp; + } } if (ce->unserialize_func) { - ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + if (tmp != NULL) { + ce->unserialize_func = tmp; + } } if (ce->__isset) { - ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__isset); + if (tmp != NULL) { + ce->__isset = tmp; + } } if (ce->__unset) { - ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__unset); + if (tmp != NULL) { + ce->__unset = tmp; + } } if (ce->__tostring) { - ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__tostring); + if (tmp != NULL) { + ce->__tostring = tmp; + } } if (ce->__callstatic) { - ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + if (tmp != NULL) { + ce->__callstatic = tmp; + } } if (ce->__debugInfo) { - ce->__debugInfo = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); + zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); + if (tmp != NULL) { + ce->__debugInfo = tmp; + } } -// zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce); - return 0; } static void zend_accel_persist_class_table(HashTable *class_table) { - zend_hash_persist(class_table, zend_persist_class_entry); - zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce); + Bucket *p; + zend_class_entry *ce; + + zend_hash_persist(class_table); + ZEND_HASH_FOREACH_BUCKET(class_table, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_class_entry(&p->val); + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_FOREACH_BUCKET(class_table, p) { + if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { + ce = Z_PTR(p->val); + zend_update_parent_ce(ce); + } + } ZEND_HASH_FOREACH_END(); } zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, const char **key, unsigned int key_length, int for_shm) { + Bucket *p; + script->mem = ZCG(mem); ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ - zend_shared_alloc_clear_xlat_table(); - zend_accel_store(script, sizeof(zend_persistent_script)); + script = zend_shared_memdup_free(script, sizeof(zend_persistent_script)); if (key && *key) { - *key = zend_accel_memdup(*key, key_length + 1); + *key = zend_shared_memdup_put((void*)*key, key_length + 1); } script->corrupted = 0; @@ -912,10 +1078,21 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script script->arena_mem = ZCG(arena_mem) = ZCG(mem); ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size); + zend_map_ptr_extend(ZCSG(map_ptr_last)); + zend_accel_persist_class_table(&script->script.class_table); - zend_hash_persist(&script->script.function_table, zend_persist_op_array); + zend_hash_persist(&script->script.function_table); + ZEND_HASH_FOREACH_BUCKET(&script->script.function_table, p) { + ZEND_ASSERT(p->key != NULL); + zend_accel_store_interned_string(p->key); + zend_persist_op_array(&p->val); + } ZEND_HASH_FOREACH_END(); zend_persist_op_array_ex(&script->script.main_op_array, script); + if (for_shm) { + ZCSG(map_ptr_last) = CG(map_ptr_last); + } + script->corrupted = 0; ZCG(current_persistent_script) = NULL; diff --git a/ext/opcache/zend_persist.h b/ext/opcache/zend_persist.h index d659b88f57..78b6b0f6d9 100644 --- a/ext/opcache/zend_persist.h +++ b/ext/opcache/zend_persist.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 5bb50434c6..dacc2376e8 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -28,12 +28,19 @@ #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) +#define ADD_ARENA_SIZE(m) ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m) -#define ADD_ARENA_SIZE(m) ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m) +#define ADD_SIZE_EX(m) do { \ + if (ZCG(is_immutable_class)) { \ + ADD_SIZE(m); \ + } else { \ + ADD_ARENA_SIZE(m); \ + } \ + } while (0) # define ADD_STRING(str) ADD_DUP_SIZE((str), _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))) -# define ADD_INTERNED_STRING(str, do_free) do { \ +# define ADD_INTERNED_STRING(str) do { \ if (ZCG(current_persistent_script)->corrupted) { \ ADD_STRING(str); \ } else if (!IS_ACCEL_INTERNED(str)) { \ @@ -48,43 +55,24 @@ static void zend_persist_zval_calc(zval *z); -static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval *pElement)) +static void zend_hash_persist_calc(HashTable *ht) { - uint32_t idx; - Bucket *p; - - if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED) || ht->nNumUsed == 0) { + if ((HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) || ht->nNumUsed == 0) { return; } - if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { + if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { /* compact table */ uint32_t hash_size; - if (ht->nNumUsed <= HT_MIN_SIZE) { - hash_size = HT_MIN_SIZE * 2; - } else { - hash_size = (uint32_t)(-(int32_t)ht->nTableMask); - while (hash_size >> 2 > ht->nNumUsed) { - hash_size >>= 1; - } + hash_size = (uint32_t)(-(int32_t)ht->nTableMask); + while (hash_size >> 2 > ht->nNumUsed) { + hash_size >>= 1; } ADD_SIZE(hash_size * sizeof(uint32_t) + ht->nNumUsed * sizeof(Bucket)); } else { ADD_SIZE(HT_USED_SIZE(ht)); } - - for (idx = 0; idx < ht->nNumUsed; idx++) { - p = ht->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - - /* persist bucket and key */ - if (p->key) { - ADD_INTERNED_STRING(p->key, 1); - } - - pPersistElement(&p->val); - } } static void zend_persist_ast_calc(zend_ast *ast) @@ -119,7 +107,7 @@ static void zend_persist_zval_calc(zval *z) switch (Z_TYPE_P(z)) { case IS_STRING: - ADD_INTERNED_STRING(Z_STR_P(z), 0); + ADD_INTERNED_STRING(Z_STR_P(z)); if (ZSTR_IS_INTERNED(Z_STR_P(z))) { Z_TYPE_FLAGS_P(z) = 0; } @@ -127,8 +115,16 @@ static void zend_persist_zval_calc(zval *z) case IS_ARRAY: size = zend_shared_memdup_size(Z_ARR_P(z), sizeof(zend_array)); if (size) { + Bucket *p; + ADD_SIZE(size); - zend_hash_persist_calc(Z_ARRVAL_P(z), zend_persist_zval_calc); + zend_hash_persist_calc(Z_ARRVAL_P(z)); + ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { + if (p->key) { + ADD_INTERNED_STRING(p->key); + } + zend_persist_zval_calc(&p->val); + } ZEND_HASH_FOREACH_END(); } break; case IS_REFERENCE: @@ -145,22 +141,16 @@ static void zend_persist_zval_calc(zval *z) zend_persist_ast_calc(Z_ASTVAL_P(z)); } break; + default: + ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT); + ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE); + break; } } static void zend_persist_op_array_calc_ex(zend_op_array *op_array) { - if (op_array->static_variables) { - if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) { - HashTable *old = op_array->static_variables; - - ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable)); - zend_hash_persist_calc(op_array->static_variables, zend_persist_zval_calc); - zend_shared_alloc_register_xlat_entry(old, op_array->static_variables); - } - } - - if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { + if (op_array->scope && zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { /* already stored */ if (op_array->function_name) { zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name); @@ -168,30 +158,45 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) op_array->function_name = new_name; } } + ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array))); return; } + if (op_array->static_variables) { + if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) { + Bucket *p; + + zend_shared_alloc_register_xlat_entry(op_array->static_variables, op_array->static_variables); + ADD_SIZE(sizeof(HashTable)); + zend_hash_persist_calc(op_array->static_variables); + ZEND_HASH_FOREACH_BUCKET(op_array->static_variables, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_zval_calc(&p->val); + } ZEND_HASH_FOREACH_END(); + } + } + if (op_array->literals) { zval *p = op_array->literals; zval *end = p + op_array->last_literal; - ADD_DUP_SIZE(op_array->literals, sizeof(zval) * op_array->last_literal); + ADD_SIZE(sizeof(zval) * op_array->last_literal); while (p < end) { zend_persist_zval_calc(p); p++; } } - ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last); + zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes); + ADD_SIZE(sizeof(zend_op) * op_array->last); if (op_array->function_name) { zend_string *old_name = op_array->function_name; - zend_string *new_name = zend_shared_alloc_get_xlat_entry(old_name); - - if (new_name) { - op_array->function_name = new_name; - } else { - ADD_INTERNED_STRING(op_array->function_name, 0); - zend_shared_alloc_register_xlat_entry(old_name, op_array->function_name); + if (!zend_shared_alloc_get_xlat_entry(old_name)) { + ADD_INTERNED_STRING(op_array->function_name); + if (!zend_shared_alloc_get_xlat_entry(op_array->function_name)) { + zend_shared_alloc_register_xlat_entry(old_name, op_array->function_name); + } } } @@ -212,23 +217,23 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) arg_info--; num_args++; } - ADD_DUP_SIZE(arg_info, sizeof(zend_arg_info) * num_args); + ADD_SIZE(sizeof(zend_arg_info) * num_args); for (i = 0; i < num_args; i++) { if (arg_info[i].name) { - ADD_INTERNED_STRING(arg_info[i].name, 1); + ADD_INTERNED_STRING(arg_info[i].name); } if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) { zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type); zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type); - ADD_INTERNED_STRING(type_name, 1); + ADD_INTERNED_STRING(type_name); arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null); } } } if (op_array->live_range) { - ADD_DUP_SIZE(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); + ADD_SIZE(sizeof(zend_live_range) * op_array->last_live_range); } if (ZCG(accel_directives).save_comments && op_array->doc_comment) { @@ -236,15 +241,15 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } if (op_array->try_catch_array) { - ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + ADD_SIZE(sizeof(zend_try_catch_element) * op_array->last_try_catch); } if (op_array->vars) { int i; - ADD_DUP_SIZE(op_array->vars, sizeof(zend_string*) * op_array->last_var); + ADD_SIZE(sizeof(zend_string*) * op_array->last_var); for (i = 0; i < op_array->last_var; i++) { - ADD_INTERNED_STRING(op_array->vars[i], 0); + ADD_INTERNED_STRING(op_array->vars[i]); } } @@ -258,6 +263,9 @@ static void zend_persist_op_array_calc(zval *zv) ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); ADD_SIZE(sizeof(zend_op_array)); zend_persist_op_array_calc_ex(Z_PTR_P(zv)); + if (ZCG(current_persistent_script)->corrupted) { + ADD_ARENA_SIZE(sizeof(void*)); + } } static void zend_persist_class_method_calc(zval *zv) @@ -265,14 +273,26 @@ static void zend_persist_class_method_calc(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); zend_op_array *old_op_array; - ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); + if (op_array->type != ZEND_USER_FUNCTION) { + ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION); + if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) { + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); + if (!old_op_array) { + ADD_SIZE_EX(sizeof(zend_internal_function)); + zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv)); + } + } + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); - if (old_op_array) { - Z_PTR_P(zv) = old_op_array; - } else { - ADD_ARENA_SIZE(sizeof(zend_op_array)); + if (!old_op_array) { + ADD_SIZE_EX(sizeof(zend_op_array)); zend_persist_op_array_calc_ex(Z_PTR_P(zv)); zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv)); + if (!ZCG(is_immutable_class)) { + ADD_ARENA_SIZE(sizeof(void*)); + } } } @@ -282,8 +302,13 @@ static void zend_persist_property_info_calc(zval *zv) if (!zend_shared_alloc_get_xlat_entry(prop)) { zend_shared_alloc_register_xlat_entry(prop, prop); - ADD_ARENA_SIZE(sizeof(zend_property_info)); - ADD_INTERNED_STRING(prop->name, 0); + ADD_SIZE_EX(sizeof(zend_property_info)); + ADD_INTERNED_STRING(prop->name); + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *class_name = ZEND_TYPE_NAME(prop->type); + ADD_INTERNED_STRING(class_name); + prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } @@ -296,7 +321,7 @@ static void zend_persist_class_constant_calc(zval *zv) if (!zend_shared_alloc_get_xlat_entry(c)) { zend_shared_alloc_register_xlat_entry(c, c); - ADD_ARENA_SIZE(sizeof(zend_class_constant)); + ADD_SIZE_EX(sizeof(zend_class_constant)); zend_persist_zval_calc(&c->value); if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); @@ -304,15 +329,54 @@ static void zend_persist_class_constant_calc(zval *zv) } } +static void check_property_type_resolution(zend_class_entry *ce) { + zend_property_info *prop; + if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) { + /* Preloading might have computed this already. */ + return; + } + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + return; + } + } ZEND_HASH_FOREACH_END(); + } + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; +} static void zend_persist_class_entry_calc(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); + Bucket *p; if (ce->type == ZEND_USER_CLASS) { - ADD_ARENA_SIZE(sizeof(zend_class_entry)); - ADD_INTERNED_STRING(ce->name, 0); - zend_hash_persist_calc(&ce->function_table, zend_persist_class_method_calc); + /* The same zend_class_entry may be reused by class_alias */ + if (zend_shared_alloc_get_xlat_entry(ce)) { + return; + } + zend_shared_alloc_register_xlat_entry(ce, ce); + + check_property_type_resolution(ce); + + ZCG(is_immutable_class) = + (ce->ce_flags & ZEND_ACC_LINKED) && + (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) && + (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) && + !ZCG(current_persistent_script)->corrupted; + + ADD_SIZE_EX(sizeof(zend_class_entry)); + ADD_INTERNED_STRING(ce->name); + if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { + ADD_INTERNED_STRING(ce->parent_name); + } + zend_hash_persist_calc(&ce->function_table); + ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_class_method_calc(&p->val); + } ZEND_HASH_FOREACH_END(); if (ce->default_properties_table) { int i; @@ -331,7 +395,12 @@ static void zend_persist_class_entry_calc(zval *zv) } } } - zend_hash_persist_calc(&ce->constants_table, zend_persist_class_constant_calc); + zend_hash_persist_calc(&ce->constants_table); + ZEND_HASH_FOREACH_BUCKET(&ce->constants_table, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_class_constant_calc(&p->val); + } ZEND_HASH_FOREACH_END(); if (ce->info.user.filename) { ADD_STRING(ce->info.user.filename); @@ -340,53 +409,99 @@ static void zend_persist_class_entry_calc(zval *zv) ADD_STRING(ce->info.user.doc_comment); } - zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc); + zend_hash_persist_calc(&ce->properties_info); + ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_property_info_calc(&p->val); + } ZEND_HASH_FOREACH_END(); - if (ce->trait_aliases) { - int i = 0; - while (ce->trait_aliases[i]) { - if (ce->trait_aliases[i]->trait_method.method_name) { - ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.method_name, 0); - } - if (ce->trait_aliases[i]->trait_method.class_name) { - ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.class_name, 0); - } + if (ce->properties_info_table) { + ADD_SIZE_EX(sizeof(zend_property_info *) * ce->default_properties_count); + } + + if (ce->num_interfaces) { + uint32_t i; - if (ce->trait_aliases[i]->alias) { - ADD_INTERNED_STRING(ce->trait_aliases[i]->alias, 0); + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + for (i = 0; i < ce->num_interfaces; i++) { + ADD_INTERNED_STRING(ce->interface_names[i].name); + ADD_INTERNED_STRING(ce->interface_names[i].lc_name); } - ADD_SIZE(sizeof(zend_trait_alias)); - i++; + ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces); + } else { + ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces); } - ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1)); } - if (ce->trait_precedences) { - int i = 0; - int j; + if (ce->num_traits) { + uint32_t i; + + for (i = 0; i < ce->num_traits; i++) { + ADD_INTERNED_STRING(ce->trait_names[i].name); + ADD_INTERNED_STRING(ce->trait_names[i].lc_name); + } + ADD_SIZE(sizeof(zend_class_name) * ce->num_traits); + + if (ce->trait_aliases) { + i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method.method_name) { + ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.method_name); + } + if (ce->trait_aliases[i]->trait_method.class_name) { + ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.class_name); + } + + if (ce->trait_aliases[i]->alias) { + ADD_INTERNED_STRING(ce->trait_aliases[i]->alias); + } + ADD_SIZE(sizeof(zend_trait_alias)); + i++; + } + ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1)); + } - while (ce->trait_precedences[i]) { - ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.method_name, 0); - ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.class_name, 0); + if (ce->trait_precedences) { + int j; - for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) { - ADD_INTERNED_STRING(ce->trait_precedences[i]->exclude_class_names[j], 0); + i = 0; + while (ce->trait_precedences[i]) { + ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.method_name); + ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.class_name); + + for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) { + ADD_INTERNED_STRING(ce->trait_precedences[i]->exclude_class_names[j]); + } + ADD_SIZE(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); + i++; } - ADD_SIZE(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); - i++; + ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1)); } - ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1)); + } + + if (ce->iterator_funcs_ptr) { + ADD_SIZE(sizeof(zend_class_iterator_funcs)); } } } static void zend_accel_persist_class_table_calc(HashTable *class_table) { - zend_hash_persist_calc(class_table, zend_persist_class_entry_calc); + Bucket *p; + + zend_hash_persist_calc(class_table); + ZEND_HASH_FOREACH_BUCKET(class_table, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_class_entry_calc(&p->val); + } ZEND_HASH_FOREACH_END(); } uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, const char *key, unsigned int key_length, int for_shm) { + Bucket *p; + new_persistent_script->mem = NULL; new_persistent_script->size = 0; new_persistent_script->arena_mem = NULL; @@ -399,9 +514,10 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s new_persistent_script->corrupted = 1; } - ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script)); + ADD_SIZE(sizeof(zend_persistent_script)); if (key) { - ADD_DUP_SIZE(key, key_length + 1); + ADD_SIZE(key_length + 1); + zend_shared_alloc_register_xlat_entry(key, key); } ADD_STRING(new_persistent_script->script.filename); @@ -417,7 +533,12 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s if (new_persistent_script->script.function_table.nNumUsed != new_persistent_script->script.function_table.nNumOfElements) { zend_hash_rehash(&new_persistent_script->script.function_table); } - zend_hash_persist_calc(&new_persistent_script->script.function_table, zend_persist_op_array_calc); + zend_hash_persist_calc(&new_persistent_script->script.function_table); + ZEND_HASH_FOREACH_BUCKET(&new_persistent_script->script.function_table, p) { + ZEND_ASSERT(p->key != NULL); + ADD_INTERNED_STRING(p->key); + zend_persist_op_array_calc(&p->val); + } ZEND_HASH_FOREACH_END(); zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array); #if defined(__AVX__) || defined(__SSE2__) diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 3db3c0106a..8e75cda333 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -28,7 +28,6 @@ #include <fcntl.h> #ifndef ZEND_WIN32 # include <sys/types.h> -# include <dirent.h> # include <signal.h> # include <sys/stat.h> # include <stdio.h> @@ -260,6 +259,7 @@ int zend_shared_alloc_startup(size_t requested_size) void zend_shared_alloc_shutdown(void) { zend_shared_segment **tmp_shared_segments; + zend_shared_segment *shared_segments_buf[16]; size_t shared_segments_array_size; zend_smm_shared_globals tmp_shared_globals; int i; @@ -267,18 +267,28 @@ void zend_shared_alloc_shutdown(void) tmp_shared_globals = *smm_shared_globals; smm_shared_globals = &tmp_shared_globals; shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *)); - tmp_shared_segments = emalloc(shared_segments_array_size); + if (shared_segments_array_size > 16) { + tmp_shared_segments = malloc(shared_segments_array_size); + } else { + tmp_shared_segments = shared_segments_buf; + } copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)()); ZSMMG(shared_segments) = tmp_shared_segments; for (i = 0; i < ZSMMG(shared_segments_count); i++) { S_H(detach_segment)(ZSMMG(shared_segments)[i]); } - efree(ZSMMG(shared_segments)); + if (shared_segments_array_size > 16) { + free(ZSMMG(shared_segments)); + } ZSMMG(shared_segments) = NULL; g_shared_alloc_handler = NULL; #ifndef ZEND_WIN32 close(lock_file); + +# ifdef ZTS + tsrm_mutex_free(zts_lock); +# endif #endif } @@ -344,30 +354,84 @@ int zend_shared_memdup_size(void *source, size_t size) /* we already duplicated this pointer */ return 0; } - zend_shared_alloc_register_xlat_entry(source, source); + zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, source); return ZEND_ALIGNED_SIZE(size); } -void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source) +static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, zend_bool arena, zend_bool get_xlat, zend_bool set_xlat, zend_bool free_source) { void *old_p, *retval; - zend_ulong key = (zend_ulong)source; - - key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key = _rotr(key, 3);*/ - if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) { - /* we already duplicated this pointer */ - return old_p; + zend_ulong key; + + if (get_xlat) { + key = (zend_ulong)source; + key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key = _rotr(key, 3);*/ + if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) { + /* we already duplicated this pointer */ + return old_p; + } + } + if (arena) { + retval = ZCG(arena_mem); + ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(size)); + } else { + retval = ZCG(mem); + ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); } - retval = ZCG(mem); - ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); memcpy(retval, source, size); - zend_shared_alloc_register_xlat_entry(source, retval); + if (set_xlat) { + if (!get_xlat) { + key = (zend_ulong)source; + key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key = _rotr(key, 3);*/ + } + zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, retval); + } if (free_source) { efree(source); } return retval; } +void *zend_shared_memdup_get_put_free(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 1, 1, 1); +} + +void *zend_shared_memdup_put_free(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 0, 1, 1); +} + +void *zend_shared_memdup_free(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 0, 0, 1); +} + +void *zend_shared_memdup_get_put(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 1, 1, 0); +} + +void *zend_shared_memdup_put(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 0, 1, 0); +} + +void *zend_shared_memdup(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 0, 0, 0, 0); +} + +void *zend_shared_memdup_arena_put(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 1, 0, 1, 0); +} + +void *zend_shared_memdup_arena(void *source, size_t size) +{ + return _zend_shared_memdup(source, size, 1, 0, 0, 0); +} + void zend_shared_alloc_safe_unlock(void) { if (ZCG(locked)) { @@ -454,6 +518,16 @@ void zend_shared_alloc_clear_xlat_table(void) zend_hash_clean(&ZCG(xlat_table)); } +uint32_t zend_shared_alloc_checkpoint_xlat_table(void) +{ + return ZCG(xlat_table).nNumUsed; +} + +void zend_shared_alloc_restore_xlat_table(uint32_t checkpoint) +{ + zend_hash_discard(&ZCG(xlat_table), checkpoint); +} + void zend_shared_alloc_register_xlat_entry(const void *old, const void *new) { zend_ulong key = (zend_ulong)old; @@ -524,6 +598,25 @@ void zend_accel_shared_protect(int mode) for (i = 0; i < ZSMMG(shared_segments_count); i++) { mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode); } +#elif defined(ZEND_WIN32) + int i; + + if (!smm_shared_globals) { + return; + } + + if (mode) { + mode = PAGE_READONLY; + } else { + mode = PAGE_READWRITE; + } + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + DWORD oldProtect; + if (!VirtualProtect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode, &oldProtect)) { + zend_accel_error(ACCEL_LOG_ERROR, "Failed to protect memory"); + } + } #endif } diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index b41f79b6e5..2a77dfbef7 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend OPcache | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 The PHP Group | + | 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 | @@ -33,6 +33,9 @@ # define USE_MMAP 1 # endif #elif defined(__linux__) || defined(_AIX) +# ifdef HAVE_SHM_MMAP_POSIX +# define USE_SHM_OPEN 1 +# endif # ifdef HAVE_SHM_IPC # define USE_SHM 1 # endif @@ -125,7 +128,15 @@ void zend_shared_alloc_shutdown(void); void *zend_shared_alloc(size_t size); /* copy into shared memory */ -void *_zend_shared_memdup(void *p, size_t size, zend_bool free_source); +void *zend_shared_memdup_get_put_free(void *source, size_t size); +void *zend_shared_memdup_put_free(void *source, size_t size); +void *zend_shared_memdup_free(void *source, size_t size); +void *zend_shared_memdup_get_put(void *source, size_t size); +void *zend_shared_memdup_put(void *source, size_t size); +void *zend_shared_memdup(void *source, size_t size); +void *zend_shared_memdup_arena_put(void *source, size_t size); +void *zend_shared_memdup_arena(void *source, size_t size); + int zend_shared_memdup_size(void *p, size_t size); int zend_accel_in_shm(void *ptr); @@ -154,6 +165,8 @@ void zend_shared_alloc_safe_unlock(void); void zend_shared_alloc_init_xlat_table(void); void zend_shared_alloc_destroy_xlat_table(void); void zend_shared_alloc_clear_xlat_table(void); +uint32_t zend_shared_alloc_checkpoint_xlat_table(void); +void zend_shared_alloc_restore_xlat_table(uint32_t checkpoint); void zend_shared_alloc_register_xlat_entry(const void *old, const void *new); void *zend_shared_alloc_get_xlat_entry(const void *old); |