summaryrefslogtreecommitdiff
path: root/ext/opcache
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache')
-rw-r--r--ext/opcache/Optimizer/block_pass.c145
-rw-r--r--ext/opcache/Optimizer/compact_literals.c175
-rw-r--r--ext/opcache/Optimizer/compact_vars.c11
-rw-r--r--ext/opcache/Optimizer/dce.c151
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c191
-rw-r--r--ext/opcache/Optimizer/escape_analysis.c57
-rw-r--r--ext/opcache/Optimizer/nop_removal.c8
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c10
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c16
-rw-r--r--ext/opcache/Optimizer/pass1_5.c49
-rw-r--r--ext/opcache/Optimizer/pass2.c57
-rw-r--r--ext/opcache/Optimizer/pass3.c45
-rw-r--r--ext/opcache/Optimizer/sccp.c265
-rw-r--r--ext/opcache/Optimizer/scdf.c35
-rw-r--r--ext/opcache/Optimizer/scdf.h2
-rw-r--r--ext/opcache/Optimizer/ssa_integrity.c2
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.c20
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.h10
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c118
-rw-r--r--ext/opcache/Optimizer/zend_cfg.h19
-rw-r--r--ext/opcache/Optimizer/zend_dfg.c35
-rw-r--r--ext/opcache/Optimizer/zend_dfg.h10
-rw-r--r--ext/opcache/Optimizer/zend_dump.c71
-rw-r--r--ext/opcache/Optimizer/zend_dump.h11
-rw-r--r--ext/opcache/Optimizer/zend_func_info.c121
-rw-r--r--ext/opcache/Optimizer/zend_func_info.h14
-rw-r--r--ext/opcache/Optimizer/zend_inference.c742
-rw-r--r--ext/opcache/Optimizer/zend_inference.h18
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c449
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.h2
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h11
-rw-r--r--ext/opcache/Optimizer/zend_ssa.c65
-rw-r--r--ext/opcache/Optimizer/zend_ssa.h14
-rw-r--r--ext/opcache/Optimizer/zend_worklist.h10
-rw-r--r--ext/opcache/README215
-rw-r--r--ext/opcache/ZendAccelerator.c2125
-rw-r--r--ext/opcache/ZendAccelerator.h33
-rw-r--r--ext/opcache/config.m4171
-rw-r--r--ext/opcache/config.w326
-rw-r--r--ext/opcache/shared_alloc_mmap.c11
-rw-r--r--ext/opcache/shared_alloc_posix.c2
-rw-r--r--ext/opcache/shared_alloc_shm.c17
-rw-r--r--ext/opcache/shared_alloc_win32.c100
-rw-r--r--ext/opcache/tests/blacklist-win32.phpt4
-rw-r--r--ext/opcache/tests/blacklist.phpt20
-rw-r--r--ext/opcache/tests/bug64353.phpt1
-rw-r--r--ext/opcache/tests/bug65510.phpt1
-rw-r--r--ext/opcache/tests/bug66338.phpt8
-rw-r--r--ext/opcache/tests/bug68644.phpt1
-rw-r--r--ext/opcache/tests/bug69549.phpt (renamed from ext/opcache/tests/bug695449.phpt)0
-rw-r--r--ext/opcache/tests/bug78014.inc11
-rw-r--r--ext/opcache/tests/bug78014.phpt29
-rw-r--r--ext/opcache/tests/bug78034.phpt23
-rw-r--r--ext/opcache/tests/bug78106.phpt2
-rw-r--r--ext/opcache/tests/bug78175.phpt17
-rw-r--r--ext/opcache/tests/bug78175_2.phpt22
-rw-r--r--ext/opcache/tests/bug78376.phpt18
-rw-r--r--ext/opcache/tests/bug78429.phpt13
-rw-r--r--ext/opcache/tests/bug78937_1.phpt24
-rw-r--r--ext/opcache/tests/bug78937_2.phpt25
-rw-r--r--ext/opcache/tests/bug78937_3.phpt27
-rw-r--r--ext/opcache/tests/bug78937_4.phpt25
-rw-r--r--ext/opcache/tests/bug78937_5.phpt26
-rw-r--r--ext/opcache/tests/bug78937_6.phpt28
-rw-r--r--ext/opcache/tests/bug78961.phpt17
-rw-r--r--ext/opcache/tests/bug78986.phpt28
-rw-r--r--ext/opcache/tests/bug79193.phpt18
-rw-r--r--ext/opcache/tests/issue0115.phpt2
-rw-r--r--ext/opcache/tests/issue0140.phpt2
-rw-r--r--ext/opcache/tests/issue0149.phpt2
-rw-r--r--ext/opcache/tests/log_verbosity_bug.phpt5
-rw-r--r--ext/opcache/tests/opt/dce_001.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_002.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_003.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_004.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_005.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_006.phpt3
-rw-r--r--ext/opcache/tests/opt/dce_007.phpt1
-rw-r--r--ext/opcache/tests/opt/dce_008.phpt1
-rw-r--r--ext/opcache/tests/opt/prop_types.phpt116
-rw-r--r--ext/opcache/tests/opt/sccp_001.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_002.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_003.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_004.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_005.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_006.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_007.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_008.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_009.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_010.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_011.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_012.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_016.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_017.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_019.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_022.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_024.phpt1
-rw-r--r--ext/opcache/tests/opt/sccp_026.phpt38
-rw-r--r--ext/opcache/tests/opt/sccp_027.phpt27
-rw-r--r--ext/opcache/tests/opt/sccp_028.phpt25
-rw-r--r--ext/opcache/tests/opt/sccp_029.phpt27
-rw-r--r--ext/opcache/tests/optimize_static_001.phpt19
-rw-r--r--ext/opcache/tests/phi_remove_001.phpt2
-rw-r--r--ext/opcache/tests/php_cli_server.inc30
-rw-r--r--ext/opcache/tests/preload.inc58
-rw-r--r--ext/opcache/tests/preload_001.phpt27
-rw-r--r--ext/opcache/tests/preload_002.phpt22
-rw-r--r--ext/opcache/tests/preload_003.phpt22
-rw-r--r--ext/opcache/tests/preload_004.phpt20
-rw-r--r--ext/opcache/tests/preload_005.phpt19
-rw-r--r--ext/opcache/tests/preload_006.phpt19
-rw-r--r--ext/opcache/tests/preload_007.phpt18
-rw-r--r--ext/opcache/tests/preload_008.phpt19
-rw-r--r--ext/opcache/tests/preload_009.phpt21
-rw-r--r--ext/opcache/tests/preload_010.phpt18
-rw-r--r--ext/opcache/tests/preload_011.phpt33
-rw-r--r--ext/opcache/tests/preload_012.phpt17
-rw-r--r--ext/opcache/tests/preload_013.phpt21
-rw-r--r--ext/opcache/tests/preload_bug78014.inc2
-rw-r--r--ext/opcache/tests/preload_bug78175.inc4
-rw-r--r--ext/opcache/tests/preload_bug78175_2.inc23
-rw-r--r--ext/opcache/tests/preload_bug78376.inc6
-rw-r--r--ext/opcache/tests/preload_bug78937.inc8
-rw-r--r--ext/opcache/tests/preload_bug79114.phpt28
-rw-r--r--ext/opcache/tests/preload_class_alias.inc3
-rw-r--r--ext/opcache/tests/preload_class_alias.phpt20
-rw-r--r--ext/opcache/tests/preload_class_alias_2.inc4
-rw-r--r--ext/opcache/tests/preload_class_alias_2.phpt20
-rw-r--r--ext/opcache/tests/preload_const_autoload.inc7
-rw-r--r--ext/opcache/tests/preload_const_autoload_2.inc5
-rw-r--r--ext/opcache/tests/preload_globals.inc5
-rw-r--r--ext/opcache/tests/preload_include.inc7
-rw-r--r--ext/opcache/tests/preload_include_dummy.inc3
-rw-r--r--ext/opcache/tests/preload_inheritance_error.inc9
-rw-r--r--ext/opcache/tests/preload_inheritance_error_ind.inc2
-rw-r--r--ext/opcache/tests/preload_loadable_classes_1.inc18
-rw-r--r--ext/opcache/tests/preload_loadable_classes_1.phpt22
-rw-r--r--ext/opcache/tests/preload_loadable_classes_2.inc6
-rw-r--r--ext/opcache/tests/preload_loadable_classes_2.phpt20
-rw-r--r--ext/opcache/tests/preload_loadable_classes_3.inc5
-rw-r--r--ext/opcache/tests/preload_loadable_classes_3.phpt16
-rw-r--r--ext/opcache/tests/preload_nested_function.inc7
-rw-r--r--ext/opcache/tests/preload_overwritten_prop_init.inc10
-rw-r--r--ext/opcache/tests/preload_static_var_inheritance.inc9
-rw-r--r--ext/opcache/tests/preload_static_var_inheritance.phpt18
-rw-r--r--ext/opcache/tests/preload_trait_static.inc12
-rw-r--r--ext/opcache/tests/preload_trait_static.phpt19
-rw-r--r--ext/opcache/tests/preload_undef_const.inc4
-rw-r--r--ext/opcache/tests/preload_undef_const_2.inc11
-rw-r--r--ext/opcache/tests/preload_unresolved_prop_type.inc2
-rw-r--r--ext/opcache/tests/preload_unresolved_prop_type.phpt17
-rw-r--r--ext/opcache/tests/preload_unresolved_prop_type_2.inc5
-rw-r--r--ext/opcache/tests/preload_variance.inc43
-rw-r--r--ext/opcache/tests/preload_variance_ind.inc2
-rw-r--r--ext/opcache/tests/preload_windows.phpt16
-rw-r--r--ext/opcache/tests/revalidate_path_01.phpt6
-rw-r--r--ext/opcache/tests/sccp_loop_var_free.phpt18
-rw-r--r--ext/opcache/tests/verify_return_instanceof.phpt19
-rw-r--r--ext/opcache/tests/zzz_basic_logging.phpt1
-rw-r--r--ext/opcache/zend_accelerator_blacklist.c14
-rw-r--r--ext/opcache/zend_accelerator_blacklist.h2
-rw-r--r--ext/opcache/zend_accelerator_debug.c2
-rw-r--r--ext/opcache/zend_accelerator_debug.h2
-rw-r--r--ext/opcache/zend_accelerator_hash.c2
-rw-r--r--ext/opcache/zend_accelerator_hash.h2
-rw-r--r--ext/opcache/zend_accelerator_module.c119
-rw-r--r--ext/opcache/zend_accelerator_module.h2
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c483
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.h15
-rw-r--r--ext/opcache/zend_file_cache.c432
-rw-r--r--ext/opcache/zend_file_cache.h2
-rw-r--r--ext/opcache/zend_persist.c887
-rw-r--r--ext/opcache/zend_persist.h2
-rw-r--r--ext/opcache/zend_persist_calc.c319
-rw-r--r--ext/opcache/zend_shared_alloc.c123
-rw-r--r--ext/opcache/zend_shared_alloc.h17
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(&copy->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);