summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
Diffstat (limited to 'Zend')
-rw-r--r--Zend/Optimizer/block_pass.c1937
-rw-r--r--Zend/Optimizer/compact_literals.c839
-rw-r--r--Zend/Optimizer/compact_vars.c123
-rw-r--r--Zend/Optimizer/dce.c628
-rw-r--r--Zend/Optimizer/dfa_pass.c1637
-rw-r--r--Zend/Optimizer/escape_analysis.c539
-rw-r--r--Zend/Optimizer/nop_removal.c106
-rw-r--r--Zend/Optimizer/optimize_func_calls.c337
-rw-r--r--Zend/Optimizer/optimize_temp_vars_5.c187
-rw-r--r--Zend/Optimizer/pass1.c685
-rw-r--r--Zend/Optimizer/pass3.c356
-rw-r--r--Zend/Optimizer/sccp.c2483
-rw-r--r--Zend/Optimizer/scdf.c229
-rw-r--r--Zend/Optimizer/scdf.h99
-rw-r--r--Zend/Optimizer/ssa_integrity.c378
-rw-r--r--Zend/Optimizer/zend_call_graph.c271
-rw-r--r--Zend/Optimizer/zend_call_graph.h69
-rw-r--r--Zend/Optimizer/zend_cfg.c909
-rw-r--r--Zend/Optimizer/zend_cfg.h127
-rw-r--r--Zend/Optimizer/zend_dfg.c333
-rw-r--r--Zend/Optimizer/zend_dfg.h51
-rw-r--r--Zend/Optimizer/zend_dump.c1239
-rw-r--r--Zend/Optimizer/zend_dump.h48
-rw-r--r--Zend/Optimizer/zend_func_info.c973
-rw-r--r--Zend/Optimizer/zend_func_info.h68
-rw-r--r--Zend/Optimizer/zend_inference.c4677
-rw-r--r--Zend/Optimizer/zend_inference.h288
-rw-r--r--Zend/Optimizer/zend_optimizer.c1576
-rw-r--r--Zend/Optimizer/zend_optimizer.h98
-rw-r--r--Zend/Optimizer/zend_optimizer_internal.h123
-rw-r--r--Zend/Optimizer/zend_ssa.c1628
-rw-r--r--Zend/Optimizer/zend_ssa.h326
-rw-r--r--Zend/Optimizer/zend_worklist.h121
-rw-r--r--Zend/Zend.m44
-rw-r--r--Zend/tests/anon/015.phpt30
-rw-r--r--Zend/tests/anon/016.phpt31
-rw-r--r--Zend/tests/array_self_add_globals.phpt1
-rw-r--r--Zend/tests/array_unpack/non_integer_keys.phpt4
-rw-r--r--Zend/tests/array_unpack/string_keys.phpt64
-rw-r--r--Zend/tests/array_unpack/unpack_string_keys_compile_time.phpt19
-rw-r--r--Zend/tests/attributes/016_custom_attribute_validation.phpt4
-rw-r--r--Zend/tests/bug27798.phpt4
-rw-r--r--Zend/tests/bug43201.phpt12
-rw-r--r--Zend/tests/bug53826.phpt33
-rw-r--r--Zend/tests/bug60536_003.phpt8
-rw-r--r--Zend/tests/bug64677.phpt2
-rw-r--r--Zend/tests/bug70895.phpt6
-rw-r--r--Zend/tests/bug70898.phpt2
-rw-r--r--Zend/tests/bug71539_6.phpt15
-rw-r--r--Zend/tests/bug71695.phpt17
-rw-r--r--Zend/tests/bug75474.phpt75
-rw-r--r--Zend/tests/bug78239.phpt2
-rw-r--r--Zend/tests/bug78335_2.phpt2
-rw-r--r--Zend/tests/bug79862.phpt8
-rw-r--r--Zend/tests/call_to_deprecated_function_args.phpt2
-rw-r--r--Zend/tests/class_exists_002.phpt2
-rw-r--r--Zend/tests/closure_bindTo_preserves_used_variables.phpt17
-rw-r--r--Zend/tests/closures/closure_from_callable_gc.phpt27
-rw-r--r--Zend/tests/const_deprecation.phpt2
-rw-r--r--Zend/tests/exception_001.phpt2
-rw-r--r--Zend/tests/gc_010.phpt21
-rw-r--r--Zend/tests/get_mangled_object_vars.phpt2
-rw-r--r--Zend/tests/globals_001.phpt2
-rw-r--r--Zend/tests/globals_002.phpt2
-rw-r--r--Zend/tests/globals_003.phpt2
-rw-r--r--Zend/tests/globals_004.phpt2
-rw-r--r--Zend/tests/inherit_internal_static.phpt2
-rw-r--r--Zend/tests/interface_exists_001.phpt2
-rw-r--r--Zend/tests/invalid_const_class_name.phpt8
-rw-r--r--Zend/tests/iterable_or_null.phpt2
-rw-r--r--Zend/tests/list_keyed_leading_comma.phpt10
-rw-r--r--Zend/tests/lsb_023.phpt26
-rw-r--r--Zend/tests/lsb_024.phpt25
-rw-r--r--Zend/tests/method_static_var.phpt4
-rw-r--r--Zend/tests/multibyte/multibyte_encoding_001.phpt2
-rw-r--r--Zend/tests/multibyte/multibyte_encoding_003.phptbin328 -> 329 bytes
-rw-r--r--Zend/tests/named_params/undef_var.phpt2
-rw-r--r--Zend/tests/null_to_non_nullable_special_func.phpt12
-rw-r--r--Zend/tests/nullsafe_operator/013.phpt9
-rw-r--r--Zend/tests/objects_033.phpt4
-rw-r--r--Zend/tests/oct_whitespace.phpt8
-rw-r--r--Zend/tests/overloaded_func_001.phpt2
-rw-r--r--Zend/tests/overloaded_func_002.phpt2
-rw-r--r--Zend/tests/resource_key.phpt36
-rw-r--r--Zend/tests/restrict_globals/globals_in_globals.phpt11
-rw-r--r--Zend/tests/restrict_globals/invalid_append.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_append_isset.phpt8
-rw-r--r--Zend/tests/restrict_globals/invalid_append_unset.phpt8
-rw-r--r--Zend/tests/restrict_globals/invalid_assign.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_assign_list.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_assign_list_ref.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_assign_op.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt11
-rw-r--r--Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt11
-rw-r--r--Zend/tests/restrict_globals/invalid_foreach.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_foreach_ref.phpt10
-rw-r--r--Zend/tests/restrict_globals/invalid_pass_by_ref.phpt23
-rw-r--r--Zend/tests/restrict_globals/invalid_unset.phpt10
-rw-r--r--Zend/tests/restrict_globals/key_canonicalization.phpt11
-rw-r--r--Zend/tests/restrict_globals/valid.phpt52
-rw-r--r--Zend/tests/return_types/internal_functions001.phpt2
-rw-r--r--Zend/tests/return_types/internal_functions002.phpt2
-rw-r--r--Zend/tests/return_types/internal_functions003.phpt2
-rw-r--r--Zend/tests/static_variable_in_dynamic_function.phpt21
-rw-r--r--Zend/tests/static_variable_in_dynamic_function_2.phpt21
-rw-r--r--Zend/tests/str_or_obj_of_class_zpp.phpt6
-rw-r--r--Zend/tests/str_or_obj_zpp.phpt6
-rw-r--r--Zend/tests/trait_exists_001.phpt2
-rw-r--r--Zend/tests/traits/bug69579.phpt2
-rw-r--r--Zend/tests/traits/get_declared_traits_004.phpt16
-rw-r--r--Zend/tests/traits/property008.phpt12
-rw-r--r--Zend/tests/type_declarations/internal_function_strict_mode.phpt2
-rw-r--r--Zend/tests/type_declarations/typed_properties_095.phpt2
-rw-r--r--Zend/tests/type_declarations/union_types/inheritance_internal.phpt2
-rw-r--r--Zend/tests/undef_index_to_exception.phpt2
-rw-r--r--Zend/tests/unset_cv09.phpt14
-rw-r--r--Zend/tests/unset_cv10.phpt4
-rw-r--r--Zend/zend.c42
-rw-r--r--Zend/zend.h33
-rw-r--r--Zend/zend_API.c440
-rw-r--r--Zend/zend_API.h296
-rw-r--r--Zend/zend_alloc.c7
-rw-r--r--Zend/zend_arena.h4
-rw-r--r--Zend/zend_ast.c11
-rw-r--r--Zend/zend_ast.h4
-rw-r--r--Zend/zend_attributes.c19
-rw-r--r--Zend/zend_attributes.h2
-rw-r--r--Zend/zend_attributes.stub.php4
-rw-r--r--Zend/zend_attributes_arginfo.h19
-rw-r--r--Zend/zend_bitset.h6
-rw-r--r--Zend/zend_builtin_functions.c130
-rw-r--r--Zend/zend_builtin_functions.stub.php6
-rw-r--r--Zend/zend_builtin_functions_arginfo.h17
-rw-r--r--Zend/zend_closures.c62
-rw-r--r--Zend/zend_closures.stub.php2
-rw-r--r--Zend/zend_closures_arginfo.h13
-rw-r--r--Zend/zend_compile.c639
-rw-r--r--Zend/zend_compile.h64
-rw-r--r--Zend/zend_constants.c6
-rw-r--r--Zend/zend_constants.h2
-rw-r--r--Zend/zend_cpuinfo.c4
-rw-r--r--Zend/zend_exceptions.c96
-rw-r--r--Zend/zend_exceptions.h2
-rw-r--r--Zend/zend_exceptions.stub.php40
-rw-r--r--Zend/zend_exceptions_arginfo.h211
-rw-r--r--Zend/zend_execute.c196
-rw-r--r--Zend/zend_execute.h24
-rw-r--r--Zend/zend_execute_API.c43
-rw-r--r--Zend/zend_extensions.h14
-rw-r--r--Zend/zend_gc.c43
-rw-r--r--Zend/zend_gc.h8
-rw-r--r--Zend/zend_generators.c26
-rw-r--r--Zend/zend_generators.h7
-rw-r--r--Zend/zend_generators.stub.php3
-rw-r--r--Zend/zend_generators_arginfo.h24
-rw-r--r--Zend/zend_globals.h42
-rw-r--r--Zend/zend_hash.c22
-rw-r--r--Zend/zend_hash.h55
-rw-r--r--Zend/zend_highlight.h2
-rw-r--r--Zend/zend_inheritance.c568
-rw-r--r--Zend/zend_inheritance.h9
-rw-r--r--Zend/zend_ini.c10
-rw-r--r--Zend/zend_ini.h12
-rw-r--r--Zend/zend_ini_parser.y4
-rw-r--r--Zend/zend_interfaces.c32
-rw-r--r--Zend/zend_interfaces.h11
-rw-r--r--Zend/zend_interfaces.stub.php2
-rw-r--r--Zend/zend_interfaces_arginfo.h86
-rw-r--r--Zend/zend_language_scanner.h6
-rw-r--r--Zend/zend_language_scanner.l135
-rw-r--r--Zend/zend_map_ptr.h12
-rw-r--r--Zend/zend_modules.h2
-rw-r--r--Zend/zend_object_handlers.c137
-rw-r--r--Zend/zend_object_handlers.h44
-rw-r--r--Zend/zend_objects_API.c2
-rw-r--r--Zend/zend_objects_API.h2
-rw-r--r--Zend/zend_opcode.c162
-rw-r--r--Zend/zend_operators.c151
-rw-r--r--Zend/zend_operators.h98
-rw-r--r--Zend/zend_portability.h2
-rw-r--r--Zend/zend_ptr_stack.c4
-rw-r--r--Zend/zend_ptr_stack.h6
-rw-r--r--Zend/zend_signal.h4
-rw-r--r--Zend/zend_smart_str.h18
-rw-r--r--Zend/zend_smart_string.h12
-rw-r--r--Zend/zend_stack.c2
-rw-r--r--Zend/zend_stack.h2
-rw-r--r--Zend/zend_stream.h2
-rw-r--r--Zend/zend_string.c10
-rw-r--r--Zend/zend_string.h12
-rw-r--r--Zend/zend_strtod.c3
-rw-r--r--Zend/zend_ts_hash.c4
-rw-r--r--Zend/zend_ts_hash.h4
-rw-r--r--Zend/zend_types.h74
-rw-r--r--Zend/zend_vm_def.h315
-rw-r--r--Zend/zend_vm_execute.h898
-rwxr-xr-xZend/zend_vm_gen.php193
-rw-r--r--Zend/zend_vm_handlers.h919
-rw-r--r--Zend/zend_vm_opcodes.c10
-rw-r--r--Zend/zend_vm_opcodes.h3
-rw-r--r--Zend/zend_weakrefs.c30
-rw-r--r--Zend/zend_weakrefs.stub.php4
-rw-r--r--Zend/zend_weakrefs_arginfo.h25
203 files changed, 28485 insertions, 2649 deletions
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c
new file mode 100644
index 0000000000..e833ff3dbf
--- /dev/null
+++ b/Zend/Optimizer/block_pass.c
@@ -0,0 +1,1937 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+#include "zend_dump.h"
+
+/* Checks if a constant (like "true") may be replaced by its value */
+int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
+{
+ zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
+ if (c) {
+ if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
+ && !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
+ && (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
+ || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
+ ZVAL_COPY_VALUE(result, &c->value);
+ if (copy) {
+ Z_TRY_ADDREF_P(result);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /* Special constants null/true/false can always be substituted. */
+ c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
+ if (c) {
+ ZVAL_COPY_VALUE(result, &c->value);
+ return 1;
+ }
+ return 0;
+}
+
+/* Data dependencies macros */
+
+#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
+#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
+
+static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
+{
+ zend_op *opcodes = op_array->opcodes;
+
+ do {
+ 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;
+
+ 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;
+ }
+
+ /* strip the inside NOPs */
+ i = j = b->start + 1;
+ while (i < b->start + b->len) {
+ if (op_array->opcodes[i].opcode != ZEND_NOP) {
+ if (i != j) {
+ op_array->opcodes[j] = op_array->opcodes[i];
+ }
+ j++;
+ }
+ i++;
+ }
+ b->len = j - b->start;
+ while (j < i) {
+ MAKE_NOP(op_array->opcodes + j);
+ j++;
+ }
+}
+
+static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) {
+ HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ zval *zv;
+ if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
+ || (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) {
+ /* fallback to next block */
+ return block->successors[block->successors_count - 1];
+ }
+ if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) {
+ /* always jump to the default arm */
+ return block->successors[block->successors_count - 1];
+ }
+ if (Z_TYPE_P(val) == IS_LONG) {
+ zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
+ } else {
+ ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING);
+ zv = zend_hash_find(jumptable, Z_STR_P(val));
+ }
+ if (!zv) {
+ /* default */
+ return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)];
+ }
+ return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
+}
+
+static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count)
+{
+ zend_op *opline, *src;
+ zend_op *end, *last_op = NULL;
+
+ 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 &&
+ opline->opcode != ZEND_FREE) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST
+ ) {
+ znode_op op1 = opline->op1;
+ if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+ COPY_NODE(opline->result, opline->op1);
+ COPY_NODE(opline->op1, src->op1);
+ VAR_SOURCE(op1) = NULL;
+ MAKE_NOP(src);
+ ++(*opt_count);
+ } else {
+ zval c;
+ ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
+ if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
+ VAR_SOURCE(op1) = NULL;
+ literal_dtor(&ZEND_OP1_LITERAL(src));
+ MAKE_NOP(src);
+ ++(*opt_count);
+ } else {
+ zval_ptr_dtor_nogc(&c);
+ }
+ }
+ }
+ }
+
+ /* Constant Propagation: strip X = QM_ASSIGN(const) */
+ if (opline->op2_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST) {
+
+ znode_op op2 = opline->op2;
+ zval c;
+
+ ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
+ if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
+ VAR_SOURCE(op2) = NULL;
+ literal_dtor(&ZEND_OP1_LITERAL(src));
+ MAKE_NOP(src);
+ ++(*opt_count);
+ } else {
+ zval_ptr_dtor_nogc(&c);
+ }
+ }
+ }
+
+ switch (opline->opcode) {
+ case ZEND_ECHO:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ }
+ } else if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) {
+ if (last_op == opline - 1) {
+ /* compress consecutive ECHO's.
+ * Float to string conversion may be affected by current
+ * locale setting.
+ */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+ convert_to_string(&ZEND_OP1_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
+ 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));
+ if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
+ } else {
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
+ ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
+ MAKE_NOP(last_op);
+ ++(*opt_count);
+ }
+ last_op = opline;
+ }
+ break;
+
+ case ZEND_FREE:
+ if (opline->op1_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ switch (src->opcode) {
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ /* T = BOOL(X), FREE(T) => T = BOOL(X) */
+ /* The remaining BOOL is removed by a separate optimization */
+ VAR_SOURCE(opline->op1) = NULL;
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_ASSIGN_STATIC_PROP:
+ 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:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_PRE_INC_STATIC_PROP:
+ case ZEND_PRE_DEC_STATIC_PROP:
+ src->result_type = IS_UNUSED;
+ VAR_SOURCE(opline->op1) = NULL;
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ default:
+ break;
+ }
+ }
+ } else if (opline->op1_type == IS_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ /* V = OP, FREE(V) => OP. NOP */
+ if (src &&
+ src->opcode != ZEND_FETCH_R &&
+ src->opcode != ZEND_FETCH_STATIC_PROP_R &&
+ src->opcode != ZEND_FETCH_DIM_R &&
+ src->opcode != ZEND_FETCH_OBJ_R &&
+ src->opcode != ZEND_NEW) {
+ src->result_type = IS_UNUSED;
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ if (src->opcode == ZEND_QM_ASSIGN) {
+ if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ src->opcode = ZEND_FREE;
+ } else {
+ MAKE_NOP(src);
+ }
+ }
+ }
+ }
+ break;
+
+#if 0
+ /* pre-evaluate functions:
+ constant(x)
+ function_exists(x)
+ extension_loaded(x)
+ BAD: interacts badly with Accelerator
+ */
+ if((opline->op1_type & IS_VAR) &&
+ VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
+ VAR_SOURCE(opline->op1)->extended_value == 1) {
+ zend_op *fcall = VAR_SOURCE(opline->op1);
+ zend_op *sv = fcall-1;
+ if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
+ sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
+ Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
+ ) {
+ zval *arg = &OPLINE_OP1_LITERAL(sv);
+ char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
+ int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
+ if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
+ (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
+ ) {
+ zend_function *function;
+ if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
+ literal_dtor(arg);
+ MAKE_NOP(sv);
+ MAKE_NOP(fcall);
+ LITERAL_BOOL(opline->op1, 1);
+ opline->op1_type = IS_CONST;
+ }
+ } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
+ zval c;
+ if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) {
+ literal_dtor(arg);
+ MAKE_NOP(sv);
+ MAKE_NOP(fcall);
+ ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
+ /* no copy ctor - get already copied it */
+ opline->op1_type = IS_CONST;
+ }
+ } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
+ if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
+ literal_dtor(arg);
+ MAKE_NOP(sv);
+ MAKE_NOP(fcall);
+ LITERAL_BOOL(opline->op1, 1);
+ opline->op1_type = IS_CONST;
+ }
+ }
+ }
+ }
+#endif
+
+ case ZEND_FETCH_LIST_R:
+ case ZEND_FETCH_LIST_W:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* LIST variable will be deleted later by FREE */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
+ }
+ break;
+
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* SWITCH variable will be deleted later by FREE, so we can't optimize it */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
+ break;
+ }
+ if (opline->op1_type == IS_CONST) {
+ int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_JMP;
+ opline->op1_type = IS_UNUSED;
+ opline->op2_type = IS_UNUSED;
+ block->successors_count = 1;
+ block->successors[0] = target;
+ }
+ break;
+
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_COPY_TMP:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* Variable will be deleted later by FREE, so we can't optimize it */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
+ break;
+ }
+ /* break missing intentionally */
+
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
+ }
+ /* IS_EQ(TRUE, X) => BOOL(X)
+ * IS_EQ(FALSE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(FALSE, X) => BOOL(X)
+ * CASE(TRUE, X) => BOOL(X)
+ * CASE(FALSE, X) => BOOL_NOT(X)
+ */
+ if (opline->op1_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ COPY_NODE(opline->op1, opline->op2);
+ SET_UNUSED(opline->op2);
+ ++(*opt_count);
+ goto optimize_bool;
+ } else if (opline->op2_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ SET_UNUSED(opline->op2);
+ ++(*opt_count);
+ goto optimize_bool;
+ }
+ break;
+
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ optimize_bool:
+ if (opline->op1_type == IS_CONST) {
+ goto optimize_const_unary_op;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ switch (src->opcode) {
+ case ZEND_BOOL_NOT:
+ /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
+ MAKE_NOP(src);
+ ++(*opt_count);
+ goto optimize_bool;
+ case ZEND_BOOL:
+ /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ goto optimize_bool;
+ case ZEND_IS_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_IS_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_IS_SMALLER:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_IN_ARRAY:
+ case ZEND_ARRAY_KEY_EXISTS:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ break;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ }
+ }
+ }
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ ++(*opt_count);
+ block->successors_count = 1;
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
+ (opline->opcode == ZEND_JMPZ)) {
+
+ MAKE_NOP(opline);
+ block->successors[0] = block->successors[1];
+ block->len--;
+ cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
+ break;
+ } else {
+ zend_basic_block *next = cfg->blocks + block->successors[1];
+
+ next->flags &= ~ZEND_BB_FOLLOW;
+ if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
+ next->flags &= ~ZEND_BB_REACHABLE;
+ }
+ opline->opcode = ZEND_JMP;
+ COPY_NODE(opline->op1, opline->op2);
+ break;
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
+ opline->opcode = INV_COND(opline->opcode);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ } else if (src->opcode == ZEND_BOOL ||
+ src->opcode == ZEND_QM_ASSIGN) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case ZEND_JMPZNZ:
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ ++(*opt_count);
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
+ zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ block->successors[0] = block->successors[1];
+ } else {
+ zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ }
+ block->successors_count = 1;
+ opline->op1_type = IS_UNUSED;
+ opline->extended_value = 0;
+ opline->opcode = ZEND_JMP;
+ break;
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT) {
+ /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
+ uint32_t tmp;
+
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ tmp = block->successors[0];
+ block->successors[0] = block->successors[1];
+ block->successors[1] = tmp;
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ } else if (src->opcode == ZEND_BOOL ||
+ src->opcode == ZEND_QM_ASSIGN) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ while (1) {
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
+ (opline->opcode == ZEND_JMPZ_EX)) {
+
+ ++(*opt_count);
+ opline->opcode = ZEND_QM_ASSIGN;
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), opline->opcode == ZEND_JMPZ_EX);
+ opline->op2.num = 0;
+ block->successors_count = 1;
+ block->successors[0] = block->successors[1];
+ cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
+ break;
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
+ opline->result.var == opline->op1.var)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL ||
+ src->opcode == ZEND_QM_ASSIGN) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
+ }
+
+ if (opline->op2_type == IS_CONST &&
+ opline->op1_type == IS_TMP_VAR) {
+
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ (src->opcode == ZEND_CONCAT ||
+ src->opcode == ZEND_FAST_CONCAT) &&
+ src->op2_type == IS_CONST) {
+ /* compress consecutive CONCATs */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
+ convert_to_string(&ZEND_OP2_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
+ convert_to_string(&ZEND_OP2_LITERAL(src));
+ }
+
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
+ l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
+ Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
+ } else {
+ Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
+ zval_ptr_dtor_str(&ZEND_OP2_LITERAL(opline));
+ ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
+ ZVAL_NULL(&ZEND_OP2_LITERAL(src));
+ MAKE_NOP(src);
+ ++(*opt_count);
+ }
+ }
+
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING &&
+ src->op1_type != IS_CONST) {
+ /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ }
+ }
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ 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_op *src = VAR_SOURCE(opline->op2);
+ VAR_SOURCE(opline->op2) = NULL;
+ COPY_NODE(opline->op2, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ }
+ }
+ if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
+ Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
+ /* convert CONCAT('', X) => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ COPY_NODE(opline->op1, opline->op2);
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ ++(*opt_count);
+ } else if (opline->op2_type == IS_CONST &&
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
+ Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
+ /* convert CONCAT(X, '') => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ ++(*opt_count);
+ } else if (opline->opcode == ZEND_CONCAT &&
+ (opline->op1_type == IS_CONST ||
+ (opline->op1_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op1) &&
+ (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
+ (opline->op2_type == IS_CONST ||
+ (opline->op2_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op2) &&
+ (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
+ opline->opcode = ZEND_FAST_CONCAT;
+ ++(*opt_count);
+ }
+ break;
+
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_BOOL_XOR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ /* evaluate constant expressions */
+ zval result;
+
+optimize_constant_binary_op:
+ if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ if (opline->op1_type == IS_CONST) {
+ /* evaluate constant unary ops */
+ zval result;
+
+optimize_const_unary_op:
+ if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_CAST:
+ if (opline->op1_type == IS_CONST) {
+ /* cast of constant operand */
+ zval result;
+
+ if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_STRLEN:
+ if (opline->op1_type == IS_CONST) {
+ zval result;
+
+ if (zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_RETURN:
+ case ZEND_EXIT:
+ if (opline->op1_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ if (src && src->opcode == ZEND_QM_ASSIGN) {
+ zend_op *op = src + 1;
+ bool optimize = 1;
+
+ while (op < opline) {
+ if ((op->op1_type == opline->op1_type
+ && op->op1.var == opline->op1.var)
+ || (op->op2_type == opline->op1_type
+ && op->op2.var == opline->op1.var)) {
+ optimize = 0;
+ break;
+ }
+ op++;
+ }
+
+ if (optimize) {
+ /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ ++(*opt_count);
+ }
+ }
+ }
+ break;
+
+ case ZEND_QM_ASSIGN:
+ if (opline->op1_type == opline->result_type &&
+ opline->op1.var == opline->result.var) {
+ /* strip T = QM_ASSIGN(T) */
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ opline->result_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ /* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode != ZEND_COPY_TMP &&
+ src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
+ src->opcode != ZEND_ADD_ARRAY_UNPACK &&
+ (src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
+ src == opline -1)) {
+ src->result.var = opline->result.var;
+ VAR_SOURCE(opline->op1) = NULL;
+ VAR_SOURCE(opline->result) = src;
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ }
+ }
+ break;
+ }
+
+ /* get variable source */
+ if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
+ SET_VAR_SOURCE(opline);
+ }
+ opline++;
+ }
+}
+
+/* Rebuild plain (optimized) op_array from CFG */
+static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ zend_basic_block *b;
+ zend_op *new_opcodes;
+ zend_op *opline;
+ uint32_t len = 0;
+ int n;
+
+ for (b = blocks; b < end; b++) {
+ if (b->len == 0) {
+ continue;
+ }
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+ /* Only keep the FREE for the loop var */
+ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+ || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+ len += b->len = 1;
+ continue;
+ }
+
+ opline = op_array->opcodes + b->start + b->len - 1;
+ if (opline->opcode == ZEND_JMP) {
+ zend_basic_block *next = b + 1;
+
+ while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
+ next++;
+ }
+ if (next < end && next == blocks + b->successors[0]) {
+ /* JMP to the next block - strip it */
+ MAKE_NOP(opline);
+ b->len--;
+ }
+ } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
+ /* skip empty block */
+ b->len--;
+ }
+ len += b->len;
+ } else {
+ /* this block will not be used, delete all constants there */
+ zend_op *op = op_array->opcodes + b->start;
+ zend_op *end = op + b->len;
+ for (; op < end; op++) {
+ if (op->op1_type == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
+ }
+ if (op->op2_type == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
+ }
+ }
+ }
+ }
+
+ new_opcodes = emalloc(len * sizeof(zend_op));
+ opline = new_opcodes;
+
+ /* Copy code of reachable blocks into a single buffer */
+ for (b = blocks; b < end; b++) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
+ b->start = opline - new_opcodes;
+ opline += b->len;
+ }
+ }
+
+ /* adjust jump targets */
+ efree(op_array->opcodes);
+ op_array->opcodes = new_opcodes;
+ op_array->last = len;
+
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
+ continue;
+ }
+ opline = op_array->opcodes + b->start + b->len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_CALL:
+ case ZEND_JMP:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
+ }
+ break;
+ 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);
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ zval *zv;
+ uint32_t s = 0;
+ ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
+
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
+ } ZEND_HASH_FOREACH_END();
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
+ break;
+ }
+ }
+ }
+
+ /* adjust exception jump targets & remove unused try_catch_array entries */
+ if (op_array->last_try_catch) {
+ int i, j;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
+ for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
+ if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
+ map[i] = j;
+ op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
+ if (op_array->try_catch_array[i].catch_op) {
+ op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
+ } else {
+ op_array->try_catch_array[j].catch_op = 0;
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
+ } else {
+ op_array->try_catch_array[j].finally_op = 0;
+ }
+ if (!op_array->try_catch_array[i].finally_end) {
+ op_array->try_catch_array[j].finally_end = 0;
+ } else {
+ op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
+ }
+ j++;
+ }
+ }
+ if (i != j) {
+ op_array->last_try_catch = j;
+ if (j == 0) {
+ efree(op_array->try_catch_array);
+ op_array->try_catch_array = NULL;
+ }
+
+ if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ zend_op *opline = new_opcodes;
+ zend_op *end = opline + len;
+ while (opline < end) {
+ if (opline->opcode == ZEND_FAST_RET &&
+ opline->op2.num != (uint32_t)-1 &&
+ opline->op2.num < (uint32_t)j) {
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ }
+ }
+ free_alloca(map, use_heap);
+ }
+
+ /* adjust early binding list */
+ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
+ ZEND_ASSERT(op_array == &ctx->script->main_op_array);
+ ctx->script->first_early_binding_opline =
+ zend_build_delayed_early_binding_list(op_array);
+ }
+
+ /* rebuild map (just for printing) */
+ memset(cfg->map, -1, sizeof(int) * op_array->last);
+ for (n = 0; n < cfg->blocks_count; n++) {
+ if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ cfg->map[cfg->blocks[n].start] = n;
+ }
+ }
+}
+
+static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+{
+ int b;
+ zend_basic_block *target_block = cfg->blocks + block->successors[n];
+
+ if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ do {
+ b = target_block->successors[0];
+ target_block = cfg->blocks + b;
+ } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
+ block->successors[n] = b;
+ ++(*opt_count);
+ }
+ return target_block;
+}
+
+static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+{
+ int b;
+ zend_basic_block *target_block = cfg->blocks + block->successors[n];
+
+ if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ do {
+ b = target_block->successors[0];
+ target_block = cfg->blocks + b;
+ } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
+ block->successors[n] = b;
+ ++(*opt_count);
+ }
+ return target_block;
+}
+
+static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
+{
+ zend_basic_block *next_block = block + 1;
+ zend_basic_block *end = cfg->blocks + cfg->blocks_count;
+
+ while (1) {
+ if (next_block == end) {
+ return NULL;
+ } else if (next_block->flags & ZEND_BB_REACHABLE) {
+ break;
+ }
+ next_block++;
+ }
+ while (next_block->len == 0 && !(next_block->flags & ZEND_BB_PROTECTED)) {
+ next_block = cfg->blocks + next_block->successors[0];
+ }
+ return next_block;
+}
+
+
+/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
+static zend_always_inline int in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count)
+{
+ int i;
+
+ for (i = 0; i < jmp_hitlist_count; i++) {
+ if (jmp_hitlist[i] == target) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define CHECK_LOOP(target) \
+ if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
+ jmp_hitlist[jmp_hitlist_count++] = target; \
+ } else { \
+ break; \
+ }
+
+static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count)
+{
+ /* last_op is the last opcode of the current block */
+ zend_basic_block *target_block, *follow_block, *next_block;
+ zend_op *last_op, *target;
+ int next, jmp_hitlist_count;
+
+ if (block->len == 0) {
+ return;
+ }
+
+ last_op = op_array->opcodes + block->start + block->len - 1;
+ switch (last_op->opcode) {
+ case ZEND_JMP:
+ jmp_hitlist_count = 0;
+
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+ if (target->opcode == ZEND_JMP) {
+ /* JMP L, L: JMP L1 -> JMP L1 */
+ next = target_block->successors[0];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+
+ next_block = get_next_block(cfg, block);
+ if (target_block == next_block) {
+ /* JMP(next) -> NOP */
+ MAKE_NOP(last_op);
+ ++(*opt_count);
+ block->len--;
+ } else if (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+ if (target->opcode == ZEND_JMPZNZ) {
+ /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
+ *last_op = *target;
+ if (last_op->op1_type == IS_CONST) {
+ zval zv;
+ ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
+ last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ }
+ block->successors_count = 2;
+ block->successors[0] = target_block->successors[0];
+ block->successors[1] = target_block->successors[1];
+ ++(*opt_count);
+ goto optimize_jmpznz;
+ } else if ((target->opcode == ZEND_RETURN ||
+ target->opcode == ZEND_RETURN_BY_REF ||
+ target->opcode == ZEND_GENERATOR_RETURN ||
+ target->opcode == ZEND_EXIT) &&
+ !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
+ /* JMP L, L: RETURN to immediate RETURN */
+ *last_op = *target;
+ if (last_op->op1_type == IS_CONST) {
+ zval zv;
+ ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
+ last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ }
+ block->successors_count = 0;
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_JMP_NULL:
+ jmp_hitlist_count = 0;
+
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */
+ next = target_block->successors[0];
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ } else {
+ break;
+ }
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ jmp_hitlist_count = 0;
+
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == last_op->opcode &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == INV_COND(last_op->opcode) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
+ next = target_block->successors[last_op->opcode == ZEND_JMPNZ];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+
+ follow_block = get_follow_block(cfg, block, 1, opt_count);
+ if (target_block == follow_block) {
+ /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
+ if (last_op->op1_type == IS_CV) {
+ last_op->opcode = ZEND_CHECK_VAR;
+ last_op->op2.num = 0;
+ } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ last_op->opcode = ZEND_FREE;
+ last_op->op2.num = 0;
+ } else {
+ MAKE_NOP(last_op);
+ block->len--;
+ }
+ block->successors_count = 1;
+ ++(*opt_count);
+ } else if (follow_block->len == 1) {
+ target = op_array->opcodes + follow_block->start;
+ if (target->opcode == ZEND_JMP) {
+ if (block->successors[0] == follow_block->successors[0]) {
+ /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
+ if (last_op->op1_type == IS_CV) {
+ last_op->opcode = ZEND_CHECK_VAR;
+ last_op->op2.num = 0;
+ } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ last_op->opcode = ZEND_FREE;
+ last_op->op2.num = 0;
+ } else {
+ MAKE_NOP(last_op);
+ block->len--;
+ }
+ block->successors[0] = follow_block - cfg->blocks;
+ block->successors_count = 1;
+ ++(*opt_count);
+ break;
+ } else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) {
+ next_block = get_next_block(cfg, follow_block);
+
+ if (target_block == next_block) {
+ /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/
+
+ last_op->opcode = INV_COND(last_op->opcode);
+
+ block->successors[0] = follow_block->successors[0];
+ block->successors[1] = next_block - cfg->blocks;
+
+ follow_block->flags &= ~ZEND_BB_REACHABLE;
+ MAKE_NOP(target);
+ follow_block->len = 0;
+
+ next_block->flags |= ZEND_BB_FOLLOW;
+
+ break;
+ }
+ }
+
+ /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
+ if (last_op->opcode == ZEND_JMPZ) {
+ block->successors[1] = follow_block->successors[0];
+ } else {
+ block->successors[1] = block->successors[0];
+ block->successors[0] = follow_block->successors[0];
+ }
+ last_op->opcode = ZEND_JMPZNZ;
+ ++(*opt_count);
+ }
+ }
+ break;
+
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMPZ_EX:
+ jmp_hitlist_count = 0;
+
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == last_op->opcode-3 &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == last_op->opcode &&
+ target->result.var == last_op->result.var &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
+ next = target_block->successors[last_op->opcode == ZEND_JMPNZ_EX];
+ } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
+ target->result.var == last_op->result.var &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */
+ next = target_block->successors[1];
+ } else if (target->opcode == ZEND_BOOL &&
+ (SAME_VAR(target->op1, last_op->result) ||
+ SAME_VAR(target->op1, last_op->op1))) {
+ /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
+ Z = JMPZ_EX(X,L1+1) */
+
+ /* NOTE: This optimization pattern is not safe, but works, */
+ /* because result of JMPZ_EX instruction */
+ /* is not used on the following path and */
+ /* should be used once on the branch path. */
+ /* */
+ /* The pattern works well only if jums processed in */
+ /* direct order, otherwise it breaks JMPZ_EX */
+ /* sequences too early. */
+ last_op->result.var = target->result.var;
+ next = target_block->successors[0];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+
+ follow_block = get_follow_block(cfg, block, 1, opt_count);
+ if (target_block == follow_block) {
+ /* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */
+ last_op->opcode = ZEND_BOOL;
+ last_op->op2.num = 0;
+ block->successors_count = 1;
+ ++(*opt_count);
+ break;
+ }
+ break;
+
+ case ZEND_JMPZNZ: {
+optimize_jmpznz:
+ jmp_hitlist_count = 0;
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ while (target_block->len == 1) {
+ target = op_array->opcodes + target_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
+ next = target_block->successors[0];
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ next = target_block->successors[0];
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+ next = target_block->successors[1];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[0] = next;
+ ++(*opt_count);
+ target_block = get_target_block(cfg, block, 0, opt_count);
+ }
+
+ jmp_hitlist_count = 0;
+ follow_block = get_target_block(cfg, block, 1, opt_count);
+ while (follow_block->len == 1) {
+ target = op_array->opcodes + follow_block->start;
+
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X, L1, L2), L2: JMP(L3) -> JMPZNZ(X, L1, L3) */
+ next = follow_block->successors[0];
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L2: X = JMPNZ(X, L3) -> JMPZNZ(X, L1, L3) */
+ next = follow_block->successors[0];
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ SAME_VAR(target->op1, last_op->op1)) {
+ /* JMPZNZ(X, L1, L2), L2: JMPZ(X, L3) -> JMPZNZ(X, L1, L2+1) */
+ next = follow_block->successors[1];
+ } else {
+ break;
+ }
+ CHECK_LOOP(next);
+ block->successors[1] = next;
+ ++(*opt_count);
+ follow_block = get_target_block(cfg, block, 1, opt_count);
+ }
+
+ next_block = get_next_block(cfg, block);
+ if (target_block == follow_block &&
+ !(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
+ /* JMPZNZ(?,L,L) -> JMP(L) */
+ last_op->opcode = ZEND_JMP;
+ SET_UNUSED(last_op->op1);
+ SET_UNUSED(last_op->op2);
+ last_op->extended_value = 0;
+ block->successors_count = 1;
+ ++(*opt_count);
+ } else if (target_block == next_block) {
+ /* jumping to next on Z - can follow to it and jump only on NZ */
+ /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
+ int tmp = block->successors[0];
+ last_op->opcode = ZEND_JMPNZ;
+ block->successors[0] = block->successors[1];
+ block->successors[1] = tmp;
+ ++(*opt_count);
+ } else if (follow_block == next_block) {
+ /* jumping to next on NZ - can follow to it and jump only on Z */
+ /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
+ last_op->opcode = ZEND_JMPZ;
+ ++(*opt_count);
+ }
+ break;
+ }
+ }
+}
+
+/* Global data dependencies */
+
+/* Find a set of variables which are used outside of the block where they are
+ * defined. We won't apply some optimization patterns for such variables. */
+static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
+{
+ int n;
+ zend_basic_block *block, *next_block;
+ uint32_t var_num;
+ uint32_t bitset_len;
+ zend_bitset usage;
+ zend_bitset defined_here;
+ void *checkpoint;
+ zend_op *opline, *end;
+
+
+ if (op_array->T == 0) {
+ /* shortcut - if no Ts, nothing to do */
+ return;
+ }
+
+ checkpoint = zend_arena_checkpoint(ctx->arena);
+ bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
+ defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
+
+ zend_bitset_clear(defined_here, bitset_len);
+ for (n = 1; n < cfg->blocks_count; n++) {
+ block = cfg->blocks + n;
+
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
+ continue;
+ }
+
+ opline = op_array->opcodes + block->start;
+ end = opline + block->len;
+ if (!(block->flags & ZEND_BB_FOLLOW) ||
+ (block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_clear(defined_here, bitset_len);
+ }
+
+ while (opline<end) {
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = VAR_NUM(opline->op1.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ }
+ if (opline->op2_type == IS_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (opline->opcode == ZEND_FE_FETCH_R ||
+ opline->opcode == ZEND_FE_FETCH_RW) {
+ /* these opcode use the op2 as result */
+ zend_bitset_incl(defined_here, var_num);
+ } else if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ }
+
+ if (opline->result_type == IS_VAR) {
+ var_num = VAR_NUM(opline->result.var);
+ zend_bitset_incl(defined_here, var_num);
+ } else if (opline->result_type == IS_TMP_VAR) {
+ 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)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ break;
+ default :
+ zend_bitset_incl(defined_here, var_num);
+ }
+ }
+ opline++;
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
+ int printed = 0;
+ uint32_t i;
+
+ for (i = op_array->last_var; i< op_array->T; i++) {
+ if (zend_bitset_in(used_ext, i)) {
+ if (!printed) {
+ fprintf(stderr, "NON-LOCAL-VARS: %d", i);
+ printed = 1;
+ } else {
+ fprintf(stderr, ", %d", i);
+ }
+ }
+ }
+ if (printed) {
+ fprintf(stderr, "\n");
+ }
+ }
+
+ usage = defined_here;
+ next_block = NULL;
+ for (n = cfg->blocks_count; n > 0;) {
+ block = cfg->blocks + (--n);
+
+ if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
+ continue;
+ }
+
+ end = op_array->opcodes + block->start;
+ opline = end + block->len - 1;
+ if (!next_block ||
+ !(next_block->flags & ZEND_BB_FOLLOW) ||
+ (next_block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_copy(usage, used_ext, bitset_len);
+ } else if (block->successors_count > 1) {
+ zend_bitset_union(usage, used_ext, bitset_len);
+ }
+ next_block = block;
+
+ while (opline >= end) {
+ /* usage checks */
+ if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
+ if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
+ switch (opline->opcode) {
+ 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:
+ case ZEND_ASSIGN_REF:
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ opline->result_type = IS_UNUSED;
+ break;
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_POST_INC_STATIC_PROP:
+ case ZEND_POST_DEC_STATIC_PROP:
+ opline->opcode -= 2;
+ opline->result_type = IS_UNUSED;
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ if (opline->op1_type == IS_CV) {
+ opline->opcode = ZEND_CHECK_VAR;
+ SET_UNUSED(opline->result);
+ } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ opline->opcode = ZEND_FREE;
+ SET_UNUSED(opline->result);
+ } else {
+ if (opline->op1_type == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ }
+ MAKE_NOP(opline);
+ }
+ break;
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ opline->opcode -= 3;
+ 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;
+ }
+ } else {
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ADD_ARRAY_UNPACK:
+ case ZEND_ROPE_ADD:
+ break;
+ default:
+ zend_bitset_excl(usage, VAR_NUM(opline->result.var));
+ break;
+ }
+ }
+ }
+
+ if (opline->op2_type == IS_VAR) {
+ switch (opline->opcode) {
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
+ break;
+ default:
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
+ break;
+ }
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
+ }
+
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
+ }
+
+ opline--;
+ }
+ }
+
+ zend_arena_release(&ctx->arena, checkpoint);
+}
+
+static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count)
+{
+ int i;
+ zend_basic_block *b, *bb;
+ zend_basic_block *prev = NULL;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ b = cfg->blocks + i;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ if ((b->flags & ZEND_BB_FOLLOW) &&
+ !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
+ prev && prev->successors_count == 1 && prev->successors[0] == i)
+ {
+ zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
+ if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
+ MAKE_NOP(last_op);
+ }
+
+ for (bb = prev + 1; bb != b; bb++) {
+ zend_op *op = op_array->opcodes + bb->start;
+ zend_op *end = op + bb->len;
+ while (op < end) {
+ if (op->op1_type == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
+ }
+ if (op->op2_type == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
+ }
+ MAKE_NOP(op);
+ op++;
+ }
+ /* make block empty */
+ bb->len = 0;
+ }
+
+ /* re-link */
+ prev->flags |= (b->flags & ZEND_BB_EXIT);
+ prev->len = b->start + b->len - prev->start;
+ prev->successors_count = b->successors_count;
+ if (b->successors != b->successors_storage) {
+ prev->successors = b->successors;
+ b->successors = b->successors_storage;
+ } else {
+ memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
+ }
+
+ /* unlink & make block empty and unreachable */
+ b->flags = 0;
+ b->len = 0;
+ b->successors_count = 0;
+ ++(*opt_count);
+ } else {
+ prev = b;
+ }
+ }
+ }
+}
+
+#define PASSES 3
+
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_cfg cfg;
+ zend_basic_block *blocks, *end, *b;
+ int pass;
+ uint32_t bitset_len;
+ zend_bitset usage;
+ void *checkpoint;
+ zend_op **Tsource;
+ uint32_t opt_count;
+ int *jmp_hitlist;
+
+ /* Build CFG */
+ checkpoint = zend_arena_checkpoint(ctx->arena);
+ if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) {
+ zend_arena_release(&ctx->arena, checkpoint);
+ return;
+ }
+
+ if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
+ zend_arena_release(&ctx->arena, checkpoint);
+ return;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
+ }
+
+ 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 *));
+ usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
+ jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int));
+
+ blocks = cfg.blocks;
+ end = blocks + cfg.blocks_count;
+ for (pass = 0; pass < PASSES; pass++) {
+ opt_count = 0;
+
+ /* Compute data dependencies */
+ zend_bitset_clear(usage, bitset_len);
+ zend_t_usage(&cfg, op_array, usage, ctx);
+
+ /* optimize each basic block separately */
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ continue;
+ }
+ /* we track data dependencies only inside a single basic block */
+ if (!(b->flags & ZEND_BB_FOLLOW) ||
+ (b->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
+ }
+ zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count);
+ }
+
+ /* Eliminate NOPs */
+ for (b = blocks; b < end; b++) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ strip_nops(op_array, b);
+ }
+ }
+
+ opt_count = 0;
+
+ /* Jump optimization for each block */
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count);
+ }
+ }
+
+ /* Eliminate unreachable basic blocks */
+ zend_cfg_remark_reachable_blocks(op_array, &cfg);
+
+ /* Merge Blocks */
+ zend_merge_blocks(op_array, &cfg, &opt_count);
+
+ if (opt_count == 0) {
+ break;
+ }
+ }
+
+ assemble_code_blocks(&cfg, op_array, ctx);
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
+ }
+
+ /* Destroy CFG */
+ zend_arena_release(&ctx->arena, checkpoint);
+}
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
new file mode 100644
index 0000000000..55cc40afb9
--- /dev/null
+++ b/Zend/Optimizer/compact_literals.c
@@ -0,0 +1,839 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ | Xinchen Hui <laruence@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* pass 11
+ * - compact literals table
+ */
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_extensions.h"
+
+#define DEBUG_COMPACT_LITERALS 0
+
+#define LITERAL_VALUE 0x0100
+#define LITERAL_FUNC 0x0200
+#define LITERAL_CLASS 0x0300
+#define LITERAL_CONST 0x0400
+#define LITERAL_CLASS_CONST 0x0500
+#define LITERAL_STATIC_METHOD 0x0600
+#define LITERAL_STATIC_PROPERTY 0x0700
+#define LITERAL_METHOD 0x0800
+#define LITERAL_PROPERTY 0x0900
+#define LITERAL_GLOBAL 0x0A00
+
+#define LITERAL_KIND_MASK 0x0f00
+#define LITERAL_NUM_RELATED_MASK 0x000f
+
+#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
+
+typedef struct _literal_info {
+ uint32_t flags; /* bitmask (see defines above) */
+} literal_info;
+
+#define LITERAL_INFO(n, kind, related) do { \
+ info[n].flags = ((kind) | (related)); \
+ } while (0)
+
+static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
+{
+ zend_arg_info *arg_info;
+ if (arg_num > 0) {
+ if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+ return 0;
+ }
+ if (EXPECTED(arg_num <= op_array->num_args)) {
+ arg_info = &op_array->arg_info[arg_num-1];
+ } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
+ arg_info = &op_array->arg_info[op_array->num_args];
+ } else {
+ return 0;
+ }
+ } else {
+ arg_info = op_array->arg_info - 1;
+ }
+
+ if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
+ if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
+ return ZEND_TYPE_LIST(arg_info->type)->num_types;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static uint32_t add_static_slot(HashTable *hash,
+ zend_op_array *op_array,
+ uint32_t op1,
+ uint32_t op2,
+ uint32_t kind,
+ int *cache_size)
+{
+ uint32_t ret;
+ zval *class_name = &op_array->literals[op1];
+ zval *prop_name = &op_array->literals[op2];
+ zval *pos, tmp;
+
+ zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
+ ZSTR_H(key) = zend_string_hash_func(key);
+ ZSTR_H(key) += kind;
+
+ pos = zend_hash_find(hash, key);
+ if (pos) {
+ ret = Z_LVAL_P(pos);
+ } else {
+ ret = *cache_size;
+ *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
+ ZVAL_LONG(&tmp, ret);
+ zend_hash_add(hash, key, &tmp);
+ }
+ zend_string_release_ex(key, 0);
+ return ret;
+}
+
+void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_op *opline, *end;
+ int i, j, n, *map, cache_size;
+ zval zv, *pos;
+ literal_info *info;
+ int l_null = -1;
+ int l_false = -1;
+ int l_true = -1;
+ int l_empty_arr = -1;
+ HashTable hash, double_hash;
+ zend_string *key = NULL;
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+ int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
+
+ if (op_array->last_literal) {
+ info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
+
+ /* Mark literals of specific types */
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
+ break;
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
+ }
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
+ }
+ if (opline->op2_type == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
+ }
+ break;
+ case ZEND_CATCH:
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
+ break;
+ case ZEND_DEFINED:
+ LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1);
+ break;
+ case ZEND_FETCH_CONSTANT:
+ if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
+ } else {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2);
+ }
+ break;
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
+ }
+ 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:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ 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);
+ }
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
+ }
+ break;
+ case ZEND_FETCH_CLASS:
+ case ZEND_INSTANCEOF:
+ if (opline->op2_type == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
+ }
+ break;
+ case ZEND_NEW:
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
+ }
+ 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:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_UNSET_OBJ:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ 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);
+ }
+ if (opline->op2_type == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
+ break;
+ case ZEND_RECV_INIT:
+ LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
+ break;
+ case ZEND_DECLARE_CLASS:
+ case ZEND_DECLARE_CLASS_DELAYED:
+ LITERAL_INFO(opline->op1.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:
+ case ZEND_UNSET_DIM:
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ 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);
+ }
+ if (opline->op2_type == IS_CONST) {
+ 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);
+ }
+ }
+ break;
+ default:
+ if (opline->op1_type == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
+ }
+ break;
+ }
+ opline++;
+ }
+
+#if DEBUG_COMPACT_LITERALS
+ {
+ int i, use_copy;
+ fprintf(stderr, "File %s func %s\n", op_array->filename->val,
+ op_array->function_name ? op_array->function_name->val : "main");
+ fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
+
+ for (i = 0; i < op_array->last_literal; i++) {
+ zval zv;
+ ZVAL_COPY_VALUE(&zv, op_array->literals + i);
+ use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
+ fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
+ if (use_copy) {
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ fflush(stderr);
+ }
+#endif
+
+ /* Merge equal constants */
+ j = 0;
+ zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
+ /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
+ zend_hash_init(&double_hash, 0, NULL, NULL, 0);
+ map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
+ memset(map, 0, op_array->last_literal * sizeof(int));
+ for (i = 0; i < op_array->last_literal; i++) {
+ if (!info[i].flags) {
+ /* unset literal */
+ zval_ptr_dtor_nogc(&op_array->literals[i]);
+ continue;
+ }
+ switch (Z_TYPE(op_array->literals[i])) {
+ case IS_NULL:
+ if (l_null < 0) {
+ l_null = j;
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ }
+ map[i] = l_null;
+ break;
+ case IS_FALSE:
+ if (l_false < 0) {
+ l_false = j;
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ }
+ map[i] = l_false;
+ break;
+ case IS_TRUE:
+ if (l_true < 0) {
+ l_true = j;
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ }
+ map[i] = l_true;
+ break;
+ case IS_LONG:
+ if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
+ if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
+ map[i] = Z_LVAL_P(pos);
+ } else {
+ map[i] = j;
+ ZVAL_LONG(&zv, j);
+ zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ }
+ } else {
+ ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
+ key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
+ ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
+ LITERAL_NUM_RELATED(info[i].flags) - 1;
+ if ((pos = zend_hash_find(&hash, key)) != NULL
+ && LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
+ map[i] = Z_LVAL_P(pos);
+ zval_ptr_dtor_nogc(&op_array->literals[i+1]);
+ } else {
+ map[i] = j;
+ ZVAL_LONG(&zv, j);
+ zend_hash_add_new(&hash, key, &zv);
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ op_array->literals[j+1] = op_array->literals[i+1];
+ info[j+1] = info[i+1];
+ }
+ j += 2;
+ }
+ zend_string_release_ex(key, 0);
+ i++;
+ }
+ break;
+ case IS_DOUBLE:
+ if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
+ map[i] = Z_LVAL_P(pos);
+ } else {
+ map[i] = j;
+ ZVAL_LONG(&zv, j);
+ 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];
+ }
+ j++;
+ }
+ break;
+ case IS_STRING: {
+ if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
+ key = zend_string_copy(Z_STR(op_array->literals[i]));
+ } 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;
+ }
+ 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 ||
+ ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
+ (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
+ zend_string_release_ex(key, 0);
+ map[i] = Z_LVAL_P(pos);
+ zval_ptr_dtor_nogc(&op_array->literals[i]);
+ n = LITERAL_NUM_RELATED(info[i].flags);
+ while (n > 1) {
+ i++;
+ zval_ptr_dtor_nogc(&op_array->literals[i]);
+ n--;
+ }
+ } else {
+ map[i] = j;
+ ZVAL_LONG(&zv, j);
+ 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];
+ }
+ j++;
+ n = LITERAL_NUM_RELATED(info[i].flags);
+ while (n > 1) {
+ i++;
+ if (i != j) op_array->literals[j] = op_array->literals[i];
+ j++;
+ n--;
+ }
+ }
+ break;
+ }
+ case IS_ARRAY:
+ if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
+ if (l_empty_arr < 0) {
+ l_empty_arr = j;
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ } else {
+ zval_ptr_dtor_nogc(&op_array->literals[i]);
+ }
+ map[i] = l_empty_arr;
+ break;
+ }
+ /* break missing intentionally */
+ default:
+ /* don't merge other types */
+ map[i] = j;
+ if (i != j) {
+ op_array->literals[j] = op_array->literals[i];
+ info[j] = info[i];
+ }
+ j++;
+ break;
+ }
+ }
+
+ /* Only clean "hash", as it will be reused in the loop below. */
+ zend_hash_clean(&hash);
+ zend_hash_destroy(&double_hash);
+ op_array->last_literal = j;
+
+ const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
+ memset(const_slot, -1, j * 6 * sizeof(int));
+ class_slot = const_slot + j;
+ func_slot = class_slot + j;
+ bind_var_slot = func_slot + j;
+ property_slot = bind_var_slot + j;
+ method_slot = property_slot + j;
+
+ /* Update opcodes to use new literals table */
+ cache_size = zend_op_array_extension_handles * sizeof(void*);
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ if (opline->op1_type == IS_CONST) {
+ opline->op1.constant = map[opline->op1.constant];
+ }
+ if (opline->op2_type == IS_CONST) {
+ opline->op2.constant = map[opline->op2.constant];
+ }
+ switch (opline->opcode) {
+ case ZEND_RECV_INIT:
+ case ZEND_RECV:
+ case ZEND_RECV_VARIADIC:
+ {
+ size_t num_classes = type_num_classes(op_array, opline->op1.num);
+ if (num_classes) {
+ opline->extended_value = cache_size;
+ cache_size += num_classes * sizeof(void *);
+ }
+ break;
+ }
+ case ZEND_VERIFY_RETURN_TYPE:
+ {
+ size_t num_classes = type_num_classes(op_array, 0);
+ if (num_classes) {
+ opline->op2.num = cache_size;
+ cache_size += num_classes * sizeof(void *);
+ }
+ 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 &&
+ property_slot[opline->op2.constant] >= 0) {
+ (opline+1)->extended_value = property_slot[opline->op2.constant];
+ } else {
+ (opline+1)->extended_value = cache_size;
+ cache_size += 3 * sizeof(void *);
+ if (opline->op1_type == IS_UNUSED) {
+ property_slot[opline->op2.constant] = (opline+1)->extended_value;
+ }
+ }
+ }
+ 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:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_UNSET_OBJ:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ if (opline->op2_type == IS_CONST) {
+ // 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 & ZEND_FETCH_OBJ_FLAGS);
+ } else {
+ 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 & ~ZEND_FETCH_OBJ_FLAGS;
+ }
+ }
+ }
+ break;
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ if (opline->op2_type == IS_CONST) {
+ // 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 & ZEND_ISEMPTY);
+ } else {
+ opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
+ cache_size += 3 * sizeof(void *);
+ if (opline->op1_type == IS_UNUSED) {
+ property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
+ }
+ }
+ }
+ break;
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ // op2 func
+ if (func_slot[opline->op2.constant] >= 0) {
+ opline->result.num = func_slot[opline->op2.constant];
+ } else {
+ opline->result.num = cache_size;
+ cache_size += sizeof(void *);
+ func_slot[opline->op2.constant] = opline->result.num;
+ }
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ if (opline->op2_type == IS_CONST) {
+ // op2 method
+ if (opline->op1_type == IS_UNUSED &&
+ method_slot[opline->op2.constant] >= 0) {
+ opline->result.num = method_slot[opline->op2.constant];
+ } else {
+ opline->result.num = cache_size;
+ cache_size += 2 * sizeof(void *);
+ if (opline->op1_type == IS_UNUSED) {
+ method_slot[opline->op2.constant] = opline->result.num;
+ }
+ }
+ }
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ if (opline->op2_type == IS_CONST) {
+ // op2 static method
+ if (opline->op1_type == IS_CONST) {
+ opline->result.num = add_static_slot(&hash, op_array,
+ opline->op1.constant,
+ opline->op2.constant,
+ LITERAL_STATIC_METHOD,
+ &cache_size);
+ } else {
+ opline->result.num = cache_size;
+ cache_size += 2 * sizeof(void *);
+ }
+ } else if (opline->op1_type == IS_CONST) {
+ // op1 class
+ if (class_slot[opline->op1.constant] >= 0) {
+ opline->result.num = class_slot[opline->op1.constant];
+ } else {
+ opline->result.num = cache_size;
+ cache_size += sizeof(void *);
+ class_slot[opline->op1.constant] = opline->result.num;
+ }
+ }
+ break;
+ case ZEND_DEFINED:
+ // op1 const
+ if (const_slot[opline->op1.constant] >= 0) {
+ opline->extended_value = const_slot[opline->op1.constant];
+ } else {
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ const_slot[opline->op1.constant] = opline->extended_value;
+ }
+ break;
+ case ZEND_FETCH_CONSTANT:
+ // op2 const
+ if (const_slot[opline->op2.constant] >= 0) {
+ opline->extended_value = const_slot[opline->op2.constant];
+ } else {
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ const_slot[opline->op2.constant] = opline->extended_value;
+ }
+ break;
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (opline->op1_type == IS_CONST) {
+ // op1/op2 class_const
+ opline->extended_value = add_static_slot(&hash, op_array,
+ opline->op1.constant,
+ opline->op2.constant,
+ LITERAL_CLASS_CONST,
+ &cache_size);
+ } else {
+ opline->extended_value = cache_size;
+ 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:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ 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:
+ 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) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
+ } else {
+ 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_FETCH_OBJ_FLAGS);
+ } else {
+ opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
+ class_slot[opline->op2.constant] = cache_size;
+ cache_size += sizeof(void *);
+ }
+ }
+ break;
+ case ZEND_FETCH_CLASS:
+ case ZEND_INSTANCEOF:
+ 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_NEW:
+ if (opline->op1_type == IS_CONST) {
+ // op1 class
+ if (class_slot[opline->op1.constant] >= 0) {
+ opline->op2.num = class_slot[opline->op1.constant];
+ } else {
+ opline->op2.num = cache_size;
+ cache_size += sizeof(void *);
+ class_slot[opline->op1.constant] = opline->op2.num;
+ }
+ }
+ break;
+ case ZEND_CATCH:
+ if (opline->op1_type == IS_CONST) {
+ // op1 class
+ if (class_slot[opline->op1.constant] >= 0) {
+ opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
+ } else {
+ opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
+ cache_size += sizeof(void *);
+ class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
+ }
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ // op2 bind var
+ if (bind_var_slot[opline->op2.constant] >= 0) {
+ opline->extended_value = bind_var_slot[opline->op2.constant];
+ } else {
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ bind_var_slot[opline->op2.constant] = opline->extended_value;
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_CLASS_DELAYED:
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_CHECK_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ opline->result.num = cache_size;
+ cache_size += 2 * sizeof(void *);
+ }
+ break;
+ }
+ opline++;
+ }
+ op_array->cache_size = cache_size;
+ zend_hash_destroy(&hash);
+ zend_arena_release(&ctx->arena, checkpoint);
+
+ if (1) {
+ opline = op_array->opcodes;
+ while (1) {
+ if (opline->opcode == ZEND_RECV_INIT) {
+ zval *val = &op_array->literals[opline->op2.constant];
+
+ if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
+ /* Ensure zval is aligned to 8 bytes */
+ op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
+ Z_CACHE_SLOT_P(val) = op_array->cache_size;
+ op_array->cache_size += sizeof(zval);
+ }
+ } else if (opline->opcode != ZEND_RECV) {
+ break;
+ }
+ opline++;
+ }
+ }
+
+#if DEBUG_COMPACT_LITERALS
+ {
+ int i, use_copy;
+ fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
+
+ for (i = 0; i < op_array->last_literal; i++) {
+ zval zv;
+ ZVAL_COPY_VALUE(&zv, op_array->literals + i);
+ use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
+ fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
+ if (use_copy) {
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ fflush(stderr);
+ }
+#endif
+ }
+}
diff --git a/Zend/Optimizer/compact_vars.c b/Zend/Optimizer/compact_vars.c
new file mode 100644
index 0000000000..e70e189827
--- /dev/null
+++ b/Zend/Optimizer/compact_vars.c
@@ -0,0 +1,123 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Removing unused variables |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_bitset.h"
+
+/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
+ * This pass does not operate on SSA form anymore. */
+void zend_optimizer_compact_vars(zend_op_array *op_array) {
+ int i;
+
+ ALLOCA_FLAG(use_heap1);
+ ALLOCA_FLAG(use_heap2);
+ uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
+ zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
+ uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
+ uint32_t num_cvs, num_tmps;
+
+ /* Determine which CVs are used */
+ zend_bitset_clear(used_vars, used_vars_len);
+ for (i = 0; i < op_array->last; i++) {
+ zend_op *opline = &op_array->opcodes[i];
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
+ }
+ if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
+ }
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
+ if (opline->opcode == ZEND_ROPE_INIT) {
+ uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
+ while (num > 1) {
+ num--;
+ zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
+ }
+ }
+ }
+ }
+
+ num_cvs = 0;
+ for (i = 0; i < op_array->last_var; i++) {
+ if (zend_bitset_in(used_vars, i)) {
+ vars_map[i] = num_cvs++;
+ } else {
+ vars_map[i] = (uint32_t) -1;
+ }
+ }
+
+ num_tmps = 0;
+ for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
+ if (zend_bitset_in(used_vars, i)) {
+ vars_map[i] = num_cvs + num_tmps++;
+ } else {
+ vars_map[i] = (uint32_t) -1;
+ }
+ }
+
+ free_alloca(used_vars, use_heap1);
+ if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
+ free_alloca(vars_map, use_heap2);
+ return;
+ }
+
+ ZEND_ASSERT(num_cvs <= op_array->last_var);
+ ZEND_ASSERT(num_tmps <= op_array->T);
+
+ /* Update CV and TMP references in opcodes */
+ for (i = 0; i < op_array->last; i++) {
+ zend_op *opline = &op_array->opcodes[i];
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
+ }
+ if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
+ }
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
+ }
+ }
+
+ /* Update CV name table */
+ if (num_cvs != op_array->last_var) {
+ if (num_cvs) {
+ zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
+ for (i = 0; i < op_array->last_var; i++) {
+ if (vars_map[i] != (uint32_t) -1) {
+ names[vars_map[i]] = op_array->vars[i];
+ } else {
+ zend_string_release_ex(op_array->vars[i], 0);
+ }
+ }
+ efree(op_array->vars);
+ op_array->vars = names;
+ } else {
+ for (i = 0; i < op_array->last_var; i++) {
+ zend_string_release_ex(op_array->vars[i], 0);
+ }
+ efree(op_array->vars);
+ op_array->vars = NULL;
+ }
+ op_array->last_var = num_cvs;
+ }
+
+ op_array->T = num_tmps;
+
+ free_alloca(vars_map, use_heap2);
+}
diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c
new file mode 100644
index 0000000000..940f1b6ee2
--- /dev/null
+++ b/Zend/Optimizer/dce.c
@@ -0,0 +1,628 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DCE - Dead Code Elimination |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "Optimizer/zend_optimizer_internal.h"
+#include "Optimizer/zend_inference.h"
+#include "Optimizer/zend_ssa.h"
+#include "Optimizer/zend_func_info.h"
+#include "Optimizer/zend_call_graph.h"
+#include "zend_bitset.h"
+
+/* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes
+ * that all instructions and phis are dead. Instructions with immediate side-effects are then marked
+ * as live. We then recursively (using a worklist) propagate liveness to the instructions that def
+ * the used operands.
+ *
+ * Notes:
+ * * This pass does not perform unreachable code elimination. This happens as part of the SCCP
+ * pass.
+ * * The DCE is performed without taking control-dependence into account, i.e. all conditional
+ * branches are assumed to be live. It's possible to take control-dependence into account using
+ * the DCE algorithm described by Cytron et al., however it requires the construction of a
+ * postdominator tree and of postdominance frontiers, which does not seem worthwhile at this
+ * point.
+ * * We separate intrinsic side-effects from potential side-effects in the form of notices thrown
+ * by the instruction (in case we want to make this configurable). See may_have_side_effects() and
+ * zend_may_throw().
+ * * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same
+ * order. There is an optimization option to allow reordering of dtor effects.
+ * * The algorithm is able to eliminate dead modifications of non-escaping arrays
+ * and objects as well as dead arrays and objects allocations.
+ */
+
+typedef struct {
+ zend_ssa *ssa;
+ zend_op_array *op_array;
+ zend_bitset instr_dead;
+ zend_bitset phi_dead;
+ zend_bitset instr_worklist;
+ zend_bitset phi_worklist;
+ zend_bitset phi_worklist_no_val;
+ uint32_t instr_worklist_len;
+ uint32_t phi_worklist_len;
+ unsigned reorder_dtor_effects : 1;
+} context;
+
+static inline bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
+ if (def < 0) {
+ /* This modification is not tracked by SSA, assume the worst */
+ return 1;
+ }
+ if (ssa->var_info[use].type & MAY_BE_REF) {
+ /* Modification of reference may have side-effect */
+ return 1;
+ }
+ return 0;
+}
+
+static inline bool may_have_side_effects(
+ zend_op_array *op_array, zend_ssa *ssa,
+ const zend_op *opline, const zend_ssa_op *ssa_op,
+ bool reorder_dtor_effects) {
+ switch (opline->opcode) {
+ case ZEND_NOP:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_QM_ASSIGN:
+ case ZEND_FREE:
+ case ZEND_FE_FREE:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_POW:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_BOOL_XOR:
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ case ZEND_BW_NOT:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_CAST:
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_SPACESHIP:
+ case ZEND_STRLEN:
+ case ZEND_COUNT:
+ case ZEND_GET_TYPE:
+ case ZEND_ISSET_ISEMPTY_THIS:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_FETCH_IS:
+ case ZEND_IN_ARRAY:
+ case ZEND_FUNC_NUM_ARGS:
+ case ZEND_FUNC_GET_ARGS:
+ case ZEND_ARRAY_KEY_EXISTS:
+ /* No side effects */
+ return 0;
+ case ZEND_ROPE_END:
+ /* TODO: Rope dce optimization, see #76446 */
+ return 1;
+ case ZEND_JMP:
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ /* For our purposes a jumps and branches are side effects. */
+ return 1;
+ case ZEND_BEGIN_SILENCE:
+ case ZEND_END_SILENCE:
+ case ZEND_ECHO:
+ case ZEND_INCLUDE_OR_EVAL:
+ case ZEND_THROW:
+ case ZEND_MATCH_ERROR:
+ case ZEND_EXT_STMT:
+ case ZEND_EXT_FCALL_BEGIN:
+ case ZEND_EXT_FCALL_END:
+ case ZEND_TICKS:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ /* Intrinsic side effects */
+ return 1;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ /* For now assume all calls have side effects */
+ return 1;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ /* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
+ * due to the prologue skipping code. */
+ return 1;
+ case ZEND_ASSIGN_REF:
+ return 1;
+ case ZEND_ASSIGN:
+ {
+ if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
+ return 1;
+ }
+ if (!reorder_dtor_effects) {
+ if (opline->op2_type != IS_CONST
+ && (OP2_INFO() & MAY_HAVE_DTOR)
+ && ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
+ /* DCE might shorten lifetime */
+ return 1;
+ }
+ }
+ return 0;
+ }
+ case ZEND_UNSET_VAR:
+ return 1;
+ case ZEND_UNSET_CV:
+ {
+ uint32_t t1 = OP1_INFO();
+ if (t1 & MAY_BE_REF) {
+ /* We don't consider uses as the LHS of an assignment as real uses during DCE, so
+ * an unset may be considered dead even if there is a later assignment to the
+ * variable. Removing the unset in this case would not be correct if the variable
+ * is a reference, because unset breaks references. */
+ return 1;
+ }
+ return 0;
+ }
+ case ZEND_PRE_INC:
+ case ZEND_POST_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_DEC:
+ return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def);
+ case ZEND_ASSIGN_OP:
+ return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
+ || 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)
+ || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
+ return 1;
+ }
+ if (!reorder_dtor_effects) {
+ opline++;
+ ssa_op++;
+ if (opline->op1_type != IS_CONST
+ && (OP1_INFO() & MAY_HAVE_DTOR)) {
+ /* DCE might shorten lifetime */
+ return 1;
+ }
+ }
+ return 0;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
+ || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
+ 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;
+ case ZEND_CHECK_VAR:
+ return (OP1_INFO() & MAY_BE_UNDEF) != 0;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ /* Model as not having side-effects -- let the side-effect be introduced by
+ * FE_FETCH if the array is not known to be non-empty. */
+ return (OP1_INFO() & MAY_BE_ANY) != MAY_BE_ARRAY;
+ default:
+ /* For everything we didn't handle, assume a side-effect */
+ return 1;
+ }
+}
+
+static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
+ zend_ssa_var *var = &ctx->ssa->vars[var_num];
+ if (var->definition >= 0) {
+ if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
+ zend_bitset_incl(ctx->instr_worklist, var->definition);
+ }
+ } else if (var->definition_phi) {
+ if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
+ zend_bitset_incl(ctx->phi_worklist, var_num);
+ }
+ }
+}
+
+static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
+ zend_ssa_var *var = &ctx->ssa->vars[var_num];
+ if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
+ zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
+ }
+}
+
+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)
+ || (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)
+ || (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);
+ }
+ }
+}
+
+static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
+ zend_ssa *ssa = ctx->ssa;
+ int source;
+ FOREACH_PHI_SOURCE(phi, source) {
+ add_to_worklists(ctx, source, check);
+ } FOREACH_PHI_SOURCE_END();
+}
+
+static inline bool is_var_dead(context *ctx, int var_num) {
+ zend_ssa_var *var = &ctx->ssa->vars[var_num];
+ if (var->definition_phi) {
+ return zend_bitset_in(ctx->phi_dead, var_num);
+ } else if (var->definition >= 0) {
+ return zend_bitset_in(ctx->instr_dead, var->definition);
+ } else {
+ /* Variable has no definition, so either the definition has already been removed (var is
+ * dead) or this is one of the implicit variables at the start of the function (for our
+ * purposes live) */
+ return var_num >= ctx->op_array->last_var;
+ }
+}
+
+// Sometimes we can mark the var as EXT_UNUSED
+static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
+ if (use_chain >= 0) {
+ return 0;
+ }
+ zend_ssa_var *var = &ctx->ssa->vars[free_var];
+ int def = var->definition;
+
+ if (def >= 0) {
+ zend_ssa_op *def_op = &ctx->ssa->ops[def];
+
+ if (def_op->result_def == free_var
+ && var->phi_use_chain == NULL
+ && var->use_chain == (opline - ctx->op_array->opcodes)) {
+ zend_op *def_opline = &ctx->op_array->opcodes[def];
+
+ switch (def_opline->opcode) {
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ 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:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ case ZEND_DO_FCALL:
+ case ZEND_INCLUDE_OR_EVAL:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ case ZEND_ASSERT_CHECK:
+ def_opline->result_type = IS_UNUSED;
+ def_opline->result.var = 0;
+ def_op->result_def = -1;
+ var->definition = -1;
+ return 1;
+ default:
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline bool is_free_of_live_var(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+ switch (opline->opcode) {
+ case ZEND_FREE:
+ /* It is always safe to remove FREEs of non-refcounted values, even if they are live. */
+ if (!(ctx->ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ return 0;
+ }
+ /* break missing intentionally */
+ case ZEND_FE_FREE:
+ return !is_var_dead(ctx, ssa_op->op1_use);
+ default:
+ return 0;
+ }
+}
+
+/* Returns whether the instruction has been DCEd */
+static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+ zend_ssa *ssa = ctx->ssa;
+ int free_var = -1;
+ zend_uchar free_var_type;
+
+ if (opline->opcode == ZEND_NOP) {
+ return 0;
+ }
+
+ /* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
+ if (is_free_of_live_var(ctx, opline, ssa_op)) {
+ return 0;
+ }
+
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
+ if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
+ if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)
+ && opline->opcode != ZEND_CASE
+ && opline->opcode != ZEND_CASE_STRICT) {
+ free_var = ssa_op->op1_use;
+ free_var_type = opline->op1_type;
+ }
+ }
+ }
+ if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
+ if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
+ if (ssa->var_info[ssa_op->op2_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+ if (free_var >= 0) {
+ // TODO: We can't free two vars. Keep instruction alive.
+ zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
+ return 0;
+ }
+ free_var = ssa_op->op2_use;
+ free_var_type = opline->op2_type;
+ }
+ }
+ }
+
+ zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
+ zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
+
+ if (free_var >= 0) {
+ opline->opcode = ZEND_FREE;
+ opline->op1.var = EX_NUM_TO_VAR(ssa->vars[free_var].var);
+ opline->op1_type = free_var_type;
+
+ ssa_op->op1_use = free_var;
+ ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
+ ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
+ return 0;
+ }
+ return 1;
+}
+
+static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
+ int common_source = -1;
+ int source;
+ FOREACH_PHI_SOURCE(phi, source) {
+ if (common_source == -1) {
+ common_source = source;
+ } else if (common_source != source && source != phi->ssa_var) {
+ return -1;
+ }
+ } FOREACH_PHI_SOURCE_END();
+ ZEND_ASSERT(common_source != -1);
+ return common_source;
+}
+
+static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
+ zend_ssa *ssa = ctx->ssa;
+ if (phi->pi < 0) {
+ /* Phi assignment with identical source operands */
+ int common_source = get_common_phi_source(ssa, phi);
+ if (common_source >= 0) {
+ zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1);
+ zend_ssa_remove_phi(ssa, phi);
+ }
+ } else {
+ /* Pi assignment that is only used in Phi/Pi assignments */
+ // TODO What if we want to rerun type inference after DCE? Maybe separate this?
+ /*ZEND_ASSERT(phi->sources[0] != -1);
+ if (ssa->vars[phi->ssa_var].use_chain < 0) {
+ zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1);
+ zend_ssa_remove_phi(ssa, phi);
+ }*/
+ }
+}
+
+static inline bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
+ if (ssa_op->op1_def >= 0
+ && ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
+ return 1;
+ }
+ if (ssa_op->op2_def >= 0
+ && ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
+ return 1;
+ }
+ if (ssa_op->result_def >= 0
+ && ssa->vars[ssa_op->result_def].var < op_array->num_args) {
+ return 1;
+ }
+ return 0;
+}
+
+int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects) {
+ int i;
+ zend_ssa_phi *phi;
+ int removed_ops = 0;
+
+ /* DCE of CV operations that changes arguments may affect vararg functions. */
+ bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0;
+
+ context ctx;
+ ctx.ssa = ssa;
+ ctx.op_array = op_array;
+ ctx.reorder_dtor_effects = reorder_dtor_effects;
+
+ /* We have no dedicated phi vector, so we use the whole ssa var vector instead */
+ ctx.instr_worklist_len = zend_bitset_len(op_array->last);
+ ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
+ memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
+ ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
+ ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
+ memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
+ ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
+ memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
+
+ /* Optimistically assume all instructions and phis to be dead */
+ ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
+ memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
+ ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
+ memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
+
+ /* Mark reacable instruction without side effects as dead */
+ int b = ssa->cfg.blocks_count;
+ while (b > 0) {
+ int op_data = -1;
+
+ b--;
+ zend_basic_block *block = &ssa->cfg.blocks[b];
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
+ continue;
+ }
+ i = block->start + block->len;
+ while (i > block->start) {
+ i--;
+
+ if (op_array->opcodes[i].opcode == ZEND_OP_DATA) {
+ op_data = i;
+ continue;
+ }
+
+ 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], ssa, 0);
+ if (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], &ssa->ops[i], op_array, ssa)
+ || (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) {
+ if (op_array->opcodes[i].opcode == ZEND_NEW
+ && op_array->opcodes[i+1].opcode == ZEND_DO_FCALL
+ && ssa->ops[i].result_def >= 0
+ && ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ 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], ssa, 0);
+ if (op_data >= 0) {
+ add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
+ }
+ }
+ } else {
+ zend_bitset_incl(ctx.instr_dead, i);
+ if (op_data >= 0) {
+ zend_bitset_incl(ctx.instr_dead, op_data);
+ }
+ }
+ op_data = -1;
+ }
+ }
+
+ /* Propagate liveness backwards to all definitions of used vars */
+ while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len)
+ || !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], 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], ssa, 1);
+ }
+ }
+ while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) {
+ zend_bitset_excl(ctx.phi_dead, i);
+ zend_bitset_excl(ctx.phi_worklist_no_val, i);
+ add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1);
+ }
+ }
+
+ /* 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]);
+ } ZEND_BITSET_FOREACH_END();
+
+ /* Improper uses don't count as "uses" for the purpose of instruction elimination,
+ * but we have to retain phis defining them.
+ * Propagate this information backwards, marking any phi with an improperly used
+ * target as non-dead. */
+ while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) {
+ zend_ssa_phi *phi = ssa->vars[i].definition_phi;
+ int source;
+ zend_bitset_excl(ctx.phi_dead, i);
+ FOREACH_PHI_SOURCE(phi, source) {
+ add_to_phi_worklist_no_val(&ctx, source);
+ } FOREACH_PHI_SOURCE_END();
+ }
+
+ /* Now collect the actually dead phis */
+ FOREACH_PHI(phi) {
+ if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) {
+ zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
+ zend_ssa_remove_phi(ssa, phi);
+ } else {
+ /* Remove trivial phis (phis with identical source operands) */
+ try_remove_trivial_phi(&ctx, phi);
+ }
+ } FOREACH_PHI_END();
+
+ return removed_ops;
+}
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
new file mode 100644
index 0000000000..fe06de276b
--- /dev/null
+++ b/Zend/Optimizer/dfa_pass.c
@@ -0,0 +1,1637 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+#ifndef ZEND_DEBUG_DFA
+# define ZEND_DEBUG_DFA ZEND_DEBUG
+#endif
+
+#if ZEND_DEBUG_DFA
+# include "ssa_integrity.c"
+#endif
+
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
+{
+ uint32_t build_flags;
+
+ if (op_array->last_try_catch) {
+ /* TODO: we can't analyze functions with try/catch/finally ??? */
+ return FAILURE;
+ }
+
+ /* Build SSA */
+ memset(ssa, 0, sizeof(zend_ssa));
+
+ if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
+ /* TODO: we can't analyze functions with indirect variable access ??? */
+ return FAILURE;
+ }
+
+ if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
+ }
+
+ /* Compute Dominators Tree */
+ if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ /* Identify reducible and irreducible loops */
+ if (zend_cfg_identify_loops(op_array, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
+ zend_dump_dominators(op_array, &ssa->cfg);
+ }
+
+ build_flags = 0;
+ if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
+ build_flags |= ZEND_SSA_DEBUG_LIVENESS;
+ }
+ if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
+ build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
+ }
+ if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "dfa ssa", ssa);
+ }
+
+
+ if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_ssa_escape_analysis(ctx->script, op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
+ zend_dump_ssa_variables(op_array, ssa, 0);
+ }
+
+ return SUCCESS;
+}
+
+static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_basic_block *blocks_end = blocks + ssa->cfg.blocks_count;
+ zend_basic_block *b;
+ zend_func_info *func_info;
+ int j;
+ uint32_t i = 0;
+ uint32_t target = 0;
+ uint32_t *shiftlist;
+ ALLOCA_FLAG(use_heap);
+
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
+ memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
+ /* remove empty callee_info */
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ zend_call_info **call_info = &func_info->callee_info;
+ while ((*call_info)) {
+ if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) {
+ *call_info = (*call_info)->next_callee;
+ } else {
+ call_info = &(*call_info)->next_callee;
+ }
+ }
+ }
+
+ for (b = blocks; b < blocks_end; b++) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ if (b->len) {
+ uint32_t new_start, old_end;
+ while (i < b->start) {
+ shiftlist[i] = i - target;
+ i++;
+ }
+
+ if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+ /* Only keep the FREE for the loop var */
+ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+ || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+ b->len = 1;
+ }
+
+ new_start = target;
+ old_end = b->start + b->len;
+ while (i < old_end) {
+ shiftlist[i] = i - target;
+ if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP)) {
+ if (i != target) {
+ op_array->opcodes[target] = op_array->opcodes[i];
+ ssa->ops[target] = ssa->ops[i];
+ ssa->cfg.map[target] = b - blocks;
+ }
+ target++;
+ }
+ i++;
+ }
+ b->start = new_start;
+ if (target != old_end) {
+ zend_op *opline;
+ zend_op *new_opline;
+
+ b->len = target - b->start;
+ opline = op_array->opcodes + old_end - 1;
+ if (opline->opcode == ZEND_NOP) {
+ continue;
+ }
+
+ new_opline = op_array->opcodes + target - 1;
+ zend_optimizer_migrate_jump(op_array, new_opline, opline);
+ }
+ } else {
+ b->start = target;
+ }
+ } else {
+ b->start = target;
+ b->len = 0;
+ }
+ }
+
+ if (target != op_array->last) {
+ /* reset rest opcodes */
+ for (i = target; i < op_array->last; i++) {
+ MAKE_NOP(op_array->opcodes + i);
+ }
+
+ /* update SSA variables */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].definition >= 0) {
+ ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
+ }
+ if (ssa->vars[j].use_chain >= 0) {
+ ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
+ }
+ }
+ for (i = 0; i < op_array->last; i++) {
+ if (ssa->ops[i].op1_use_chain >= 0) {
+ ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
+ }
+ if (ssa->ops[i].op2_use_chain >= 0) {
+ ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
+ }
+ if (ssa->ops[i].res_use_chain >= 0) {
+ ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
+ }
+ }
+
+ /* update branch targets */
+ for (b = blocks; b < blocks_end; b++) {
+ if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
+ zend_op *opline = op_array->opcodes + b->start + b->len - 1;
+ zend_optimizer_shift_jump(op_array, opline, shiftlist);
+ }
+ }
+
+ /* 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];
+ op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+ if (op_array->try_catch_array[j].finally_op) {
+ op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+ op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+ }
+ }
+
+ /* update early binding list */
+ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
+ uint32_t *opline_num = &ctx->script->first_early_binding_opline;
+
+ ZEND_ASSERT(op_array == &ctx->script->main_op_array);
+ do {
+ *opline_num -= shiftlist[*opline_num];
+ opline_num = &op_array->opcodes[*opline_num].result.opline_num;
+ } while (*opline_num != (uint32_t)-1);
+ }
+
+ /* update call graph */
+ if (func_info) {
+ zend_call_info *call_info = func_info->callee_info;
+ while (call_info) {
+ call_info->caller_init_opline -=
+ shiftlist[call_info->caller_init_opline - op_array->opcodes];
+ if (call_info->caller_call_opline) {
+ call_info->caller_call_opline -=
+ shiftlist[call_info->caller_call_opline - op_array->opcodes];
+ }
+ call_info = call_info->next_callee;
+ }
+ }
+
+ op_array->last = target;
+ }
+ free_alloca(shiftlist, use_heap);
+}
+
+static 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 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];
+ zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
+ zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
+
+ /* TODO: It would be better to rewrite this without using def_info,
+ * which may not be an exact representation of the type. */
+ if (use_info->type & MAY_BE_REF) {
+ return 0;
+ }
+
+ /* A type is possible that is not in the allowed types */
+ if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
+ return 0;
+ }
+
+ /* These types are not represented exactly */
+ if (ZEND_TYPE_FULL_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE|MAY_BE_STATIC)) {
+ return 0;
+ }
+
+ if (ZEND_TYPE_HAS_CLASS(info->type)) {
+ if (!use_info->ce || !def_info->ce || !safe_instanceof(use_info->ce, def_info->ce)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static bool opline_supports_assign_contraction(
+ zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
+ if (opline->opcode == ZEND_NEW) {
+ /* see Zend/tests/generators/aborted_yield_during_new.phpt */
+ return 0;
+ }
+
+ if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
+ || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
+ /* Function calls may dtor the return value after it has already been written -- allow
+ * direct assignment only for types where a double-dtor does not matter. */
+ uint32_t type = ssa->var_info[src_var].type;
+ uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
+ return !((type & MAY_BE_ANY) & ~simple);
+ }
+
+ if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
+ /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
+ * eliding the temporary variable would thus yield an incorrect result. */
+ return opline->op1_type != IS_CV || opline->op1.var != cv_var;
+ }
+
+ if (opline->opcode == ZEND_INIT_ARRAY) {
+ /* INIT_ARRAY initializes the result array before reading key/value. */
+ return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
+ && (opline->op2_type != IS_CV || opline->op2.var != cv_var);
+ }
+
+ if (opline->opcode == ZEND_CAST
+ && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
+ /* CAST to array/object may initialize the result to an empty array/object before
+ * reading the expression. */
+ return opline->op1_type != IS_CV || opline->op1.var != cv_var;
+ }
+
+ return 1;
+}
+
+int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ int removed_ops = 0;
+
+ if (func_info->callee_info) {
+ zend_call_info *call_info = func_info->callee_info;
+
+ do {
+ if (call_info->caller_call_opline
+ && call_info->caller_call_opline->opcode == ZEND_DO_ICALL
+ && call_info->callee_func
+ && zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")
+ && (call_info->caller_init_opline->extended_value == 2
+ || (call_info->caller_init_opline->extended_value == 3
+ && (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
+ && (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
+
+ zend_op *send_array;
+ zend_op *send_needly;
+ bool strict = 0;
+
+ if (call_info->caller_init_opline->extended_value == 2) {
+ send_array = call_info->caller_call_opline - 1;
+ send_needly = call_info->caller_call_opline - 2;
+ } else {
+ if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
+ strict = 1;
+ }
+ send_array = call_info->caller_call_opline - 2;
+ send_needly = call_info->caller_call_opline - 3;
+ }
+
+ if (send_array->opcode == ZEND_SEND_VAL
+ && send_array->op1_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
+ && (send_needly->opcode == ZEND_SEND_VAL
+ || send_needly->opcode == ZEND_SEND_VAR)
+ ) {
+ int ok = 1;
+
+ HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
+ HashTable *dst;
+ zval *val, tmp;
+ zend_ulong idx;
+
+ ZVAL_TRUE(&tmp);
+ dst = zend_new_array(zend_hash_num_elements(src));
+ if (strict) {
+ ZEND_HASH_FOREACH_VAL(src, val) {
+ if (Z_TYPE_P(val) == IS_STRING) {
+ zend_hash_add(dst, Z_STR_P(val), &tmp);
+ } else if (Z_TYPE_P(val) == IS_LONG) {
+ zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
+ } else {
+ zend_array_destroy(dst);
+ ok = 0;
+ break;
+ }
+ } ZEND_HASH_FOREACH_END();
+ } else {
+ ZEND_HASH_FOREACH_VAL(src, val) {
+ if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
+ zend_array_destroy(dst);
+ ok = 0;
+ break;
+ }
+ zend_hash_add(dst, Z_STR_P(val), &tmp);
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ if (ok) {
+ uint32_t op_num = send_needly - op_array->opcodes;
+ zend_ssa_op *ssa_op = ssa->ops + op_num;
+
+ if (ssa_op->op1_use >= 0) {
+ /* Reconstruct SSA */
+ int var_num = ssa_op->op1_use;
+ zend_ssa_var *var = ssa->vars + var_num;
+
+ ZEND_ASSERT(ssa_op->op1_def < 0);
+ zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
+ ssa_op->op1_use = -1;
+ ssa_op->op1_use_chain = -1;
+ op_num = call_info->caller_call_opline - op_array->opcodes;
+ ssa_op = ssa->ops + op_num;
+ ssa_op->op1_use = var_num;
+ ssa_op->op1_use_chain = var->use_chain;
+ var->use_chain = op_num;
+ }
+
+ ZVAL_ARR(&tmp, dst);
+
+ /* Update opcode */
+ call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
+ call_info->caller_call_opline->extended_value = strict;
+ call_info->caller_call_opline->op1_type = send_needly->op1_type;
+ call_info->caller_call_opline->op1.num = send_needly->op1.num;
+ call_info->caller_call_opline->op2_type = IS_CONST;
+ call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ if (call_info->caller_init_opline->extended_value == 3) {
+ MAKE_NOP(call_info->caller_call_opline - 1);
+ }
+ MAKE_NOP(call_info->caller_init_opline);
+ MAKE_NOP(send_needly);
+ MAKE_NOP(send_array);
+ removed_ops++;
+
+ op_num = call_info->caller_call_opline - op_array->opcodes;
+ ssa_op = ssa->ops + op_num;
+ if (ssa_op->result_def >= 0) {
+ int var = ssa_op->result_def;
+ int use = ssa->vars[var].use_chain;
+
+ /* If the result is used only in a JMPZ/JMPNZ, replace result type with
+ * IS_TMP_VAR, which will enable use of smart branches. Don't do this
+ * in other cases, as not all opcodes support both VAR and TMP. */
+ if (ssa->vars[var].phi_use_chain == NULL
+ && ssa->ops[use].op1_use == var
+ && ssa->ops[use].op1_use_chain == -1
+ && (op_array->opcodes[use].opcode == ZEND_JMPZ
+ || op_array->opcodes[use].opcode == ZEND_JMPNZ)) {
+ call_info->caller_call_opline->result_type = IS_TMP_VAR;
+ op_array->opcodes[use].op1_type = IS_TMP_VAR;
+ }
+ }
+ }
+ }
+ }
+ call_info = call_info->next_callee;
+ } while (call_info);
+ }
+
+ return removed_ops;
+}
+
+static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, zend_basic_block *block)
+{
+ if (block->successors_count == 2) {
+ if (block->successors[1] != block->successors[0]) {
+ zend_ssa_remove_predecessor(ssa, block_num, block->successors[1]);
+ }
+ block->successors_count = 1;
+ }
+}
+
+static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, zend_basic_block *block)
+{
+ if (block->successors_count == 2) {
+ if (block->successors[1] != block->successors[0]) {
+ zend_ssa_remove_predecessor(ssa, block_num, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ }
+ block->successors_count = 1;
+ }
+}
+
+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) {
+ zend_op *opline = &op_array->opcodes[block->start + block->len - 1];
+
+ if (opline->opcode == ZEND_NOP) {
+ block->len--;
+ } else {
+ break;
+ }
+ }
+}
+
+static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int new_pred) {
+ zend_basic_block *block = &ssa->cfg.blocks[block_id];
+ int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
+ zend_ssa_phi *phi;
+
+ int i;
+ int old_pred_idx = -1;
+ int new_pred_idx = -1;
+ for (i = 0; i < block->predecessors_count; i++) {
+ if (predecessors[i] == old_pred) {
+ old_pred_idx = i;
+ }
+ if (predecessors[i] == new_pred) {
+ new_pred_idx = i;
+ }
+ }
+
+ ZEND_ASSERT(old_pred_idx != -1);
+ if (new_pred_idx == -1) {
+ /* If the new predecessor doesn't exist yet, simply rewire the old one */
+ predecessors[old_pred_idx] = new_pred;
+ } else {
+ /* Otherwise, rewiring the old predecessor would make the new predecessor appear
+ * twice, which violates our CFG invariants. Remove the old predecessor instead. */
+ memmove(
+ predecessors + old_pred_idx,
+ predecessors + old_pred_idx + 1,
+ sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
+ );
+
+ /* Also remove the corresponding phi node entries */
+ for (phi = ssa->blocks[block_id].phis; phi; phi = phi->next) {
+ memmove(
+ phi->sources + old_pred_idx,
+ phi->sources + old_pred_idx + 1,
+ sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
+ );
+ }
+
+ block->predecessors_count--;
+ }
+}
+
+static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa, int from, int to, int new_to)
+{
+ zend_basic_block *src = &ssa->cfg.blocks[from];
+ zend_basic_block *old = &ssa->cfg.blocks[to];
+ zend_basic_block *dst = &ssa->cfg.blocks[new_to];
+ int i;
+ zend_op *opline;
+
+ for (i = 0; i < src->successors_count; i++) {
+ if (src->successors[i] == to) {
+ src->successors[i] = new_to;
+ }
+ }
+
+ if (src->len > 0) {
+ opline = op_array->opcodes + src->start + src->len - 1;
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_ASSERT(ZEND_OP1_JMP_ADDR(opline) == op_array->opcodes + old->start);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + dst->start);
+ break;
+ case ZEND_JMPZNZ:
+ if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
+ }
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
+ }
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
+ }
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
+ }
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ zval *zv;
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) == old->start) {
+ Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
+ }
+ } ZEND_HASH_FOREACH_END();
+ if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
+ }
+ break;
+ }
+ }
+ }
+
+ replace_predecessor(ssa, new_to, to, from);
+}
+
+static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, int block_num)
+{
+ if (block->predecessors_count == 1 && ssa->blocks[block_num].phis == NULL) {
+ int *predecessors, i;
+
+ ZEND_ASSERT(block->successors_count == 1);
+ predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
+ for (i = 0; i < block->predecessors_count; i++) {
+ zend_ssa_replace_control_link(op_array, ssa, predecessors[i], block_num, block->successors[0]);
+ }
+ zend_ssa_remove_block(op_array, ssa, block_num);
+ }
+}
+
+static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
+{
+ int removed_ops = 0;
+ int block_num = 0;
+
+ for (block_num = 1; block_num < ssa->cfg.blocks_count; block_num++) {
+ zend_basic_block *block = &ssa->cfg.blocks[block_num];
+
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
+ continue;
+ }
+ compress_block(op_array, block);
+ if (block->len == 0) {
+ zend_ssa_unlink_block(op_array, ssa, block, block_num);
+ }
+ }
+
+ block_num = 0;
+ while (block_num < ssa->cfg.blocks_count
+ && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)) {
+ block_num++;
+ }
+ while (block_num < ssa->cfg.blocks_count) {
+ int next_block_num = block_num + 1;
+ zend_basic_block *block = &ssa->cfg.blocks[block_num];
+ uint32_t op_num;
+ zend_op *opline;
+ zend_ssa_op *ssa_op;
+
+ while (next_block_num < ssa->cfg.blocks_count
+ && !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
+ next_block_num++;
+ }
+
+ if (block->len) {
+ op_num = block->start + block->len - 1;
+ opline = op_array->opcodes + op_num;
+ ssa_op = ssa->ops + op_num;
+
+ switch (opline->opcode) {
+ case ZEND_JMP:
+optimize_jmp:
+ if (block->successors[0] == next_block_num) {
+ MAKE_NOP(opline);
+ removed_ops++;
+ goto optimize_nop;
+ }
+ break;
+ case ZEND_JMPZ:
+optimize_jmpz:
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ MAKE_NOP(opline);
+ removed_ops++;
+ take_successor_1(ssa, block_num, block);
+ goto optimize_nop;
+ } else {
+ opline->opcode = ZEND_JMP;
+ COPY_NODE(opline->op1, opline->op2);
+ take_successor_0(ssa, block_num, block);
+ goto optimize_jmp;
+ }
+ } else {
+ if (block->successors[0] == next_block_num) {
+ take_successor_0(ssa, block_num, block);
+ if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->op2.num = 0;
+ } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ removed_ops++;
+ goto optimize_nop;
+ } else {
+ opline->opcode = ZEND_FREE;
+ opline->op2.num = 0;
+ }
+ }
+ }
+ break;
+ case ZEND_JMPNZ:
+optimize_jmpnz:
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ opline->opcode = ZEND_JMP;
+ COPY_NODE(opline->op1, opline->op2);
+ take_successor_0(ssa, block_num, block);
+ goto optimize_jmp;
+ } else {
+ MAKE_NOP(opline);
+ removed_ops++;
+ take_successor_1(ssa, block_num, block);
+ goto optimize_nop;
+ }
+ } else if (block->successors_count == 2) {
+ if (block->successors[0] == next_block_num) {
+ take_successor_0(ssa, block_num, block);
+ if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->op2.num = 0;
+ } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ removed_ops++;
+ goto optimize_nop;
+ } else {
+ opline->opcode = ZEND_FREE;
+ opline->op2.num = 0;
+ }
+ }
+ }
+ break;
+ case ZEND_JMPZNZ:
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ take_successor_1(ssa, block_num, block);
+ } else {
+ zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ take_successor_0(ssa, block_num, block);
+ }
+ opline->op1_type = IS_UNUSED;
+ opline->extended_value = 0;
+ opline->opcode = ZEND_JMP;
+ goto optimize_jmp;
+ } else if (block->successors_count == 2) {
+ if (block->successors[0] == block->successors[1]) {
+ take_successor_0(ssa, block_num, block);
+ if (block->successors[0] == next_block_num) {
+ if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->op2.num = 0;
+ } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ removed_ops++;
+ goto optimize_nop;
+ } else {
+ opline->opcode = ZEND_FREE;
+ opline->op2.num = 0;
+ }
+ } else if ((opline->op1_type == IS_CV && !(OP1_INFO() & MAY_BE_UNDEF)) || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ ZEND_ASSERT(ssa_op->op1_use >= 0);
+ zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
+ ssa_op->op1_use = -1;
+ ssa_op->op1_use_chain = -1;
+ opline->opcode = ZEND_JMP;
+ opline->op1_type = IS_UNUSED;
+ opline->op1.num = opline->op2.num;
+ goto optimize_jmp;
+ }
+ }
+ }
+ break;
+ case ZEND_JMPZ_EX:
+ if (ssa->vars[ssa_op->result_def].use_chain < 0
+ && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
+ opline->opcode = ZEND_JMPZ;
+ opline->result_type = IS_UNUSED;
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ goto optimize_jmpz;
+ } else if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ opline->opcode = ZEND_QM_ASSIGN;
+ take_successor_1(ssa, block_num, block);
+ }
+ }
+ break;
+ case ZEND_JMPNZ_EX:
+ if (ssa->vars[ssa_op->result_def].use_chain < 0
+ && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
+ opline->opcode = ZEND_JMPNZ;
+ opline->result_type = IS_UNUSED;
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ goto optimize_jmpnz;
+ } else if (opline->op1_type == IS_CONST) {
+ if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ opline->opcode = ZEND_QM_ASSIGN;
+ take_successor_1(ssa, block_num, block);
+ }
+ }
+ break;
+ case ZEND_JMP_SET:
+ if (ssa->vars[ssa_op->result_def].use_chain < 0
+ && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
+ opline->opcode = ZEND_JMPNZ;
+ opline->result_type = IS_UNUSED;
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ goto optimize_jmpnz;
+ } else if (opline->op1_type == IS_CONST) {
+ if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
+ MAKE_NOP(opline);
+ removed_ops++;
+ take_successor_1(ssa, block_num, block);
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ goto optimize_nop;
+ }
+ }
+ break;
+ case ZEND_COALESCE:
+ {
+ zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
+ if (opline->op1_type == IS_CONST
+ && var->use_chain < 0 && var->phi_use_chain == NULL) {
+ if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ MAKE_NOP(opline);
+ removed_ops++;
+ take_successor_1(ssa, block_num, block);
+ goto optimize_nop;
+ } else {
+ opline->opcode = ZEND_JMP;
+ opline->result_type = IS_UNUSED;
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ COPY_NODE(opline->op1, opline->op2);
+ take_successor_0(ssa, block_num, block);
+ goto optimize_jmp;
+ }
+ }
+ break;
+ }
+ case ZEND_JMP_NULL:
+ {
+ zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
+ if (opline->op1_type == IS_CONST
+ && var->use_chain < 0 && var->phi_use_chain == NULL) {
+ if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
+ opline->opcode = ZEND_JMP;
+ opline->result_type = IS_UNUSED;
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ COPY_NODE(opline->op1, opline->op2);
+ take_successor_0(ssa, block_num, block);
+ goto optimize_jmp;
+ } else {
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ MAKE_NOP(opline);
+ removed_ops++;
+ take_successor_1(ssa, block_num, block);
+ goto optimize_nop;
+ }
+ }
+ break;
+ }
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ if (opline->op1_type == IS_CONST) {
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ zend_uchar type = Z_TYPE_P(zv);
+ bool correct_type =
+ (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
+ || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
+ || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
+
+ if (!correct_type) {
+ removed_ops++;
+ MAKE_NOP(opline);
+ opline->extended_value = 0;
+ take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]);
+ goto optimize_nop;
+ } else {
+ HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
+ zval *jmp_zv = type == IS_LONG
+ ? zend_hash_index_find(jmptable, Z_LVAL_P(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);
+ if (block->len == 0) {
+ if (block_num > 0) {
+ zend_ssa_unlink_block(op_array, ssa, block, block_num);
+ /* backtrack to previous basic block */
+ do {
+ block_num--;
+ } while (block_num >= 0
+ && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE));
+ if (block_num >= 0) {
+ continue;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ block_num = next_block_num;
+ }
+
+ return removed_ops;
+}
+
+static int zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ssa, int def, int cv_var)
+{
+ int result_var = ssa->ops[def].result_def;
+ int cv = EX_NUM_TO_VAR(ssa->vars[cv_var].var);
+
+ if (result_var >= 0
+ && !(ssa->var_info[cv_var].type & MAY_BE_REF)
+ && ssa->vars[cv_var].alias == NO_ALIAS
+ && ssa->vars[result_var].phi_use_chain == NULL
+ && ssa->vars[result_var].sym_use_chain == NULL) {
+ int use = ssa->vars[result_var].use_chain;
+
+ if (use >= 0
+ && zend_ssa_next_use(ssa->ops, result_var, use) < 0
+ && op_array->opcodes[use].opcode != ZEND_FREE
+ && op_array->opcodes[use].opcode != ZEND_SEND_VAL
+ && op_array->opcodes[use].opcode != ZEND_SEND_VAL_EX
+ && op_array->opcodes[use].opcode != ZEND_VERIFY_RETURN_TYPE) {
+ if (use > def) {
+ int i = use;
+ const zend_op *opline = &op_array->opcodes[use];
+
+ while (i > def) {
+ if ((opline->op1_type == IS_CV && opline->op1.var == cv)
+ || (opline->op2_type == IS_CV && opline->op2.var == cv)
+ || (opline->result_type == IS_CV && opline->result.var == cv)) {
+ return 0;
+ }
+ opline--;
+ i--;
+ }
+
+ /* Update opcodes and reconstruct SSA */
+ ssa->vars[result_var].definition = -1;
+ ssa->vars[result_var].use_chain = -1;
+ ssa->ops[def].result_def = -1;
+
+ op_array->opcodes[def].result_type = IS_UNUSED;
+ op_array->opcodes[def].result.var = 0;
+
+ if (ssa->ops[use].op1_use == result_var) {
+ ssa->ops[use].op1_use = cv_var;
+ ssa->ops[use].op1_use_chain = ssa->vars[cv_var].use_chain;
+ ssa->vars[cv_var].use_chain = use;
+
+ op_array->opcodes[use].op1_type = IS_CV;
+ op_array->opcodes[use].op1.var = cv;
+ } else if (ssa->ops[use].op2_use == result_var) {
+ ssa->ops[use].op2_use = cv_var;
+ ssa->ops[use].op2_use_chain = ssa->vars[cv_var].use_chain;
+ ssa->vars[cv_var].use_chain = use;
+
+ op_array->opcodes[use].op2_type = IS_CV;
+ op_array->opcodes[use].op2.var = cv;
+ } else if (ssa->ops[use].result_use == result_var) {
+ ssa->ops[use].result_use = cv_var;
+ ssa->ops[use].res_use_chain = ssa->vars[cv_var].use_chain;
+ ssa->vars[cv_var].use_chain = use;
+
+ op_array->opcodes[use].result_type = IS_CV;
+ op_array->opcodes[use].result.var = cv;
+ }
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
+{
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
+ }
+
+ if (ssa->var_info) {
+ int op_1;
+ int v;
+ int remove_nops = 0;
+ zend_op *opline;
+ zend_ssa_op *ssa_op;
+ zval tmp;
+
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "before dfa");
+#endif
+
+ if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
+ if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
+ remove_nops = 1;
+ }
+
+ if (zend_dfa_optimize_jmps(op_array, ssa)) {
+ remove_nops = 1;
+ }
+
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "after sccp");
+#endif
+ if (ZEND_FUNC_INFO(op_array)) {
+ if (zend_dfa_optimize_calls(op_array, ssa)) {
+ remove_nops = 1;
+ }
+ }
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_8) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
+ }
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "after calls");
+#endif
+ }
+
+ if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) {
+ if (dce_optimize_op_array(op_array, ssa, 0)) {
+ remove_nops = 1;
+ }
+ if (zend_dfa_optimize_jmps(op_array, ssa)) {
+ remove_nops = 1;
+ }
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa);
+ }
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "after dce");
+#endif
+ }
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+
+ op_1 = ssa->vars[v].definition;
+
+ if (op_1 < 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + op_1;
+ ssa_op = &ssa->ops[op_1];
+
+ /* Convert LONG constants to DOUBLE */
+ if (ssa->var_info[v].use_as_double) {
+ if (opline->opcode == ZEND_ASSIGN
+ && opline->op2_type == IS_CONST
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+
+// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->opcode == ZEND_QM_ASSIGN
+ && opline->op1_type == IS_CONST
+ ) {
+
+// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+
+ } else {
+ if (opline->opcode == ZEND_ADD
+ || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL
+ || opline->opcode == ZEND_IS_EQUAL
+ || opline->opcode == ZEND_IS_NOT_EQUAL
+ || opline->opcode == ZEND_IS_SMALLER
+ || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
+ ) {
+
+ if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST) {
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+
+ if ((OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_LONG) {
+
+// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
+
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ }
+ if (opline->opcode == ZEND_ADD) {
+ zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+
+ if (((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
+ && Z_TYPE_INFO_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) == 0)
+ || ((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_DOUBLE
+ && Z_DVAL_P(zv) == 0.0)) {
+
+// op_1: #v.? = ADD 0, #?.? [double,long] => #v.? = QM_ASSIGN #?.?
+
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.num = 0;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
+ }
+ }
+ } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST) {
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+
+ if ((OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) {
+
+// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
+
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ }
+ if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB) {
+ if (((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
+ && Z_TYPE_INFO_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) == 0)
+ || ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_DOUBLE
+ && Z_DVAL_P(zv) == 0.0)) {
+
+// op_1: #v.? = ADD #?.? [double,long], 0 => #v.? = QM_ASSIGN #?.?
+
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.num = 0;
+ }
+ }
+ }
+ } else if (opline->opcode == ZEND_CONCAT) {
+ if (!(OP1_INFO() & MAY_BE_OBJECT)
+ && !(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;
+ }
+ }
+ }
+
+ if (opline->opcode == ZEND_QM_ASSIGN
+ && ssa->ops[op_1].result_def == v
+ && opline->op1_type & (IS_TMP_VAR|IS_VAR)
+ && !(ssa->var_info[v].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ ) {
+
+ int src_var = ssa->ops[op_1].op1_use;
+
+ if (src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op1_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
+ && opline_supports_assign_contraction(
+ ssa, &op_array->opcodes[ssa->vars[src_var].definition],
+ src_var, opline->result.var)
+ ) {
+
+ int orig_var = ssa->ops[op_1].result_use;
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #v.CV = OP ...
+// op_1: QM_ASSIGN #src_var.T #orig_var.CV [undef,scalar] -> #v.CV, NOP
+
+ if (orig_var < 0 || zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
+
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
+
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
+ ssa->ops[op_1].result_use = -1;
+ ssa->ops[op_1].result_def = -1;
+ ssa->ops[op_1].res_use_chain = -1;
+
+ /* Update opcodes */
+ op_array->opcodes[op_2].result_type = opline->result_type;
+ op_array->opcodes[op_2].result.var = opline->result.var;
+
+ MAKE_NOP(opline);
+ remove_nops = 1;
+
+ if (op_array->opcodes[op_2].opcode == ZEND_SUB
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op1_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
+ && ssa->ops[op_2].op2_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
+ op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+ ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
+ ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
+ ssa->ops[op_2].op2_use = -1;
+ ssa->ops[op_2].op2_use_chain = -1;
+ }
+ }
+ }
+ }
+
+ if (ssa->vars[v].var >= op_array->last_var) {
+ /* skip TMP and VAR */
+ continue;
+ }
+
+ if (ssa->ops[op_1].op1_def == v
+ && RETURN_VALUE_USED(opline)) {
+ if (opline->opcode == ZEND_ASSIGN
+ || opline->opcode == ZEND_ASSIGN_OP
+ || opline->opcode == ZEND_PRE_INC
+ || opline->opcode == ZEND_PRE_DEC) {
+ zend_dfa_try_to_replace_result(op_array, ssa, op_1, v);
+ } else if (opline->opcode == ZEND_POST_INC) {
+ int result_var = ssa->ops[op_1].result_def;
+
+ if (result_var >= 0
+ && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
+ int use = ssa->vars[result_var].use_chain;
+
+ if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
+ && ssa->ops[use].op1_use == result_var
+ && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
+ opline->opcode = ZEND_PRE_INC;
+ op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
+ }
+ }
+ } else if (opline->opcode == ZEND_POST_DEC) {
+ int result_var = ssa->ops[op_1].result_def;
+
+ if (result_var >= 0
+ && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
+ int use = ssa->vars[result_var].use_chain;
+
+ if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
+ && ssa->ops[use].op2_use == result_var
+ && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
+ opline->opcode = ZEND_PRE_DEC;
+ op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
+ }
+ }
+ }
+ }
+
+ if (opline->opcode == ZEND_ASSIGN
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+ int orig_var = ssa->ops[op_1].op1_use;
+
+ if (orig_var >= 0
+ && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ ) {
+ int src_var = ssa->ops[op_1].op2_use;
+
+ if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
+ && src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op2_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
+ && opline_supports_assign_contraction(
+ ssa, &op_array->opcodes[ssa->vars[src_var].definition],
+ src_var, opline->op1.var)
+ ) {
+
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #v.CV = OP ...
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
+
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
+
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
+
+ /* Update opcodes */
+ op_array->opcodes[op_2].result_type = opline->op1_type;
+ op_array->opcodes[op_2].result.var = opline->op1.var;
+
+ MAKE_NOP(opline);
+ remove_nops = 1;
+
+ if (op_array->opcodes[op_2].opcode == ZEND_SUB
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op1_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
+ && ssa->ops[op_2].op2_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
+ op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+ ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
+ ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
+ ssa->ops[op_2].op2_use = -1;
+ ssa->ops[op_2].op2_use_chain = -1;
+ }
+ }
+ } else if (opline->op2_type == IS_CONST
+ || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
+ && ssa->ops[op_1].op2_use >= 0
+ && ssa->ops[op_1].op2_def < 0)
+ ) {
+
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
+
+ if (ssa->ops[op_1].op1_use != ssa->ops[op_1].op2_use) {
+ zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
+ } else {
+ ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain;
+ }
+
+ /* Reconstruct SSA */
+ ssa->ops[op_1].result_def = v;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
+
+ /* Update opcode */
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ opline->opcode = ZEND_QM_ASSIGN;
+ }
+ }
+
+ } 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
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// 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_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
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// 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 (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_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 = opline->extended_value;
+ opline->extended_value = 0;
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+
+ }
+ }
+
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "after dfa");
+#endif
+
+ if (remove_nops) {
+ zend_ssa_remove_nops(op_array, ssa, ctx);
+#if ZEND_DEBUG_DFA
+ ssa_verify_integrity(op_array, ssa, "after nop");
+#endif
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
+ }
+}
+
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+ zend_ssa ssa;
+
+ if (zend_dfa_analyze_op_array(op_array, ctx, &ssa) != SUCCESS) {
+ zend_arena_release(&ctx->arena, checkpoint);
+ return;
+ }
+
+ zend_dfa_optimize_op_array(op_array, ctx, &ssa, NULL);
+
+ /* Destroy SSA */
+ zend_arena_release(&ctx->arena, checkpoint);
+}
diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c
new file mode 100644
index 0000000000..c0d5081c1f
--- /dev/null
+++ b/Zend/Optimizer/escape_analysis.c
@@ -0,0 +1,539 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache, Escape Analysis |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+/*
+ * T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic
+ * compilation and deoptimization. In Proceedings of the International
+ * Conference on Virtual Execution Environments, pages 111-120, Chicago,
+ * June 2005
+ */
+
+static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ parent[i] = i;
+ size[i] = 1;
+ }
+}
+/* }}} */
+
+static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
+{
+ int p = parent[i];
+
+ while (i != p) {
+ p = parent[p];
+ parent[i] = p;
+ i = p;
+ p = parent[i];
+ }
+ return i;
+}
+/* }}} */
+
+static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
+{
+ int r1 = union_find_root(parent, i);
+ int r2 = union_find_root(parent, j);
+
+ if (r1 != r2) {
+ if (size[r1] < size[r2]) {
+ parent[r1] = r2;
+ size[r2] += size[r1];
+ } else {
+ parent[r2] = r1;
+ size[r1] += size[r2];
+ }
+ }
+}
+/* }}} */
+
+static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars = ssa->vars;
+ int ssa_vars_count = ssa->vars_count;
+ zend_ssa_phi *p;
+ int i, j;
+ int *size;
+ ALLOCA_FLAG(use_heap)
+
+ size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
+ if (!size) {
+ return FAILURE;
+ }
+ union_find_init(parent, size, ssa_vars_count);
+
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa_vars[i].definition_phi) {
+ p = ssa_vars[i].definition_phi;
+ if (p->pi >= 0) {
+ union_find_unite(parent, size, i, p->sources[0]);
+ } else {
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ union_find_unite(parent, size, i, p->sources[j]);
+ }
+ }
+ } else if (ssa_vars[i].definition >= 0) {
+ int def = ssa_vars[i].definition;
+ zend_ssa_op *op = ssa->ops + def;
+ zend_op *opline = op_array->opcodes + def;
+
+ if (op->op1_def >= 0) {
+ if (op->op1_use >= 0) {
+ if (opline->opcode != ZEND_ASSIGN) {
+ union_find_unite(parent, size, op->op1_def, op->op1_use);
+ }
+ }
+ if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
+ union_find_unite(parent, size, op->op1_def, op->op2_use);
+ }
+ }
+ if (op->op2_def >= 0) {
+ if (op->op2_use >= 0) {
+ union_find_unite(parent, size, op->op2_def, op->op2_use);
+ }
+ }
+ if (op->result_def >= 0) {
+ if (op->result_use >= 0) {
+ if (opline->opcode != ZEND_QM_ASSIGN) {
+ union_find_unite(parent, size, op->result_def, op->result_use);
+ }
+ }
+ if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
+ union_find_unite(parent, size, op->result_def, op->op1_use);
+ }
+ if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
+ union_find_unite(parent, size, op->result_def, op->op2_use);
+ }
+ if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
+ union_find_unite(parent, size, op->result_def, op->op1_def);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < ssa_vars_count; i++) {
+ parent[i] = union_find_root(parent, i);
+ }
+
+ free_alloca(size, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */
+{
+ zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
+ if (ce) {
+ return ce;
+ }
+
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (ce && ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
+{
+ zend_ssa_op *ssa_op = ssa->ops + def;
+ zend_op *opline = op_array->opcodes + def;
+
+ if (ssa_op->result_def == var) {
+ switch (opline->opcode) {
+ case ZEND_INIT_ARRAY:
+ return 1;
+ case ZEND_NEW:
+ /* objects with destructors should escape */
+ if (opline->op1_type == IS_CONST) {
+ zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
+ uint32_t forbidden_flags =
+ /* These flags will always cause an exception */
+ ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
+ | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
+ if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
+ !ce->destructor && !ce->__get && !ce->__set &&
+ !(ce->ce_flags & forbidden_flags) &&
+ (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
+ return 1;
+ }
+ }
+ break;
+ case ZEND_QM_ASSIGN:
+ if (opline->op1_type == IS_CONST
+ && Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
+ return 1;
+ }
+ if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
+ return 1;
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
+ return 1;
+ }
+ break;
+ }
+ } else if (ssa_op->op1_def == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if (opline->op2_type == IS_CONST
+ && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
+ return 1;
+ }
+ if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
+ return 1;
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
+ /* implicit object/array allocation */
+ return 1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
+{
+ zend_ssa_op *op = ssa->ops + def;
+ zend_op *opline = op_array->opcodes + def;
+
+ if (op->result_def == var) {
+ switch (opline->opcode) {
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_QM_ASSIGN:
+ case ZEND_ASSIGN:
+ return 1;
+ case ZEND_NEW:
+ /* objects with destructors should escape */
+ if (opline->op1_type == IS_CONST) {
+ zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
+ if (ce && !ce->create_object && !ce->constructor &&
+ !ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
+ return 1;
+ }
+ }
+ break;
+ }
+ } else if (op->op1_def == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ 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:
+ case ZEND_POST_DEC_OBJ:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */
+{
+ zend_ssa_op *ssa_op = ssa->ops + use;
+ zend_op *opline = op_array->opcodes + use;
+
+ if (ssa_op->op1_use == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ /* no_val */
+ break;
+ case ZEND_QM_ASSIGN:
+ if (opline->op1_type == IS_CV) {
+ if (OP1_INFO() & MAY_BE_OBJECT) {
+ /* object aliasing */
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_OBJ_R:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_OBJ_IS:
+ break;
+ 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:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ break;
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ return 1;
+ }
+ if (OP1_INFO() & MAY_BE_OBJECT) {
+ /* object aliasing */
+ return 1;
+ }
+ /* reference dependencies processed separately */
+ break;
+ case ZEND_OP_DATA:
+ if ((opline-1)->opcode != ZEND_ASSIGN_DIM
+ && (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
+ return 1;
+ }
+ if (OP1_INFO() & MAY_BE_OBJECT) {
+ /* object aliasing */
+ return 1;
+ }
+ opline--;
+ ssa_op--;
+ if (opline->op1_type != IS_CV
+ || (OP1_INFO() & MAY_BE_REF)
+ || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
+ /* assignment into escaping structure */
+ return 1;
+ }
+ /* reference dependencies processed separately */
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ if (ssa_op->op2_use == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if (opline->op1_type != IS_CV
+ || (OP1_INFO() & MAY_BE_REF)
+ || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
+ /* assignment into escaping variable */
+ return 1;
+ }
+ if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
+ if (OP2_INFO() & MAY_BE_OBJECT) {
+ /* object aliasing */
+ return 1;
+ }
+ }
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ if (ssa_op->result_use == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ case ZEND_QM_ASSIGN:
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars = ssa->vars;
+ int ssa_vars_count = ssa->vars_count;
+ int i, root, use;
+ int *ees;
+ bool has_allocations;
+ int num_non_escaped;
+ ALLOCA_FLAG(use_heap)
+
+ if (!ssa_vars) {
+ return SUCCESS;
+ }
+
+ has_allocations = 0;
+ for (i = op_array->last_var; i < ssa_vars_count; i++) {
+ if (ssa_vars[i].definition >= 0
+ && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
+ && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
+ has_allocations = 1;
+ break;
+ }
+ }
+ if (!has_allocations) {
+ return SUCCESS;
+ }
+
+
+ /* 1. Build EES (Equi-Escape Sets) */
+ ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
+ if (!ees) {
+ return FAILURE;
+ }
+
+ if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ /* 2. Identify Allocations */
+ num_non_escaped = 0;
+ for (i = op_array->last_var; i < ssa_vars_count; i++) {
+ root = ees[i];
+ if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
+ /* already escape. skip */
+ } else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
+ if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ num_non_escaped--;
+ }
+ ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
+ } else if (ssa_vars[i].definition >= 0
+ && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+ if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
+ if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ num_non_escaped--;
+ }
+ ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
+ } else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
+ && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
+ ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
+ num_non_escaped++;
+ }
+ }
+ }
+
+ /* 3. Mark escaped EES */
+ if (num_non_escaped) {
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa_vars[i].use_chain >= 0) {
+ root = ees[i];
+ if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ FOREACH_USE(ssa_vars + i, use) {
+ if (is_escape_use(op_array, ssa, use, i)) {
+ ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
+ num_non_escaped--;
+ if (num_non_escaped == 0) {
+ i = ssa_vars_count;
+ }
+ break;
+ }
+ } FOREACH_USE_END();
+ }
+ }
+ }
+ }
+
+ /* 4. Process referential dependencies */
+ if (num_non_escaped) {
+ bool changed;
+
+ do {
+ changed = 0;
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa_vars[i].use_chain >= 0) {
+ root = ees[i];
+ if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ FOREACH_USE(ssa_vars + i, use) {
+ zend_ssa_op *op = ssa->ops + use;
+ zend_op *opline = op_array->opcodes + use;
+ int enclosing_root;
+
+ if (opline->opcode == ZEND_OP_DATA &&
+ ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+ (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];
+ } else if ((opline->opcode == ZEND_INIT_ARRAY ||
+ opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
+ op->op1_use == i &&
+ op->result_def >= 0) {
+ enclosing_root = ees[op->result_def];
+ } else {
+ continue;
+ }
+
+ if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
+ ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
+ if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
+ ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
+ } else {
+ ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
+ }
+ if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
+ num_non_escaped--;
+ if (num_non_escaped == 0) {
+ changed = 0;
+ } else {
+ changed = 1;
+ }
+ break;
+ } else {
+ changed = 1;
+ }
+ }
+ } FOREACH_USE_END();
+ }
+ }
+ }
+ } while (changed);
+ }
+
+ /* 5. Propagate values of escape sets to variables */
+ for (i = 0; i < ssa_vars_count; i++) {
+ root = ees[i];
+ if (i != root) {
+ ssa_vars[i].escape_state = ssa_vars[root].escape_state;
+ }
+ }
+
+ free_alloca(ees, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
diff --git a/Zend/Optimizer/nop_removal.c b/Zend/Optimizer/nop_removal.c
new file mode 100644
index 0000000000..32d2f10bf4
--- /dev/null
+++ b/Zend/Optimizer/nop_removal.c
@@ -0,0 +1,106 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* pass 10:
+ * - remove NOPs
+ */
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+
+void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_op *end, *opline;
+ uint32_t new_count, i, shift;
+ int j;
+ uint32_t *shiftlist;
+ ALLOCA_FLAG(use_heap);
+
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
+ i = new_count = shift = 0;
+ end = op_array->opcodes + op_array->last;
+ for (opline = op_array->opcodes; opline < end; opline++) {
+
+ /* Kill JMP-over-NOP-s */
+ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
+ /* check if there are only NOPs under the branch */
+ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
+
+ while (target->opcode == ZEND_NOP) {
+ target--;
+ }
+ if (target == opline) {
+ /* only NOPs */
+ opline->opcode = ZEND_NOP;
+ }
+ }
+
+ shiftlist[i++] = shift;
+ if (opline->opcode == ZEND_NOP) {
+ shift++;
+ } else {
+ if (shift) {
+ zend_op *new_opline = op_array->opcodes + new_count;
+
+ *new_opline = *opline;
+ zend_optimizer_migrate_jump(op_array, new_opline, opline);
+ }
+ new_count++;
+ }
+ }
+
+ if (shift) {
+ op_array->last = new_count;
+ end = op_array->opcodes + op_array->last;
+
+ /* update JMPs */
+ for (opline = op_array->opcodes; opline<end; opline++) {
+ zend_optimizer_shift_jump(op_array, opline, shiftlist);
+ }
+
+ /* 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];
+ op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+ if (op_array->try_catch_array[j].finally_op) {
+ op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+ op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+ }
+ }
+
+ /* update early binding list */
+ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
+ uint32_t *opline_num = &ctx->script->first_early_binding_opline;
+
+ ZEND_ASSERT(op_array == &ctx->script->main_op_array);
+ do {
+ *opline_num -= shiftlist[*opline_num];
+ opline_num = &op_array->opcodes[*opline_num].result.opline_num;
+ } while (*opline_num != (uint32_t)-1);
+ }
+ }
+ free_alloca(shiftlist, use_heap);
+}
diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c
new file mode 100644
index 0000000000..319b17438d
--- /dev/null
+++ b/Zend/Optimizer/optimize_func_calls.c
@@ -0,0 +1,337 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ | Xinchen Hui <laruence@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* pass 4
+ * - optimize INIT_FCALL_BY_NAME to DO_FCALL
+ */
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+
+#define ZEND_OP1_IS_CONST_STRING(opline) \
+ (opline->op1_type == IS_CONST && \
+ Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
+#define ZEND_OP2_IS_CONST_STRING(opline) \
+ (opline->op2_type == IS_CONST && \
+ Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
+
+typedef struct _optimizer_call_info {
+ zend_function *func;
+ zend_op *opline;
+ bool is_prototype;
+ bool try_inline;
+ uint32_t func_arg_num;
+} optimizer_call_info;
+
+static void zend_delete_call_instructions(zend_op *opline)
+{
+ int call = 0;
+
+ while (1) {
+ switch (opline->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:
+ if (call == 0) {
+ MAKE_NOP(opline);
+ return;
+ }
+ /* break missing intentionally */
+ case ZEND_NEW:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_INIT_USER_CALL:
+ call--;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ call++;
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ if (call == 0) {
+ if (opline->op1_type == IS_CONST) {
+ MAKE_NOP(opline);
+ } else if (opline->op1_type == IS_CV) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->extended_value = 0;
+ opline->result.var = 0;
+ } else {
+ opline->opcode = ZEND_FREE;
+ opline->extended_value = 0;
+ opline->result.var = 0;
+ }
+ }
+ break;
+ }
+ opline--;
+ }
+}
+
+static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
+{
+ 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) {
+
+ zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
+
+ if (ret_opline->op1_type == IS_CONST) {
+ 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;
+ }
+
+ for (i = 0; i < num_args; i++) {
+ /* Don't inline functions with by-reference arguments. This would require
+ * correct handling of INDIRECT arguments. */
+ if (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) {
+ return;
+ }
+ }
+
+ if (fcall->extended_value < func->op_array.num_args) {
+ /* don't inline functions with named constants in default arguments */
+ i = fcall->extended_value;
+
+ do {
+ if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) {
+ return;
+ }
+ i++;
+ } while (i < func->op_array.num_args);
+ }
+
+ if (RETURN_VALUE_USED(opline)) {
+ zval zv;
+
+ ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, ret_opline->op1));
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op1_type = IS_CONST;
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ SET_UNUSED(opline->op2);
+ } else {
+ MAKE_NOP(opline);
+ }
+
+ zend_delete_call_instructions(opline-1);
+ }
+ }
+}
+
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+ int call = 0;
+ void *checkpoint;
+ optimizer_call_info *call_stack;
+
+ if (op_array->last < 2) {
+ return;
+ }
+
+ checkpoint = zend_arena_checkpoint(ctx->arena);
+ call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
+ while (opline < end) {
+ switch (opline->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:
+ /* The argument passing optimizations are valid for prototypes as well,
+ * as inheritance cannot change between ref <-> non-ref arguments. */
+ call_stack[call].func = zend_optimizer_get_called_func(
+ ctx->script, op_array, opline, &call_stack[call].is_prototype);
+ call_stack[call].try_inline =
+ !call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
+ /* break missing intentionally */
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_INIT_USER_CALL:
+ call_stack[call].opline = opline;
+ call_stack[call].func_arg_num = (uint32_t)-1;
+ call++;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ call--;
+ if (call_stack[call].func && call_stack[call].opline) {
+ zend_op *fcall = call_stack[call].opline;
+
+ if (fcall->opcode == ZEND_INIT_FCALL) {
+ /* nothing to do */
+ } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
+ fcall->opcode = ZEND_INIT_FCALL;
+ fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
+ literal_dtor(&ZEND_OP2_LITERAL(fcall));
+ fcall->op2.constant = fcall->op2.constant + 1;
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
+ } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+ fcall->opcode = ZEND_INIT_FCALL;
+ fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
+ literal_dtor(&op_array->literals[fcall->op2.constant]);
+ literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
+ fcall->op2.constant = fcall->op2.constant + 1;
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
+ } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
+ || fcall->opcode == ZEND_INIT_METHOD_CALL
+ || fcall->opcode == ZEND_NEW) {
+ /* We don't have specialized opcodes for this, do nothing */
+ } else {
+ ZEND_UNREACHABLE();
+ }
+
+ if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
+ && call_stack[call].try_inline) {
+ zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
+ }
+ }
+ call_stack[call].func = NULL;
+ call_stack[call].opline = NULL;
+ call_stack[call].try_inline = 0;
+ call_stack[call].func_arg_num = (uint32_t)-1;
+ break;
+ case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ if (call_stack[call - 1].func
+ && call_stack[call - 1].func_arg_num != (uint32_t)-1) {
+ if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 9;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_W;
+ }
+ } else {
+ if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
+ && opline->op2_type == IS_UNUSED) {
+ /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
+ * Performing the replacement would create an invalid opcode. */
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 12;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_R;
+ }
+ }
+ }
+ break;
+ case ZEND_SEND_VAL_EX:
+ if (call_stack[call - 1].func) {
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
+ if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ /* We won't convert it into_DO_FCALL to emit error at run-time */
+ call_stack[call - 1].opline = NULL;
+ } else {
+ opline->opcode = ZEND_SEND_VAL;
+ }
+ }
+ break;
+ case ZEND_CHECK_FUNC_ARG:
+ if (call_stack[call - 1].func) {
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].func_arg_num = (uint32_t)-1;
+ break;
+ }
+
+ call_stack[call - 1].func_arg_num = opline->op2.num;
+ MAKE_NOP(opline);
+ }
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ if (call_stack[call - 1].func) {
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
+ call_stack[call - 1].func_arg_num = (uint32_t)-1;
+ if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_REF;
+ } else {
+ opline->opcode = ZEND_SEND_VAR;
+ }
+ }
+ break;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ if (call_stack[call - 1].func) {
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
+ if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAL;
+ } else {
+ opline->opcode = ZEND_SEND_VAR;
+ }
+ }
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_REF:
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+ break;
+ case ZEND_SEND_UNPACK:
+ case ZEND_SEND_USER:
+ case ZEND_SEND_ARRAY:
+ call_stack[call - 1].try_inline = 0;
+ break;
+ default:
+ break;
+ }
+ opline++;
+ }
+
+ zend_arena_release(&ctx->arena, checkpoint);
+}
diff --git a/Zend/Optimizer/optimize_temp_vars_5.c b/Zend/Optimizer/optimize_temp_vars_5.c
new file mode 100644
index 0000000000..6f7400159d
--- /dev/null
+++ b/Zend/Optimizer/optimize_temp_vars_5.c
@@ -0,0 +1,187 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_bitset.h"
+
+#define GET_AVAILABLE_T() \
+ for (i = 0; i < T; i++) { \
+ if (!zend_bitset_in(taken_T, i)) { \
+ break; \
+ } \
+ } \
+ zend_bitset_incl(taken_T, i); \
+ if (i > max) { \
+ max = i; \
+ }
+
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ int T = op_array->T;
+ int offset = op_array->last_var;
+ uint32_t bitset_len;
+ zend_bitset taken_T; /* T index in use */
+ zend_op **start_of_T; /* opline where T is first used */
+ zend_bitset valid_T; /* Is the map_T valid */
+ int *map_T; /* Map's the T to its new index */
+ zend_op *opline, *end;
+ int currT;
+ int i;
+ int max = -1;
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+
+ bitset_len = zend_bitset_len(T);
+ taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
+ start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
+ valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
+ map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
+
+ end = op_array->opcodes;
+ opline = &op_array->opcodes[op_array->last - 1];
+
+ /* Find T definition points */
+ while (opline >= end) {
+ if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
+ start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
+ }
+ opline--;
+ }
+
+ zend_bitset_clear(valid_T, bitset_len);
+ zend_bitset_clear(taken_T, bitset_len);
+
+ end = op_array->opcodes;
+ opline = &op_array->opcodes[op_array->last - 1];
+
+ while (opline >= end) {
+ if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
+ currT = VAR_NUM(opline->op1.var) - offset;
+ if (opline->opcode == ZEND_ROPE_END) {
+ int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
+ int var;
+
+ var = max;
+ while (var >= 0 && !zend_bitset_in(taken_T, var)) {
+ var--;
+ }
+ max = MAX(max, var + num);
+ var = var + 1;
+ map_T[currT] = var;
+ zend_bitset_incl(valid_T, currT);
+ zend_bitset_incl(taken_T, var);
+ opline->op1.var = NUM_VAR(var + offset);
+ while (num > 1) {
+ num--;
+ zend_bitset_incl(taken_T, var + num);
+ }
+ } else {
+ if (!zend_bitset_in(valid_T, currT)) {
+ int use_new_var = 0;
+
+ /* Code in "finally" blocks may modify temporary variables.
+ * We allocate new temporaries for values that need to
+ * relive FAST_CALLs.
+ */
+ if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
+ (opline->opcode == ZEND_RETURN ||
+ opline->opcode == ZEND_GENERATOR_RETURN ||
+ opline->opcode == ZEND_RETURN_BY_REF ||
+ opline->opcode == ZEND_FREE ||
+ opline->opcode == ZEND_FE_FREE)) {
+ zend_op *curr = opline;
+
+ while (--curr >= end) {
+ if (curr->opcode == ZEND_FAST_CALL) {
+ use_new_var = 1;
+ break;
+ } else if (curr->opcode != ZEND_FREE &&
+ curr->opcode != ZEND_FE_FREE &&
+ curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
+ curr->opcode != ZEND_DISCARD_EXCEPTION) {
+ break;
+ }
+ }
+ }
+ if (use_new_var) {
+ i = ++max;
+ zend_bitset_incl(taken_T, i);
+ } else {
+ GET_AVAILABLE_T();
+ }
+ map_T[currT] = i;
+ zend_bitset_incl(valid_T, currT);
+ }
+ opline->op1.var = NUM_VAR(map_T[currT] + offset);
+ }
+ }
+
+ if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
+ currT = VAR_NUM(opline->op2.var) - offset;
+ if (!zend_bitset_in(valid_T, currT)) {
+ GET_AVAILABLE_T();
+ map_T[currT] = i;
+ zend_bitset_incl(valid_T, currT);
+ }
+ opline->op2.var = NUM_VAR(map_T[currT] + offset);
+ }
+
+ if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
+ currT = VAR_NUM(opline->result.var) - offset;
+ if (zend_bitset_in(valid_T, currT)) {
+ if (start_of_T[currT] == opline) {
+ /* ZEND_FAST_CALL can not share temporary var with others
+ * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
+ * which could be ahead of it */
+ if (opline->opcode != ZEND_FAST_CALL) {
+ zend_bitset_excl(taken_T, map_T[currT]);
+ }
+ }
+ opline->result.var = NUM_VAR(map_T[currT] + offset);
+ if (opline->opcode == ZEND_ROPE_INIT) {
+ if (start_of_T[currT] == opline) {
+ uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
+ while (num > 1) {
+ num--;
+ zend_bitset_excl(taken_T, map_T[currT]+num);
+ }
+ }
+ }
+ } else {
+ /* Code which gets here is using a wrongly built opcode such as RECV() */
+ GET_AVAILABLE_T();
+ map_T[currT] = i;
+ zend_bitset_incl(valid_T, currT);
+ opline->result.var = NUM_VAR(i + offset);
+ }
+ }
+
+ opline--;
+ }
+
+ zend_arena_release(&ctx->arena, checkpoint);
+ op_array->T = max + 1;
+}
diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c
new file mode 100644
index 0000000000..86774afef4
--- /dev/null
+++ b/Zend/Optimizer/pass1.c
@@ -0,0 +1,685 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* pass 1 (Simple local optimizations)
+ * - persistent constant substitution (true, false, null, etc)
+ * - constant casting (ADD expects numbers, CONCAT strings, etc)
+ * - constant expression evaluation
+ * - optimize constant conditional JMPs
+ * - pre-evaluate constant function calls
+ * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
+ */
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+
+void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+ bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
+ (op_array == &ctx->script->main_op_array) : 0;
+
+ while (opline < end) {
+ switch (opline->opcode) {
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ if (opline->op1_type == IS_CONST) {
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+ convert_to_string(&ZEND_OP1_LITERAL(opline));
+ }
+ }
+ if (opline->op2_type == IS_CONST) {
+ if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
+ convert_to_string(&ZEND_OP2_LITERAL(opline));
+ }
+ if (opline->op1_type == IS_CONST) {
+ goto constant_binary_op;
+ }
+ }
+ break;
+
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_POW:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_BOOL_XOR:
+ case ZEND_SPACESHIP:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ /* binary operation with constant operands */
+ zval result;
+
+constant_binary_op:
+ if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ }
+ }
+ 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_CAST:
+ if (opline->op1_type == IS_CONST) {
+ /* cast of constant operand */
+ zval result;
+
+ if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ break;
+ }
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ case ZEND_BOOL_NOT:
+ if (opline->op1_type == IS_CONST) {
+ /* unary operation on constant operand */
+ zval result;
+
+ if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ }
+ }
+ break;
+
+ case ZEND_FETCH_CONSTANT:
+ if (opline->op2_type == IS_CONST &&
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
+ Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 &&
+ memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) {
+ /* substitute __COMPILER_HALT_OFFSET__ constant */
+ zend_execute_data *orig_execute_data = EG(current_execute_data);
+ zend_execute_data fake_execute_data;
+ zval *offset;
+
+ memset(&fake_execute_data, 0, sizeof(zend_execute_data));
+ fake_execute_data.func = (zend_function*)op_array;
+ EG(current_execute_data) = &fake_execute_data;
+ if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
+
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, offset);
+ }
+ }
+ EG(current_execute_data) = orig_execute_data;
+ break;
+ }
+
+ if (opline->op2_type == IS_CONST &&
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
+ /* substitute persistent constants */
+ zval c;
+
+ if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) {
+ if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) {
+ break;
+ }
+ }
+ if (Z_TYPE(c) == IS_CONSTANT_AST) {
+ break;
+ }
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &c);
+ }
+ }
+ break;
+
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (opline->op2_type == IS_CONST &&
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
+
+ zend_class_entry *ce = NULL;
+
+ if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
+ /* for A::B */
+ if (op_array->scope &&
+ !strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
+ ZSTR_VAL(op_array->scope->name), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1)) {
+ ce = op_array->scope;
+ } else {
+ if ((ce = zend_hash_find_ptr(EG(class_table),
+ Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL ||
+ (ce->type == ZEND_INTERNAL_CLASS &&
+ ce->info.internal.module->type != MODULE_PERSISTENT) ||
+ (ce->type == ZEND_USER_CLASS &&
+ ce->info.user.filename != op_array->filename)) {
+ break;
+ }
+ }
+ } else if (op_array->scope &&
+ opline->op1_type == IS_UNUSED &&
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ /* for self::B */
+ ce = op_array->scope;
+ } else if (op_array->scope &&
+ opline->op1_type == IS_VAR &&
+ (opline - 1)->opcode == ZEND_FETCH_CLASS &&
+ ((opline - 1)->op2_type == IS_UNUSED &&
+ ((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
+ (opline - 1)->result.var == opline->op1.var) {
+ /* for self::B */
+ ce = op_array->scope;
+ }
+
+ if (ce) {
+ zend_class_constant *cc;
+ zval *c, t;
+
+ if ((cc = zend_hash_find_ptr(&ce->constants_table,
+ Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
+ (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
+ c = &cc->value;
+ if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
+ zend_ast *ast = Z_ASTVAL_P(c);
+ if (ast->kind != ZEND_AST_CONSTANT
+ || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1)
+ || Z_TYPE(t) == IS_CONSTANT_AST) {
+ break;
+ }
+ } else {
+ ZVAL_COPY_OR_DUP(&t, c);
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else if (opline->op1_type == IS_VAR) {
+ MAKE_NOP((opline - 1));
+ }
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+
+ if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ }
+ }
+ }
+ break;
+
+ case ZEND_DO_ICALL: {
+ zend_op *send1_opline = opline - 1;
+ zend_op *send2_opline = NULL;
+ zend_op *init_opline = NULL;
+
+ while (send1_opline->opcode == ZEND_NOP) {
+ send1_opline--;
+ }
+ if (send1_opline->opcode != ZEND_SEND_VAL ||
+ send1_opline->op1_type != IS_CONST) {
+ /* don't colllect constants after unknown function call */
+ collect_constants = 0;
+ break;
+ }
+ if (send1_opline->op2.num == 2) {
+ send2_opline = send1_opline;
+ send1_opline--;
+ while (send1_opline->opcode == ZEND_NOP) {
+ send1_opline--;
+ }
+ if (send1_opline->opcode != ZEND_SEND_VAL ||
+ send1_opline->op1_type != IS_CONST) {
+ /* don't colllect constants after unknown function call */
+ collect_constants = 0;
+ break;
+ }
+ }
+ init_opline = send1_opline - 1;
+ while (init_opline->opcode == ZEND_NOP) {
+ init_opline--;
+ }
+ if (init_opline->opcode != ZEND_INIT_FCALL ||
+ init_opline->op2_type != IS_CONST ||
+ Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
+ /* don't colllect constants after unknown function call */
+ collect_constants = 0;
+ break;
+ }
+
+ /* define("name", scalar); */
+ if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 &&
+ zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) {
+
+ if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING &&
+ send2_opline &&
+ Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) {
+
+ if (collect_constants) {
+ zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
+ }
+
+ if (RESULT_UNUSED(opline) &&
+ !zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
+
+ opline->opcode = ZEND_DECLARE_CONST;
+ opline->op1_type = IS_CONST;
+ opline->op2_type = IS_CONST;
+ opline->result_type = IS_UNUSED;
+ opline->op1.constant = send1_opline->op1.constant;
+ opline->op2.constant = send2_opline->op1.constant;
+ opline->result.num = 0;
+
+ literal_dtor(&ZEND_OP2_LITERAL(init_opline));
+ MAKE_NOP(init_opline);
+ MAKE_NOP(send1_opline);
+ MAKE_NOP(send2_opline);
+ }
+ break;
+ }
+ }
+
+ /* pre-evaluate constant functions:
+ constant(x)
+ function_exists(x)
+ is_callable(x)
+ extension_loaded(x)
+ */
+ if (!send2_opline &&
+ Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) {
+ if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 &&
+ !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
+ "function_exists", sizeof("function_exists")-1)) ||
+ (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 &&
+ !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
+ "is_callable", sizeof("is_callable")))) {
+ zend_internal_function *func;
+ zend_string *lc_name = zend_string_tolower(
+ Z_STR(ZEND_OP1_LITERAL(send1_opline)));
+
+ if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL
+ && func->type == ZEND_INTERNAL_FUNCTION
+ && func->module->type == MODULE_PERSISTENT
+#ifdef ZEND_WIN32
+ && func->module->handle == NULL
+#endif
+ ) {
+ zval t;
+ ZVAL_TRUE(&t);
+ literal_dtor(&ZEND_OP2_LITERAL(init_opline));
+ MAKE_NOP(init_opline);
+ literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
+ MAKE_NOP(send1_opline);
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ }
+ zend_string_release_ex(lc_name, 0);
+ break;
+ } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 &&
+ !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
+ "extension_loaded", sizeof("extension_loaded")-1)) {
+ zval t;
+ zend_string *lc_name = zend_string_tolower(
+ Z_STR(ZEND_OP1_LITERAL(send1_opline)));
+ zend_module_entry *m = zend_hash_find_ptr(&module_registry,
+ lc_name);
+
+ zend_string_release_ex(lc_name, 0);
+ if (!m) {
+ if (PG(enable_dl)) {
+ break;
+ } else {
+ ZVAL_FALSE(&t);
+ }
+ } else {
+ if (m->type == MODULE_PERSISTENT
+#ifdef ZEND_WIN32
+ && m->handle == NULL
+#endif
+ ) {
+ ZVAL_TRUE(&t);
+ } else {
+ break;
+ }
+ }
+
+ literal_dtor(&ZEND_OP2_LITERAL(init_opline));
+ MAKE_NOP(init_opline);
+ literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
+ MAKE_NOP(send1_opline);
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ break;
+ } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 &&
+ !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
+ "constant", sizeof("constant")-1)) {
+ zval t;
+
+ if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
+ literal_dtor(&ZEND_OP2_LITERAL(init_opline));
+ MAKE_NOP(init_opline);
+ literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
+ MAKE_NOP(send1_opline);
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ }
+ break;
+ /* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */
+ } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 &&
+ !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
+ "dirname", sizeof("dirname") - 1) &&
+ IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
+ zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0);
+ ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
+ if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) {
+ zval t;
+
+ ZVAL_STR(&t, dirname);
+ literal_dtor(&ZEND_OP2_LITERAL(init_opline));
+ MAKE_NOP(init_opline);
+ literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
+ MAKE_NOP(send1_opline);
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ } else {
+ zend_string_release_ex(dirname, 0);
+ }
+ break;
+ }
+ }
+ /* don't colllect constants after any other function call */
+ collect_constants = 0;
+ break;
+ }
+ case ZEND_STRLEN:
+ if (opline->op1_type == IS_CONST) {
+ zval t;
+
+ if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &t);
+ }
+ }
+ }
+ break;
+ case ZEND_DEFINED:
+ {
+ zval c;
+ if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) {
+ break;
+ }
+ ZVAL_TRUE(&c);
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
+ MAKE_NOP(opline);
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &c);
+ }
+ }
+ break;
+ case ZEND_DECLARE_CONST:
+ if (collect_constants &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) {
+ 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_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
+ in case we know it wouldn't jump */
+ if (opline->op1_type == IS_CONST) {
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
+ if (opline->opcode == ZEND_JMPZ_EX) {
+ opline->opcode = ZEND_QM_ASSIGN;
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ ZVAL_TRUE(&ZEND_OP1_LITERAL(opline));
+ opline->op2.num = 0;
+ break;
+ }
+ } else {
+ if (opline->opcode == ZEND_JMPNZ_EX) {
+ opline->opcode = ZEND_QM_ASSIGN;
+ zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
+ ZVAL_FALSE(&ZEND_OP1_LITERAL(opline));
+ opline->op2.num = 0;
+ break;
+ }
+ }
+ }
+ collect_constants = 0;
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ if (opline->op1_type == IS_CONST) {
+ int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
+
+ if (opline->opcode == ZEND_JMPZ) {
+ should_jmp = !should_jmp;
+ }
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->op1_type = IS_UNUSED;
+ if (should_jmp) {
+ opline->opcode = ZEND_JMP;
+ COPY_NODE(opline->op1, opline->op2);
+ opline->op2.num = 0;
+ } else {
+ MAKE_NOP(opline);
+ break;
+ }
+ }
+ collect_constants = 0;
+ break;
+
+ case ZEND_JMPZNZ:
+ if (opline->op1_type == IS_CONST) {
+ zend_op *target_opline;
+
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
+ target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
+ } else {
+ target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
+ }
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
+ opline->op1_type = IS_UNUSED;
+ opline->opcode = ZEND_JMP;
+ }
+ collect_constants = 0;
+ break;
+
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ case ZEND_MATCH_ERROR:
+ case ZEND_CATCH:
+ case ZEND_FAST_CALL:
+ case ZEND_FAST_RET:
+ case ZEND_JMP:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ collect_constants = 0;
+ break;
+ }
+ opline++;
+ }
+}
diff --git a/Zend/Optimizer/pass3.c b/Zend/Optimizer/pass3.c
new file mode 100644
index 0000000000..f98c41848c
--- /dev/null
+++ b/Zend/Optimizer/pass3.c
@@ -0,0 +1,356 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* pass 3: (Jump optimization)
+ * - optimize series of JMPs
+ */
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+
+/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
+static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count)
+{
+ int i;
+
+ for (i = 0; i < jmp_hitlist_count; i++) {
+ if (jmp_hitlist[i] == target) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define CHECK_LOOP(target) \
+ if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
+ jmp_hitlist[jmp_hitlist_count++] = target; \
+ } else { \
+ break; \
+ }
+
+void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_op *opline;
+ zend_op *end;
+ zend_op *target;
+ zend_op **jmp_hitlist;
+ int jmp_hitlist_count;
+ ALLOCA_FLAG(use_heap);
+
+ jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+
+ while (opline < end) {
+
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ jmp_hitlist_count = 0;
+
+ target = ZEND_OP1_JMP_ADDR(opline);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
+ }
+
+ if (target == opline + 1) {
+ /* convert L: JMP L+1 to NOP */
+ MAKE_NOP(opline);
+ } else if (target->opcode == ZEND_JMPZNZ) {
+ /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
+ *opline = *target;
+ if (opline->op1_type == IS_CONST) {
+ zval zv;
+ ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ }
+ goto optimize_jmpznz;
+ } else if ((target->opcode == ZEND_RETURN ||
+ target->opcode == ZEND_RETURN_BY_REF ||
+ target->opcode == ZEND_GENERATOR_RETURN ||
+ target->opcode == ZEND_EXIT) &&
+ !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
+ /* JMP L, L: RETURN to immediate RETURN */
+ *opline = *target;
+ if (opline->op1_type == IS_CONST) {
+ zval zv;
+ ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
+ }
+ } else if (opline > op_array->opcodes &&
+ ((opline-1)->opcode == ZEND_JMPZ ||
+ (opline-1)->opcode == ZEND_JMPNZ)) {
+ if (ZEND_OP2_JMP_ADDR(opline-1) == target) {
+ /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
+ if ((opline-1)->op1_type == IS_CV) {
+ (opline-1)->opcode = ZEND_CHECK_VAR;
+ (opline-1)->op2.num = 0;
+ } else if ((opline-1)->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ (opline-1)->opcode = ZEND_FREE;
+ (opline-1)->op2.num = 0;
+ } else {
+ MAKE_NOP(opline-1);
+ }
+ } else {
+ /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
+ if ((opline-1)->opcode == ZEND_JMPZ) {
+ (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), target);
+ } else {
+ (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), ZEND_OP2_JMP_ADDR(opline-1));
+ ZEND_SET_OP_JMP_ADDR((opline-1), (opline-1)->op2, target);
+ }
+ (opline-1)->opcode = ZEND_JMPZNZ;
+ }
+ }
+ break;
+
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ jmp_hitlist_count = 0;
+
+ target = ZEND_OP2_JMP_ADDR(opline);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
+ }
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ jmp_hitlist_count = 0;
+
+ target = ZEND_OP2_JMP_ADDR(opline);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ /* plain JMP */
+ /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == opline->opcode &&
+ SAME_VAR(opline->op1, target->op1)) {
+ /* same opcode and same var as this opcode */
+ /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
+ target = ZEND_OP2_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == INV_COND(opline->opcode) &&
+ SAME_VAR(opline->op1, target->op1)) {
+ /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
+ JMPZ(X,L1+1) */
+ target = target + 1;
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(opline->op1, target->op1)) {
+ target = (opline->opcode == ZEND_JMPZ) ?
+ ZEND_OP2_JMP_ADDR(target) :
+ ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
+ }
+
+ /* convert L: JMPZ L+1 to NOP */
+ if (target == opline + 1) {
+ if (opline->op1_type == IS_CV) {
+ opline->opcode = ZEND_CHECK_VAR;
+ opline->op2.num = 0;
+ } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ opline->opcode = ZEND_FREE;
+ opline->op2.num = 0;
+ } else {
+ MAKE_NOP(opline);
+ }
+ }
+ break;
+
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ jmp_hitlist_count = 0;
+
+ target = ZEND_OP2_JMP_ADDR(opline);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ /* plain JMP */
+ /* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == opline->opcode-3 &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
+ JMPZ_EX(X,L2) */
+ target = ZEND_OP2_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == opline->opcode &&
+ target->result.var == opline->result.var &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
+ JMPZ_EX(X,L2) */
+ target = ZEND_OP2_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* Check for JMPZNZ with same cond variable */
+ target = (opline->opcode == ZEND_JMPZ_EX) ?
+ ZEND_OP2_JMP_ADDR(target) :
+ ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
+ CHECK_LOOP(target);
+ } else if (target->opcode == INV_EX_COND(opline->opcode) &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
+ JMPZ_EX(X,L1+1) */
+ target = target + 1;
+ } else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
+ target->result.var == opline->result.var &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
+ JMPZ_EX(X,L1+1) */
+ target = target + 1;
+ } else if (target->opcode == ZEND_BOOL &&
+ (SAME_VAR(target->op1, opline->result) ||
+ SAME_VAR(target->op1, opline->op1))) {
+ /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
+ Z = JMPZ_EX(X,L1+1) */
+
+ /* NOTE: This optimization pattern is not safe, but works, */
+ /* because result of JMPZ_EX instruction */
+ /* is not used on the following path and */
+ /* should be used once on the branch path. */
+ /* */
+ /* The pattern works well only if jums processed in */
+ /* direct order, otherwise it breaks JMPZ_EX */
+ /* sequences too early. */
+ opline->result.var = target->result.var;
+ target = target + 1;
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
+ }
+
+ /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
+ if (target == opline + 1) {
+ opline->opcode = ZEND_BOOL;
+ opline->op2.num = 0;
+ }
+ break;
+
+ case ZEND_JMPZNZ:
+optimize_jmpznz:
+ jmp_hitlist_count = 0;
+ target = ZEND_OP2_JMP_ADDR(opline);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ SAME_VAR(target->op1, opline->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ target = ZEND_OP2_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, opline->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+ target = target + 1;
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
+ }
+
+ jmp_hitlist_count = 0;
+ target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+ while (1) {
+ if (target->opcode == ZEND_JMP) {
+ /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
+ target = ZEND_OP1_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_JMPNZ &&
+ SAME_VAR(target->op1, opline->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
+ target = ZEND_OP2_JMP_ADDR(target);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_JMPZ &&
+ SAME_VAR(target->op1, opline->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ target = target + 1;
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(target->op1, opline->op1)) {
+ /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
+ target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
+ CHECK_LOOP(target);
+ } else if (target->opcode == ZEND_NOP) {
+ target = target + 1;
+ } else {
+ break;
+ }
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target);
+ }
+
+ if (ZEND_OP2_JMP_ADDR(opline) == target &&
+ !(opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
+ /* JMPZNZ(?,L,L) -> JMP(L) */
+ opline->opcode = ZEND_JMP;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
+ SET_UNUSED(opline->op1);
+ SET_UNUSED(opline->op2);
+ opline->extended_value = 0;
+ }
+ /* Don't convert JMPZNZ back to JMPZ/JMPNZ, because the
+ following JMP is not removed yet. */
+ break;
+ }
+ opline++;
+ }
+ free_alloca(jmp_hitlist, use_heap);
+}
diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c
new file mode 100644
index 0000000000..e4b7531f46
--- /dev/null
+++ b/Zend/Optimizer/sccp.c
@@ -0,0 +1,2483 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SCCP - Sparse Conditional Constant Propagation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_type_info.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "Optimizer/zend_call_graph.h"
+#include "Optimizer/zend_inference.h"
+#include "Optimizer/scdf.h"
+#include "Optimizer/zend_dump.h"
+#include "ext/standard/php_string.h"
+#include "zend_exceptions.h"
+
+/* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
+ * used value lattice is defined as follows:
+ *
+ * BOT < {constant values} < TOP
+ *
+ * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
+ * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
+ *
+ * All variables are optimistically initialized to TOP, apart from the implicit variables defined
+ * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
+ * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
+ * This is better because such operations might never be reached due to the conditional nature of
+ * the algorithm.
+ *
+ * The meet operation for phi functions is defined as follows:
+ * BOT + any = BOT
+ * TOP + any = any
+ * C_i + C_i = C_i (i.e. two equal constants)
+ * C_i + C_j = BOT (i.e. two different constants)
+ *
+ * When evaluating instructions TOP and BOT are handled as follows:
+ * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
+ * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
+ * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
+ * specific modifiers) the result is BOT.
+ * c) Otherwise, if any operand is TOP, the result is TOP.
+ * d) Otherwise (at this point all operands are known and constant), if we can compute the result
+ * for these specific constants (without throwing notices or similar) then that is the result.
+ * e) Otherwise the result is BOT.
+ *
+ * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
+ * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating
+ * TYPE_CHECKS based on the type information.
+ *
+ * Feasible successors for conditional branches are determined as follows:
+ * a) If we don't support the branch type or branch on BOT, all successors are feasible.
+ * b) Otherwise, if we branch on TOP none of the successors are feasible.
+ * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
+ * (usually only one successor will be feasible).
+ *
+ * The original SCCP algorithm is extended with ability to propagate constant array
+ * elements and object properties. The extension is based on a variation of Array
+ * SSA form and its application to Spare Constant Propagation, described at
+ * "Array SSA Form" by Vivek Sarkar, Kathleen Knobe and Stephen Fink in chapter
+ * 16 of the SSA book.
+ */
+
+#define SCP_DEBUG 0
+
+typedef struct _sccp_ctx {
+ scdf_ctx scdf;
+ zend_call_info **call_map;
+ zval *values;
+ zval top;
+ zval bot;
+} sccp_ctx;
+
+#define TOP ((zend_uchar)-1)
+#define BOT ((zend_uchar)-2)
+#define PARTIAL_ARRAY ((zend_uchar)-3)
+#define PARTIAL_OBJECT ((zend_uchar)-4)
+#define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
+#define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
+#define IS_PARTIAL_ARRAY(zv) (Z_TYPE_P(zv) == PARTIAL_ARRAY)
+#define IS_PARTIAL_OBJECT(zv) (Z_TYPE_P(zv) == PARTIAL_OBJECT)
+
+#define MAKE_PARTIAL_ARRAY(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
+#define MAKE_PARTIAL_OBJECT(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
+
+#define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
+#define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
+
+static void scp_dump_value(zval *zv) {
+ if (IS_TOP(zv)) {
+ fprintf(stderr, " top");
+ } else if (IS_BOT(zv)) {
+ fprintf(stderr, " bot");
+ } else if (Z_TYPE_P(zv) == IS_ARRAY || IS_PARTIAL_ARRAY(zv)) {
+ fprintf(stderr, " %s[", IS_PARTIAL_ARRAY(zv) ? "partial " : "");
+ zend_dump_ht(Z_ARRVAL_P(zv));
+ fprintf(stderr, "]");
+ } else if (IS_PARTIAL_OBJECT(zv)) {
+ fprintf(stderr, " {");
+ zend_dump_ht(Z_ARRVAL_P(zv));
+ fprintf(stderr, "}");
+ } else {
+ zend_dump_const(zv);
+ }
+}
+
+static void empty_partial_array(zval *zv)
+{
+ MAKE_PARTIAL_ARRAY(zv);
+ Z_ARR_P(zv) = zend_new_array(8);
+}
+
+static void dup_partial_array(zval *dst, zval *src)
+{
+ MAKE_PARTIAL_ARRAY(dst);
+ Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
+}
+
+static void empty_partial_object(zval *zv)
+{
+ MAKE_PARTIAL_OBJECT(zv);
+ Z_ARR_P(zv) = zend_new_array(8);
+}
+
+static void dup_partial_object(zval *dst, zval *src)
+{
+ MAKE_PARTIAL_OBJECT(dst);
+ Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
+}
+
+static inline bool value_known(zval *zv) {
+ return !IS_TOP(zv) && !IS_BOT(zv);
+}
+
+/* Sets new value for variable and ensures that it is lower or equal
+ * the previous one in the constant propagation lattice. */
+static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) {
+ zval *value = &ctx->values[var];
+ if (IS_BOT(value) || IS_TOP(new)) {
+ return;
+ }
+
+#if SCP_DEBUG
+ fprintf(stderr, "Lowering #%d.", var);
+ zend_dump_var(scdf->op_array, IS_CV, scdf->ssa->vars[var].var);
+ fprintf(stderr, " from");
+ scp_dump_value(value);
+ fprintf(stderr, " to");
+ scp_dump_value(new);
+ fprintf(stderr, "\n");
+#endif
+
+ if (IS_TOP(value) || IS_BOT(new)) {
+ zval_ptr_dtor_nogc(value);
+ ZVAL_COPY(value, new);
+ scdf_add_to_worklist(scdf, var);
+ return;
+ }
+
+ /* Always replace PARTIAL_(ARRAY|OBJECT), as new maybe changed by join_partial_(arrays|object) */
+ if (IS_PARTIAL_ARRAY(new) || IS_PARTIAL_OBJECT(new)) {
+ if (Z_TYPE_P(value) != Z_TYPE_P(new)
+ || zend_hash_num_elements(Z_ARR_P(new)) != zend_hash_num_elements(Z_ARR_P(value))) {
+ zval_ptr_dtor_nogc(value);
+ ZVAL_COPY(value, new);
+ scdf_add_to_worklist(scdf, var);
+ }
+ return;
+ }
+
+#if ZEND_DEBUG
+ ZEND_ASSERT(zend_is_identical(value, new));
+#endif
+}
+
+static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+ if (opline->op1_type == IS_CONST) {
+ return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
+ } else if (ssa_op->op1_use != -1) {
+ return &ctx->values[ssa_op->op1_use];
+ } else {
+ return NULL;
+ }
+}
+
+static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+ if (opline->op2_type == IS_CONST) {
+ return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
+ } else if (ssa_op->op2_use != -1) {
+ return &ctx->values[ssa_op->op2_use];
+ } else {
+ return NULL;
+ }
+}
+
+static bool can_replace_op1(
+ const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
+ switch (opline->opcode) {
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ 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:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_LIST_W:
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_SEND_UNPACK:
+ case ZEND_SEND_ARRAY:
+ case ZEND_SEND_USER:
+ case ZEND_FE_RESET_RW:
+ return 0;
+ /* Do not accept CONST */
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ case ZEND_BIND_STATIC:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_MAKE_REF:
+ case ZEND_UNSET_CV:
+ case ZEND_ISSET_ISEMPTY_CV:
+ return 0;
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
+ case ZEND_YIELD:
+ return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
+ 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_UNREACHABLE();
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static 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_CLASS_DELAYED:
+ case ZEND_BIND_LEXICAL:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ return 0;
+ }
+ return 1;
+}
+
+static bool try_replace_op1(
+ sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
+ if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
+ zval zv;
+ ZVAL_COPY(&zv, value);
+ if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
+ return 1;
+ } else {
+ // TODO: check the following special cases ???
+ switch (opline->opcode) {
+ case ZEND_CASE:
+ opline->opcode = ZEND_IS_EQUAL;
+ goto replace_op1_simple;
+ case ZEND_CASE_STRICT:
+ opline->opcode = ZEND_IS_IDENTICAL;
+ goto replace_op1_simple;
+ case ZEND_FETCH_LIST_R:
+ case ZEND_SWITCH_STRING:
+ case ZEND_SWITCH_LONG:
+ case ZEND_MATCH:
+replace_op1_simple:
+ if (Z_TYPE(zv) == IS_STRING) {
+ zend_string_hash_val(Z_STR(zv));
+ }
+ opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
+ opline->op1_type = IS_CONST;
+ return 1;
+ case ZEND_INSTANCEOF:
+ zval_ptr_dtor_nogc(&zv);
+ ZVAL_FALSE(&zv);
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op1_type = IS_CONST;
+ opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
+ opline->op2_type = IS_UNUSED;
+ if (ssa_op->op2_use >= 0) {
+ ZEND_ASSERT(ssa_op->op2_def == -1);
+ zend_ssa_unlink_use_chain(ctx->scdf.ssa, ssa_op - ctx->scdf.ssa->ops, ssa_op->op2_use);
+ ssa_op->op2_use = -1;
+ ssa_op->op2_use_chain = -1;
+ }
+ return 1;
+ default:
+ break;
+ }
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ return 0;
+}
+
+static bool try_replace_op2(
+ sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
+ if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) {
+ zval zv;
+ ZVAL_COPY(&zv, value);
+ if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
+ return 1;
+ } else {
+ switch (opline->opcode) {
+ case ZEND_FETCH_CLASS:
+ if (Z_TYPE(zv) == IS_STRING) {
+ ZEND_ASSERT((opline + 1)->opcode == ZEND_INSTANCEOF);
+ 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_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;
+ zend_ssa_remove_result_def(ctx->scdf.ssa, ssa_op);
+ MAKE_NOP(opline);
+ return 1;
+ }
+ }
+ default:
+ break;
+ }
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ return 0;
+}
+
+static inline int ct_eval_binary_op(zval *result, zend_uchar binop, zval *op1, zval *op2) {
+ /* TODO: We could implement support for evaluation of + on partial arrays. */
+ if (IS_PARTIAL_ARRAY(op1) || IS_PARTIAL_ARRAY(op2)) {
+ return FAILURE;
+ }
+
+ return zend_optimizer_eval_binary_op(result, binop, op1, op2);
+}
+
+static inline int ct_eval_bool_cast(zval *result, zval *op) {
+ if (IS_PARTIAL_ARRAY(op)) {
+ if (zend_hash_num_elements(Z_ARRVAL_P(op)) == 0) {
+ /* An empty partial array may be non-empty at runtime, we don't know whether the
+ * result will be true or false. */
+ return FAILURE;
+ }
+
+ ZVAL_TRUE(result);
+ return SUCCESS;
+ }
+
+ ZVAL_BOOL(result, zend_is_true(op));
+ return SUCCESS;
+}
+
+static inline int zval_to_string_offset(zend_long *result, zval *op) {
+ switch (Z_TYPE_P(op)) {
+ case IS_LONG:
+ *result = Z_LVAL_P(op);
+ return SUCCESS;
+ case IS_STRING:
+ if (IS_LONG == is_numeric_string(
+ Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
+ return SUCCESS;
+ }
+ return FAILURE;
+ default:
+ return FAILURE;
+ }
+}
+
+static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) {
+ switch (Z_TYPE_P(op2)) {
+ case IS_NULL:
+ *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
+ return SUCCESS;
+ case IS_FALSE:
+ *result = zend_hash_index_find(Z_ARR_P(op1), 0);
+ return SUCCESS;
+ case IS_TRUE:
+ *result = zend_hash_index_find(Z_ARR_P(op1), 1);
+ return SUCCESS;
+ case IS_LONG:
+ *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
+ return SUCCESS;
+ case IS_DOUBLE:
+ *result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2)));
+ return SUCCESS;
+ case IS_STRING:
+ *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
+ return SUCCESS;
+ default:
+ return FAILURE;
+ }
+}
+
+static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
+ if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
+ zval *value;
+ if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
+ ZVAL_COPY(result, value);
+ return SUCCESS;
+ }
+ } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
+ zend_long index;
+ if (zval_to_string_offset(&index, op2) == FAILURE) {
+ return FAILURE;
+ }
+ if (index >= 0 && index < Z_STRLEN_P(op1)) {
+ ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
+ return SUCCESS;
+ }
+ }
+ return FAILURE;
+}
+
+/* op1 may be NULL here to indicate an unset value */
+static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
+ zval zv;
+ if (!(extended_value & ZEND_ISEMPTY)) {
+ ZVAL_BOOL(result, op1 && Z_TYPE_P(op1) != IS_NULL);
+ return SUCCESS;
+ } else if (!op1) {
+ ZVAL_TRUE(result);
+ return SUCCESS;
+ } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
+ ZVAL_BOOL(result, Z_TYPE(zv) == IS_FALSE);
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+
+static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
+ if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
+ zval *value;
+ if (fetch_array_elem(&value, op1, op2) == FAILURE) {
+ return FAILURE;
+ }
+ if (IS_PARTIAL_ARRAY(op1) && (!value || IS_BOT(value))) {
+ return FAILURE;
+ }
+ return ct_eval_isset_isempty(result, extended_value, value);
+ } else if (Z_TYPE_P(op1) == IS_STRING) {
+ // TODO
+ return FAILURE;
+ } else {
+ ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
+ return SUCCESS;
+ }
+}
+
+static inline int ct_eval_del_array_elem(zval *result, zval *key) {
+ ZEND_ASSERT(IS_PARTIAL_ARRAY(result));
+
+ switch (Z_TYPE_P(key)) {
+ case IS_NULL:
+ zend_hash_del(Z_ARR_P(result), ZSTR_EMPTY_ALLOC());
+ break;
+ case IS_FALSE:
+ zend_hash_index_del(Z_ARR_P(result), 0);
+ break;
+ case IS_TRUE:
+ zend_hash_index_del(Z_ARR_P(result), 1);
+ break;
+ case IS_LONG:
+ zend_hash_index_del(Z_ARR_P(result), Z_LVAL_P(key));
+ break;
+ case IS_DOUBLE:
+ zend_hash_index_del(Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)));
+ break;
+ case IS_STRING:
+ zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
+ break;
+ default:
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) {
+ if (!key) {
+ SEPARATE_ARRAY(result);
+ if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
+ Z_TRY_ADDREF_P(value);
+ return SUCCESS;
+ }
+ return FAILURE;
+ }
+
+ switch (Z_TYPE_P(key)) {
+ case IS_NULL:
+ SEPARATE_ARRAY(result);
+ value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
+ break;
+ case IS_FALSE:
+ SEPARATE_ARRAY(result);
+ value = zend_hash_index_update(Z_ARR_P(result), 0, value);
+ break;
+ case IS_TRUE:
+ SEPARATE_ARRAY(result);
+ value = zend_hash_index_update(Z_ARR_P(result), 1, value);
+ break;
+ case IS_LONG:
+ SEPARATE_ARRAY(result);
+ value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
+ break;
+ case IS_DOUBLE:
+ SEPARATE_ARRAY(result);
+ value = zend_hash_index_update(
+ Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value);
+ break;
+ case IS_STRING:
+ SEPARATE_ARRAY(result);
+ value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
+ break;
+ default:
+ return FAILURE;
+ }
+
+ Z_TRY_ADDREF_P(value);
+ 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) {
+ value = zend_hash_update(Z_ARR_P(result), key, value);
+ } else {
+ 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:
+ case IS_FALSE:
+ array_init(result);
+ /* break missing intentionally */
+ case IS_ARRAY:
+ case PARTIAL_ARRAY:
+ return ct_eval_add_array_elem(result, value, key);
+ case IS_STRING:
+ // TODO Before enabling this case, make sure ARRAY_DIM result op is correct
+#if 0
+ zend_long index;
+ zend_string *new_str, *value_str;
+ if (!key || Z_TYPE_P(value) == IS_ARRAY
+ || zval_to_string_offset(&index, key) == FAILURE || index < 0) {
+ return FAILURE;
+ }
+
+ if (index >= Z_STRLEN_P(result)) {
+ new_str = zend_string_alloc(index + 1, 0);
+ memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
+ memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
+ ZSTR_VAL(new_str)[index + 1] = 0;
+ } else {
+ new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
+ }
+
+ value_str = zval_get_string(value);
+ ZVAL_STR(result, new_str);
+ Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
+ zend_string_release_ex(value_str, 0);
+#endif
+ return FAILURE;
+ default:
+ return FAILURE;
+ }
+}
+
+static inline int fetch_obj_prop(zval **result, zval *op1, zval *op2) {
+ switch (Z_TYPE_P(op2)) {
+ case IS_STRING:
+ *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
+ return SUCCESS;
+ default:
+ return FAILURE;
+ }
+}
+
+static inline int ct_eval_fetch_obj(zval *result, zval *op1, zval *op2) {
+ if (IS_PARTIAL_OBJECT(op1)) {
+ zval *value;
+ if (fetch_obj_prop(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
+ ZVAL_COPY(result, value);
+ return SUCCESS;
+ }
+ }
+ return FAILURE;
+}
+
+static inline int ct_eval_isset_obj(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
+ if (IS_PARTIAL_OBJECT(op1)) {
+ zval *value;
+ if (fetch_obj_prop(&value, op1, op2) == FAILURE) {
+ return FAILURE;
+ }
+ if (!value || IS_BOT(value)) {
+ return FAILURE;
+ }
+ return ct_eval_isset_isempty(result, extended_value, value);
+ } else {
+ ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
+ return SUCCESS;
+ }
+}
+
+static inline int ct_eval_del_obj_prop(zval *result, zval *key) {
+ ZEND_ASSERT(IS_PARTIAL_OBJECT(result));
+
+ switch (Z_TYPE_P(key)) {
+ case IS_STRING:
+ zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
+ break;
+ default:
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static inline int ct_eval_add_obj_prop(zval *result, zval *value, zval *key) {
+ switch (Z_TYPE_P(key)) {
+ case IS_STRING:
+ value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
+ break;
+ default:
+ return FAILURE;
+ }
+
+ Z_TRY_ADDREF_P(value);
+ return SUCCESS;
+}
+
+static inline int ct_eval_assign_obj(zval *result, zval *value, zval *key) {
+ switch (Z_TYPE_P(result)) {
+ case IS_NULL:
+ case IS_FALSE:
+ empty_partial_object(result);
+ /* break missing intentionally */
+ case PARTIAL_OBJECT:
+ return ct_eval_add_obj_prop(result, value, key);
+ default:
+ return FAILURE;
+ }
+}
+
+static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) {
+ ZVAL_COPY(result, op1);
+ if (opcode == ZEND_PRE_INC
+ || opcode == ZEND_POST_INC
+ || opcode == ZEND_PRE_INC_OBJ
+ || opcode == ZEND_POST_INC_OBJ) {
+ increment_function(result);
+ } else {
+ decrement_function(result);
+ }
+ return SUCCESS;
+}
+
+static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) {
+ uint32_t type = Z_TYPE_P(op1);
+ if (type == PARTIAL_ARRAY) {
+ type = IS_ARRAY;
+ } else if (type == PARTIAL_OBJECT) {
+ type = IS_OBJECT;
+ }
+ ZVAL_BOOL(result, (type_mask >> type) & 1);
+}
+
+static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
+ HashTable *ht;
+ bool res;
+
+ if (Z_TYPE_P(op2) != IS_ARRAY) {
+ return FAILURE;
+ }
+ ht = Z_ARRVAL_P(op2);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ res = zend_hash_exists(ht, Z_STR_P(op1));
+ } else if (extended_value) {
+ if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
+ } else {
+ res = 0;
+ }
+ } else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
+ } else {
+ zend_string *key;
+ zval key_tmp;
+
+ res = 0;
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
+ ZVAL_STR(&key_tmp, key);
+ if (zend_compare(op1, &key_tmp) == 0) {
+ res = 1;
+ break;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ ZVAL_BOOL(result, res);
+ 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;
+}
+
+static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **args) {
+ /* Functions that can be evaluated independently of what the arguments are.
+ * It's okay if these functions throw on invalid arguments, but they should not warn. */
+ if (false
+ || zend_string_equals_literal(name, "array_diff")
+ || zend_string_equals_literal(name, "array_diff_assoc")
+ || zend_string_equals_literal(name, "array_diff_key")
+ || zend_string_equals_literal(name, "array_flip")
+ || zend_string_equals_literal(name, "array_is_list")
+ || zend_string_equals_literal(name, "array_key_exists")
+ || zend_string_equals_literal(name, "array_keys")
+ || zend_string_equals_literal(name, "array_merge")
+ || zend_string_equals_literal(name, "array_merge_recursive")
+ || zend_string_equals_literal(name, "array_replace")
+ || zend_string_equals_literal(name, "array_replace_recursive")
+ || zend_string_equals_literal(name, "array_values")
+ || zend_string_equals_literal(name, "base64_decode")
+ || zend_string_equals_literal(name, "base64_encode")
+#ifndef ZEND_WIN32
+ /* On Windows this function may be code page dependent. */
+ || zend_string_equals_literal(name, "dirname")
+#endif
+ || zend_string_equals_literal(name, "imagetypes")
+ || zend_string_equals_literal(name, "in_array")
+ || zend_string_equals_literal(name, "implode")
+ || zend_string_equals_literal(name, "ltrim")
+ || zend_string_equals_literal(name, "php_sapi_name")
+ || zend_string_equals_literal(name, "php_uname")
+ || zend_string_equals_literal(name, "phpversion")
+ || zend_string_equals_literal(name, "pow")
+ || zend_string_equals_literal(name, "preg_quote")
+ || zend_string_equals_literal(name, "rawurldecode")
+ || zend_string_equals_literal(name, "rawurlencode")
+ || zend_string_equals_literal(name, "rtrim")
+ || zend_string_equals_literal(name, "serialize")
+ || zend_string_equals_literal(name, "str_contains")
+ || zend_string_equals_literal(name, "str_ends_with")
+ || zend_string_equals_literal(name, "str_split")
+ || zend_string_equals_literal(name, "str_starts_with")
+ || zend_string_equals_literal(name, "strpos")
+ || zend_string_equals_literal(name, "substr")
+ || zend_string_equals_literal(name, "trim")
+ || zend_string_equals_literal(name, "urldecode")
+ || zend_string_equals_literal(name, "urlencode")
+ || zend_string_equals_literal(name, "version_compare")
+ ) {
+ return true;
+ }
+
+ if (num_args == 2) {
+ if (zend_string_equals_literal(name, "str_repeat")) {
+ /* Avoid creating overly large strings at compile-time. */
+ bool overflow;
+ return Z_TYPE_P(args[0]) == IS_STRING
+ && Z_TYPE_P(args[1]) == IS_LONG
+ && zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024
+ && !overflow;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+/* 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). */
+static inline int ct_eval_func_call(
+ zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) {
+ uint32_t i;
+ zend_function *func = zend_hash_find_ptr(CG(function_table), name);
+ if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
+ return FAILURE;
+ }
+
+ if (num_args == 1) {
+ /* Handle a few functions for which we manually implement evaluation here. */
+ if (zend_string_equals_literal(name, "chr")) {
+ zend_long c;
+ if (Z_TYPE_P(args[0]) != IS_LONG) {
+ return FAILURE;
+ }
+
+ c = Z_LVAL_P(args[0]) & 0xff;
+ ZVAL_CHAR(result, c);
+ return SUCCESS;
+ } else if (zend_string_equals_literal(name, "count")) {
+ if (Z_TYPE_P(args[0]) != IS_ARRAY) {
+ return FAILURE;
+ }
+
+ ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
+ return SUCCESS;
+ } else if (zend_string_equals_literal(name, "ini_get")) {
+ zend_ini_entry *ini_entry;
+
+ if (Z_TYPE_P(args[0]) != IS_STRING) {
+ return FAILURE;
+ }
+
+ ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
+ if (!ini_entry) {
+ ZVAL_FALSE(result);
+ } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
+ return FAILURE;
+ } else if (ini_entry->value) {
+ ZVAL_STR_COPY(result, ini_entry->value);
+ } else {
+ ZVAL_EMPTY_STRING(result);
+ }
+ return SUCCESS;
+ }
+ }
+
+ if (!can_ct_eval_func_call(name, num_args, args)) {
+ return FAILURE;
+ }
+
+ zend_execute_data *prev_execute_data = EG(current_execute_data);
+ zend_execute_data *execute_data, dummy_frame;
+ zend_op dummy_opline;
+
+ /* Add a dummy frame to get the correct strict_types behavior. */
+ memset(&dummy_frame, 0, sizeof(zend_execute_data));
+ memset(&dummy_opline, 0, sizeof(zend_op));
+ dummy_frame.func = (zend_function *) op_array;
+ dummy_frame.opline = &dummy_opline;
+ dummy_opline.opcode = ZEND_DO_FCALL;
+
+ execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
+ memset(execute_data, 0, sizeof(zend_execute_data));
+ execute_data->prev_execute_data = &dummy_frame;
+ EG(current_execute_data) = execute_data;
+
+ /* Enable suppression and counting of warnings. */
+ ZEND_ASSERT(EG(capture_warnings_during_sccp) == 0);
+ EG(capture_warnings_during_sccp) = 1;
+
+ EX(func) = func;
+ EX_NUM_ARGS() = num_args;
+ for (i = 0; i < num_args; i++) {
+ ZVAL_COPY(EX_VAR_NUM(i), args[i]);
+ }
+ ZVAL_NULL(result);
+ func->internal_function.handler(execute_data, result);
+ for (i = 0; i < num_args; i++) {
+ zval_ptr_dtor_nogc(EX_VAR_NUM(i));
+ }
+
+ int retval = SUCCESS;
+ if (EG(exception)) {
+ zval_ptr_dtor(result);
+ zend_clear_exception();
+ retval = FAILURE;
+ }
+
+ if (EG(capture_warnings_during_sccp) > 1) {
+ zval_ptr_dtor(result);
+ retval = FAILURE;
+ }
+ EG(capture_warnings_during_sccp) = 0;
+
+ efree(execute_data);
+ EG(current_execute_data) = prev_execute_data;
+ return retval;
+}
+
+#define SET_RESULT(op, zv) do { \
+ if (ssa_op->op##_def >= 0) { \
+ set_value(scdf, ctx, ssa_op->op##_def, zv); \
+ } \
+} while (0)
+#define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
+#define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
+
+#define SKIP_IF_TOP(op) if (IS_TOP(op)) return;
+
+static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
+ sccp_ctx *ctx = (sccp_ctx *) scdf;
+ zval *op1, *op2, zv; /* zv is a temporary to hold result values */
+
+ op1 = get_op1_value(ctx, opline, ssa_op);
+ op2 = get_op2_value(ctx, opline, ssa_op);
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ /* The value of op1 is irrelevant here, because we are overwriting it
+ * -- unless it can be a reference, in which case we propagate a BOT. */
+ if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
+ SET_RESULT_BOT(op1);
+ } else {
+ SET_RESULT(op1, op2);
+ }
+
+ SET_RESULT(result, op2);
+ return;
+ case ZEND_TYPE_CHECK:
+ /* We may be able to evaluate TYPE_CHECK based on type inference info,
+ * even if we don't know the precise value. */
+ if (!value_known(op1)) {
+ uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
+ uint32_t expected_type_mask = opline->extended_value;
+ if (!(type & expected_type_mask) && !(type & MAY_BE_UNDEF)) {
+ ZVAL_FALSE(&zv);
+ SET_RESULT(result, &zv);
+ return;
+ } else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type_mask))
+ && !(expected_type_mask & MAY_BE_RESOURCE)) {
+ ZVAL_TRUE(&zv);
+ SET_RESULT(result, &zv);
+ return;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ {
+ zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
+
+ /* If $a in $a[$b]=$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) {
+ op1 = &EG(uninitialized_zval);
+ }
+
+ if (IS_BOT(op1)) {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ return;
+ }
+
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(data);
+ if (op2) {
+ SKIP_IF_TOP(op2);
+ }
+
+ if (op2 && IS_BOT(op2)) {
+ /* Update of unknown index */
+ SET_RESULT_BOT(result);
+ if (ssa_op->op1_def >= 0) {
+ empty_partial_array(&zv);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ SET_RESULT_BOT(op1);
+ }
+ return;
+ }
+
+ if (IS_BOT(data)) {
+
+ SET_RESULT_BOT(result);
+ if ((IS_PARTIAL_ARRAY(op1)
+ || Z_TYPE_P(op1) == IS_NULL
+ || Z_TYPE_P(op1) == IS_FALSE
+ || Z_TYPE_P(op1) == IS_ARRAY)
+ && ssa_op->op1_def >= 0) {
+
+ if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
+ empty_partial_array(&zv);
+ } else {
+ dup_partial_array(&zv, op1);
+ }
+
+ if (!op2) {
+ /* We can't add NEXT element into partial array (skip it) */
+ SET_RESULT(op1, &zv);
+ } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
+ SET_RESULT(op1, &zv);
+ } else {
+ SET_RESULT_BOT(op1);
+ }
+
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ SET_RESULT_BOT(op1);
+ }
+
+ } else {
+
+ if (IS_PARTIAL_ARRAY(op1)) {
+ dup_partial_array(&zv, op1);
+ } else {
+ ZVAL_COPY(&zv, op1);
+ }
+
+ if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
+ /* We can't add NEXT element into partial array (skip it) */
+ SET_RESULT(result, data);
+ SET_RESULT(op1, &zv);
+ } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
+ /* Mark array containing partial array as partial */
+ if (IS_PARTIAL_ARRAY(data)) {
+ MAKE_PARTIAL_ARRAY(&zv);
+ }
+ SET_RESULT(result, data);
+ SET_RESULT(op1, &zv);
+ } else {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ }
+
+ zval_ptr_dtor_nogc(&zv);
+ }
+ return;
+ }
+
+ case ZEND_ASSIGN_OBJ:
+ 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 (IS_BOT(op1)) {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ return;
+ }
+
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(data);
+ SKIP_IF_TOP(op2);
+
+ if (IS_BOT(op2)) {
+ /* Update of unknown property */
+ SET_RESULT_BOT(result);
+ empty_partial_object(&zv);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ return;
+ }
+
+ if (IS_BOT(data)) {
+ SET_RESULT_BOT(result);
+ if (IS_PARTIAL_OBJECT(op1)
+ || Z_TYPE_P(op1) == IS_NULL
+ || Z_TYPE_P(op1) == IS_FALSE) {
+
+ if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
+ empty_partial_object(&zv);
+ } else {
+ dup_partial_object(&zv, op1);
+ }
+
+ if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) {
+ SET_RESULT(op1, &zv);
+ } else {
+ SET_RESULT_BOT(op1);
+ }
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ SET_RESULT_BOT(op1);
+ }
+
+ } else {
+
+ if (IS_PARTIAL_OBJECT(op1)) {
+ dup_partial_object(&zv, op1);
+ } else {
+ ZVAL_COPY(&zv, op1);
+ }
+
+ if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) {
+ SET_RESULT(result, data);
+ SET_RESULT(op1, &zv);
+ } else {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ }
+
+ zval_ptr_dtor_nogc(&zv);
+ }
+ } else {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ }
+ return;
+
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ {
+ /* If the value of a SEND for an ICALL changes, we need to reconsider the
+ * ICALL result value. Otherwise we can ignore the opcode. */
+ zend_call_info *call;
+ if (!ctx->call_map) {
+ return;
+ }
+
+ call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
+ if (IS_TOP(op1) || !call || !call->caller_call_opline
+ || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
+ return;
+ }
+
+ opline = call->caller_call_opline;
+ ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
+ break;
+ }
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ {
+ zval *result = NULL;
+
+ if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
+ result = &ctx->values[ssa_op->result_use];
+ if (IS_BOT(result)) {
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ return;
+ }
+ SKIP_IF_TOP(result);
+ }
+
+ if (op1) {
+ SKIP_IF_TOP(op1);
+ }
+
+ if (op2) {
+ SKIP_IF_TOP(op2);
+ }
+
+ /* We want to avoid keeping around intermediate arrays for each SSA variable in the
+ * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
+ * and use a NULL value everywhere else. */
+ if (result && Z_TYPE_P(result) == IS_NULL) {
+ SET_RESULT_BOT(result);
+ return;
+ }
+
+ if (op2 && IS_BOT(op2)) {
+ /* Update of unknown index */
+ SET_RESULT_BOT(op1);
+ if (ssa_op->result_def >= 0) {
+ empty_partial_array(&zv);
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ SET_RESULT_BOT(result);
+ }
+ return;
+ }
+
+ if ((op1 && IS_BOT(op1))
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
+
+ SET_RESULT_BOT(op1);
+ if (ssa_op->result_def >= 0) {
+ if (!result) {
+ empty_partial_array(&zv);
+ } else {
+ MAKE_PARTIAL_ARRAY(result);
+ ZVAL_COPY_VALUE(&zv, result);
+ ZVAL_NULL(result);
+ }
+ if (!op2) {
+ /* We can't add NEXT element into partial array (skip it) */
+ SET_RESULT(result, &zv);
+ } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ } else {
+ SET_RESULT_BOT(result);
+ }
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ /* If any operand is BOT, mark the result as BOT right away.
+ * Exceptions to this rule are handled above. */
+ SET_RESULT_BOT(result);
+ }
+
+ } else {
+ if (result) {
+ ZVAL_COPY_VALUE(&zv, result);
+ ZVAL_NULL(result);
+ } else {
+ array_init(&zv);
+ }
+
+ if (op1) {
+ if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
+ /* We can't add NEXT element into partial array (skip it) */
+ SET_RESULT(result, &zv);
+ } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
+ if (IS_PARTIAL_ARRAY(op1)) {
+ MAKE_PARTIAL_ARRAY(&zv);
+ }
+ SET_RESULT(result, &zv);
+ } else {
+ SET_RESULT_BOT(result);
+ }
+ } else {
+ SET_RESULT(result, &zv);
+ }
+
+ zval_ptr_dtor_nogc(&zv);
+ }
+ 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) {
+ empty_partial_object(&zv);
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ } else {
+ 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))) {
+ /* If any operand is BOT, mark the result as BOT right away.
+ * Exceptions to this rule are handled above. */
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ SET_RESULT_BOT(op2);
+ return;
+ }
+
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_POW:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_BOOL_XOR:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+
+ if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ 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->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->opcode == ZEND_ASSIGN_DIM_OP) {
+ if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY)
+ && ssa_op->op1_def >= 0 && op2) {
+ zval tmp;
+ zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
+
+ SKIP_IF_TOP(data);
+
+ if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) {
+ if (IS_BOT(data)) {
+ dup_partial_array(&zv, op1);
+ ct_eval_del_array_elem(&zv, op2);
+ SET_RESULT_BOT(result);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+
+ 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);
+ break;
+ }
+
+ if (IS_PARTIAL_ARRAY(op1)) {
+ dup_partial_array(&zv, op1);
+ } else {
+ ZVAL_COPY(&zv, op1);
+ }
+
+ if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) {
+ SET_RESULT(result, &tmp);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ } 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) {
+ zval tmp;
+ zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
+
+ SKIP_IF_TOP(data);
+
+ if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) {
+ if (IS_BOT(data)) {
+ dup_partial_object(&zv, op1);
+ ct_eval_del_obj_prop(&zv, op2);
+ SET_RESULT_BOT(result);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+
+ 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);
+ break;
+ }
+
+ dup_partial_object(&zv, op1);
+
+ if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) {
+ SET_RESULT(result, &tmp);
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+
+ zval_ptr_dtor_nogc(&tmp);
+ zval_ptr_dtor_nogc(&zv);
+ }
+ }
+ }
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ break;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ if (op1) {
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+ if (IS_PARTIAL_OBJECT(op1)
+ && ssa_op->op1_def >= 0
+ && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ zval tmp1, tmp2;
+
+ if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS
+ && ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) {
+
+ dup_partial_object(&zv, op1);
+ ct_eval_assign_obj(&zv, &tmp2, op2);
+ if (opline->opcode == ZEND_PRE_INC_OBJ
+ || opline->opcode == ZEND_PRE_DEC_OBJ) {
+ SET_RESULT(result, &tmp2);
+ } else {
+ SET_RESULT(result, &tmp1);
+ }
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ }
+ }
+ SET_RESULT_BOT(op1);
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ SKIP_IF_TOP(op1);
+ if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
+ SET_RESULT(op1, &zv);
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(op1);
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ SKIP_IF_TOP(op1);
+ SET_RESULT(result, op1);
+ if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
+ SET_RESULT(op1, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(op1);
+ break;
+ case ZEND_BW_NOT:
+ case ZEND_BOOL_NOT:
+ SKIP_IF_TOP(op1);
+ if (IS_PARTIAL_ARRAY(op1)) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+ if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_CAST:
+ SKIP_IF_TOP(op1);
+ if (IS_PARTIAL_ARRAY(op1)) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+ if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_BOOL:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ SKIP_IF_TOP(op1);
+ if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_STRLEN:
+ SKIP_IF_TOP(op1);
+ if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_YIELD_FROM:
+ // tmp = yield from [] -> tmp = null
+ SKIP_IF_TOP(op1);
+ if (Z_TYPE_P(op1) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(op1)) == 0) {
+ ZVAL_NULL(&zv);
+ SET_RESULT(result, &zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_COUNT:
+ SKIP_IF_TOP(op1);
+ if (Z_TYPE_P(op1) == IS_ARRAY) {
+ ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_IN_ARRAY:
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+ if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ 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:
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+
+ if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+
+ if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_FETCH_OBJ_R:
+ case ZEND_FETCH_OBJ_IS:
+ if (op1) {
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+
+ if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ if (op1) {
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+
+ if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_COPY_TMP:
+ SET_RESULT(result, op1);
+ break;
+ case ZEND_JMP_NULL:
+ switch (opline->extended_value) {
+ case ZEND_SHORT_CIRCUITING_CHAIN_EXPR:
+ ZVAL_NULL(&zv);
+ break;
+ case ZEND_SHORT_CIRCUITING_CHAIN_ISSET:
+ ZVAL_FALSE(&zv);
+ break;
+ case ZEND_SHORT_CIRCUITING_CHAIN_EMPTY:
+ ZVAL_TRUE(&zv);
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ SET_RESULT(result, &zv);
+ break;
+#if 0
+ case ZEND_FETCH_CLASS:
+ if (!op1) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+ SET_RESULT(result, op1);
+ break;
+#endif
+ case ZEND_ISSET_ISEMPTY_CV:
+ SKIP_IF_TOP(op1);
+ if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_TYPE_CHECK:
+ SKIP_IF_TOP(op1);
+ ct_eval_type_check(&zv, opline->extended_value, op1);
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ case ZEND_INSTANCEOF:
+ SKIP_IF_TOP(op1);
+ ZVAL_FALSE(&zv);
+ SET_RESULT(result, &zv);
+ break;
+ case ZEND_ROPE_INIT:
+ SKIP_IF_TOP(op2);
+ if (IS_PARTIAL_ARRAY(op2)) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+ if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ // TODO The way this is currently implemented will result in quadratic runtime
+ // This is not necessary, the way the algorithm works it's okay to reuse the same
+ // string for all SSA vars with some extra checks
+ SKIP_IF_TOP(op1);
+ SKIP_IF_TOP(op2);
+ if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+ SET_RESULT_BOT(result);
+ break;
+ case ZEND_DO_ICALL:
+ {
+ zend_call_info *call;
+ zval *name, *args[3] = {NULL};
+ int i;
+
+ if (!ctx->call_map) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+
+ call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
+ name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
+
+ /* We already know it can't be evaluated, don't bother checking again */
+ if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
+ break;
+ }
+
+ /* We're only interested in functions with up to three arguments right now */
+ if (call->num_args > 3 || call->send_unpack) {
+ SET_RESULT_BOT(result);
+ break;
+ }
+
+ for (i = 0; i < call->num_args; i++) {
+ zend_op *opline = call->arg_info[i].opline;
+ if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
+ SET_RESULT_BOT(result);
+ return;
+ }
+
+ args[i] = get_op1_value(ctx, opline,
+ &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
+ if (args[i]) {
+ if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
+ SET_RESULT_BOT(result);
+ return;
+ } else if (IS_TOP(args[i])) {
+ return;
+ }
+ }
+ }
+
+ /* We didn't get a BOT argument, so value stays the same */
+ if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
+ break;
+ }
+
+ if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
+ SET_RESULT(result, &zv);
+ zval_ptr_dtor_nogc(&zv);
+ break;
+ }
+
+#if 0
+ /* sort out | uniq -c | sort -n */
+ fprintf(stderr, "%s\n", Z_STRVAL_P(name));
+ /*if (args[1]) {
+ php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
+ } else {
+ php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
+ }*/
+#endif
+
+ SET_RESULT_BOT(result);
+ break;
+ }
+ default:
+ {
+ /* If we have no explicit implementation return BOT */
+ SET_RESULT_BOT(result);
+ SET_RESULT_BOT(op1);
+ SET_RESULT_BOT(op2);
+ break;
+ }
+ }
+}
+
+/* Returns whether there is a successor */
+static void sccp_mark_feasible_successors(
+ scdf_ctx *scdf,
+ int block_num, zend_basic_block *block,
+ zend_op *opline, zend_ssa_op *ssa_op) {
+ sccp_ctx *ctx = (sccp_ctx *) scdf;
+ zval *op1, zv;
+ int s;
+
+ /* We can't determine the branch target at compile-time for these */
+ switch (opline->opcode) {
+ case ZEND_ASSERT_CHECK:
+ case ZEND_CATCH:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
+ return;
+ }
+
+ op1 = get_op1_value(ctx, opline, ssa_op);
+
+ /* Branch target can be either one */
+ if (!op1 || IS_BOT(op1)) {
+ for (s = 0; s < block->successors_count; s++) {
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
+ }
+ return;
+ }
+
+ /* Branch target not yet known */
+ if (IS_TOP(op1)) {
+ return;
+ }
+
+ switch (opline->opcode) {
+ case ZEND_JMPZ:
+ case ZEND_JMPZNZ:
+ case ZEND_JMPZ_EX:
+ {
+ if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
+ return;
+ }
+ s = Z_TYPE(zv) == IS_TRUE;
+ break;
+ }
+ case ZEND_JMPNZ:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ {
+ if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
+ return;
+ }
+ s = Z_TYPE(zv) == IS_FALSE;
+ break;
+ }
+ case ZEND_COALESCE:
+ s = (Z_TYPE_P(op1) == IS_NULL);
+ break;
+ case ZEND_JMP_NULL:
+ s = (Z_TYPE_P(op1) != IS_NULL);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ /* A non-empty partial array is definitely non-empty, but an
+ * empty partial array may be non-empty at runtime. */
+ if (Z_TYPE_P(op1) != IS_ARRAY ||
+ (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) {
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
+ return;
+ }
+ s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ bool strict_comparison = opline->opcode == ZEND_MATCH;
+ zend_uchar type = Z_TYPE_P(op1);
+ bool correct_type =
+ (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
+ || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
+ || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
+
+ if (correct_type) {
+ 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 = type == IS_LONG
+ ? zend_hash_index_find(jmptable, Z_LVAL_P(op1))
+ : 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;
+ } else if (strict_comparison) {
+ zend_op_array *op_array = scdf->op_array;
+ zend_ssa *ssa = scdf->ssa;
+ int 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]);
+ }
+ return;
+ }
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
+}
+
+static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
+{
+ zend_ulong index;
+ zend_string *key;
+ zval *val1, *val2;
+
+ ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) {
+ if (key) {
+ val2 = zend_hash_find(ht2, key);
+ } else {
+ val2 = zend_hash_index_find(ht2, index);
+ }
+ if (val2 && zend_is_identical(val1, val2)) {
+ if (key) {
+ val1 = zend_hash_add_new(ret, key, val1);
+ } else {
+ val1 = zend_hash_index_add_new(ret, index, val1);
+ }
+ Z_TRY_ADDREF_P(val1);
+ }
+ } ZEND_HASH_FOREACH_END();
+}
+
+static int join_partial_arrays(zval *a, zval *b)
+{
+ zval ret;
+
+ if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a))
+ || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) {
+ return FAILURE;
+ }
+
+ empty_partial_array(&ret);
+ join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
+ zval_ptr_dtor_nogc(a);
+ ZVAL_COPY_VALUE(a, &ret);
+
+ return SUCCESS;
+}
+
+static int join_partial_objects(zval *a, zval *b)
+{
+ zval ret;
+
+ if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) {
+ return FAILURE;
+ }
+
+ empty_partial_object(&ret);
+ join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
+ zval_ptr_dtor_nogc(a);
+ ZVAL_COPY_VALUE(a, &ret);
+
+ return SUCCESS;
+}
+
+static void join_phi_values(zval *a, zval *b, bool escape) {
+ if (IS_BOT(a) || IS_TOP(b)) {
+ return;
+ }
+ if (IS_TOP(a)) {
+ zval_ptr_dtor_nogc(a);
+ ZVAL_COPY(a, b);
+ return;
+ }
+ if (IS_BOT(b)) {
+ zval_ptr_dtor_nogc(a);
+ MAKE_BOT(a);
+ return;
+ }
+ if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) {
+ if (join_partial_arrays(a, b) != SUCCESS) {
+ zval_ptr_dtor_nogc(a);
+ MAKE_BOT(a);
+ }
+ } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) {
+ if (escape || join_partial_objects(a, b) != SUCCESS) {
+ zval_ptr_dtor_nogc(a);
+ MAKE_BOT(a);
+ }
+ } else if (!zend_is_identical(a, b)) {
+ if (join_partial_arrays(a, b) != SUCCESS) {
+ zval_ptr_dtor_nogc(a);
+ MAKE_BOT(a);
+ }
+ }
+}
+
+static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
+ sccp_ctx *ctx = (sccp_ctx *) scdf;
+ zend_ssa *ssa = scdf->ssa;
+ ZEND_ASSERT(phi->ssa_var >= 0);
+ if (!IS_BOT(&ctx->values[phi->ssa_var])) {
+ zend_basic_block *block = &ssa->cfg.blocks[phi->block];
+ int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
+
+ int i;
+ zval result;
+ MAKE_TOP(&result);
+#if SCP_DEBUG
+ fprintf(stderr, "Handling phi(");
+#endif
+ if (phi->pi >= 0) {
+ ZEND_ASSERT(phi->sources[0] >= 0);
+ if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
+ join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
+ }
+ } else {
+ for (i = 0; i < block->predecessors_count; i++) {
+ ZEND_ASSERT(phi->sources[i] >= 0);
+ if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
+#if SCP_DEBUG
+ scp_dump_value(&ctx->values[phi->sources[i]]);
+ fprintf(stderr, ",");
+#endif
+ join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
+ } else {
+#if SCP_DEBUG
+ fprintf(stderr, " --,");
+#endif
+ }
+ }
+ }
+#if SCP_DEBUG
+ fprintf(stderr, ")\n");
+#endif
+
+ set_value(scdf, ctx, phi->ssa_var, &result);
+ zval_ptr_dtor_nogc(&result);
+ }
+}
+
+static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
+ zend_ssa *ssa = ctx->scdf.ssa;
+ zend_ssa_var_info *info = &ssa->var_info[var_num];
+
+ if (info->type & MAY_BE_UNDEF) {
+ return NULL;
+ }
+
+ if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
+ ZVAL_NULL(tmp);
+ return tmp;
+ }
+ if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
+ ZVAL_FALSE(tmp);
+ return tmp;
+ }
+ if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
+ ZVAL_TRUE(tmp);
+ return tmp;
+ }
+
+ if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
+ && info->has_range
+ && !info->range.overflow && !info->range.underflow
+ && info->range.min == info->range.max) {
+ ZVAL_LONG(tmp, info->range.min);
+ return tmp;
+ }
+
+ return NULL;
+}
+
+/* Call instruction -> remove opcodes that are part of the call */
+static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
+{
+ zend_ssa *ssa = ctx->scdf.ssa;
+ zend_op_array *op_array = ctx->scdf.op_array;
+ zend_call_info *call;
+ int i;
+
+ ZEND_ASSERT(ctx->call_map);
+ call = ctx->call_map[opline - op_array->opcodes];
+ ZEND_ASSERT(call);
+ ZEND_ASSERT(call->caller_call_opline == opline);
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ zend_ssa_remove_instr(ssa, call->caller_init_opline,
+ &ssa->ops[call->caller_init_opline - op_array->opcodes]);
+
+ for (i = 0; i < call->num_args; i++) {
+ zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
+ &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
+ }
+
+ // TODO: remove call_info completely???
+ call->callee_func = NULL;
+
+ return call->num_args + 2;
+}
+
+/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
+ * value(s) were determined by SCCP. It removes dead computational instructions and converts
+ * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
+ * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
+ * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
+ * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
+ * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
+ * by SCCP, because they may have potentially side-effecting types, but the actual values are
+ * not. As such doing DCE here will allow us to eliminate more dead code in combination.
+ * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
+ * we need to collect.
+ * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
+ */
+static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
+{
+ zend_ssa *ssa = ctx->scdf.ssa;
+ zend_op_array *op_array = ctx->scdf.op_array;
+ int removed_ops = 0;
+
+ if (var->definition >= 0) {
+ zend_op *opline = &op_array->opcodes[var->definition];
+ zend_ssa_op *ssa_op = &ssa->ops[var->definition];
+
+ if (opline->opcode == ZEND_ASSIGN) {
+ /* Leave assigns to DCE (due to dtor effects) */
+ return 0;
+ }
+
+ 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 variables */
+ return 0;
+ } else if (opline->opcode == ZEND_JMPZ_EX
+ || opline->opcode == ZEND_JMPNZ_EX
+ || opline->opcode == ZEND_JMP_SET
+ || opline->opcode == ZEND_COALESCE
+ || opline->opcode == ZEND_JMP_NULL
+ || opline->opcode == ZEND_FE_RESET_R
+ || opline->opcode == ZEND_FE_RESET_RW
+ || opline->opcode == ZEND_FE_FETCH_R
+ || opline->opcode == ZEND_FE_FETCH_RW
+ || opline->opcode == ZEND_NEW) {
+ /* we cannot simple remove jump instructions */
+ return 0;
+ } else if (var->use_chain >= 0
+ || var->phi_use_chain != NULL) {
+ if (value
+ && (opline->result_type & (IS_VAR|IS_TMP_VAR))
+ && opline->opcode != ZEND_QM_ASSIGN
+ && 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_UNPACK) {
+ /* Replace with QM_ASSIGN */
+ zend_uchar old_type = opline->result_type;
+ uint32_t old_var = opline->result.var;
+
+ ssa_op->result_def = -1;
+ if (opline->opcode == ZEND_DO_ICALL) {
+ removed_ops = remove_call(ctx, opline, ssa_op) - 1;
+ } else {
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ }
+ ssa_op->result_def = var_num;
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->result_type = old_type;
+ opline->result.var = old_var;
+ Z_TRY_ADDREF_P(value);
+ zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, value);
+ }
+ return 0;
+ } else {
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ if (opline->opcode == ZEND_DO_ICALL) {
+ removed_ops = remove_call(ctx, opline, ssa_op);
+ } else if (opline->opcode == ZEND_TYPE_CHECK
+ && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
+ && !value_known(&ctx->values[ssa_op->op1_use])) {
+ /* For TYPE_CHECK we may compute the result value without knowing the
+ * operand, based on type inference information. Make sure the operand is
+ * freed and leave further cleanup to DCE. */
+ opline->opcode = ZEND_FREE;
+ opline->result_type = IS_UNUSED;
+ removed_ops++;
+ } else {
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ removed_ops++;
+ }
+ }
+ } else if (ssa_op->op1_def == var_num) {
+ /* Compound assign or incdec -> convert to direct ASSIGN */
+
+ if (!value) {
+ /* In some cases zend_may_throw() may be avoided */
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ 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;
+ }
+ break;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) {
+ return 0;
+ }
+ break;
+ default:
+ if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
+ return 0;
+ }
+ break;
+ }
+ }
+
+ /* Mark result unused, if possible */
+ 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) {
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ opline->result_type = IS_UNUSED;
+ } else if (opline->opcode != ZEND_PRE_INC &&
+ opline->opcode != ZEND_PRE_DEC) {
+ /* op1_def and result_def are different */
+ return removed_ops;
+ }
+ }
+
+ /* Destroy previous op2 */
+ if (opline->op2_type == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ } else if (ssa_op->op2_use >= 0) {
+ if (ssa_op->op2_use != ssa_op->op1_use) {
+ zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
+ }
+ ssa_op->op2_use = -1;
+ ssa_op->op2_use_chain = -1;
+ }
+
+ /* Remove OP_DATA opcode */
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ removed_ops++;
+ zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
+ break;
+ 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;
+ }
+
+ if (value) {
+ /* Convert to ASSIGN */
+ opline->opcode = ZEND_ASSIGN;
+ opline->op2_type = IS_CONST;
+ opline->op2.constant = zend_optimizer_add_literal(op_array, value);
+ Z_TRY_ADDREF_P(value);
+ } else {
+ /* Remove dead array or object construction */
+ removed_ops++;
+ if (var->use_chain >= 0 || var->phi_use_chain != NULL) {
+ zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
+ }
+ zend_ssa_remove_op1_def(ssa, ssa_op);
+ zend_ssa_remove_instr(ssa, opline, ssa_op);
+ }
+ }
+ } else if (var->definition_phi
+ && var->use_chain < 0
+ && var->phi_use_chain == NULL) {
+ zend_ssa_remove_phi(ssa, var->definition_phi);
+ }
+ return removed_ops;
+}
+
+/* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
+ * can be replaced, because some instructions don't accept constant operands or only accept them
+ * if they have a certain type. */
+static int replace_constant_operands(sccp_ctx *ctx) {
+ zend_ssa *ssa = ctx->scdf.ssa;
+ zend_op_array *op_array = ctx->scdf.op_array;
+ int i;
+ zval tmp;
+ int removed_ops = 0;
+
+ /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
+ * and INIT_ARRAY. */
+ for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
+ zend_ssa_var *var = &ssa->vars[i];
+ zval *value;
+ int use;
+
+ if (IS_PARTIAL_ARRAY(&ctx->values[i])
+ || IS_PARTIAL_OBJECT(&ctx->values[i])) {
+ if (!Z_DELREF(ctx->values[i])) {
+ zend_array_destroy(Z_ARR(ctx->values[i]));
+ }
+ MAKE_BOT(&ctx->values[i]);
+ if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) {
+ removed_ops += try_remove_definition(ctx, i, var, NULL);
+ }
+ continue;
+ } else if (value_known(&ctx->values[i])) {
+ value = &ctx->values[i];
+ } else {
+ value = value_from_type_and_range(ctx, i, &tmp);
+ if (!value) {
+ continue;
+ }
+ }
+
+ FOREACH_USE(var, use) {
+ zend_op *opline = &op_array->opcodes[use];
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+ if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
+ if (opline->opcode == ZEND_NOP) {
+ removed_ops++;
+ }
+ ZEND_ASSERT(ssa_op->op1_def == -1);
+ if (ssa_op->op1_use != ssa_op->op2_use) {
+ zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
+ } else {
+ ssa_op->op2_use_chain = ssa_op->op1_use_chain;
+ }
+ ssa_op->op1_use = -1;
+ ssa_op->op1_use_chain = -1;
+ }
+ if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
+ ZEND_ASSERT(ssa_op->op2_def == -1);
+ if (ssa_op->op2_use != ssa_op->op1_use) {
+ zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
+ }
+ ssa_op->op2_use = -1;
+ ssa_op->op2_use_chain = -1;
+ }
+ } FOREACH_USE_END();
+
+ if (value_known(&ctx->values[i])) {
+ removed_ops += try_remove_definition(ctx, i, var, value);
+ }
+ }
+
+ return removed_ops;
+}
+
+static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
+ zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
+ int i;
+ sccp->call_map = call_map;
+ sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
+
+ MAKE_TOP(&sccp->top);
+ MAKE_BOT(&sccp->bot);
+
+ i = 0;
+ for (; i < op_array->last_var; ++i) {
+ /* These are all undefined variables, which we have to mark BOT.
+ * Otherwise the undefined variable warning might not be preserved. */
+ MAKE_BOT(&sccp->values[i]);
+ }
+ for (; i < ssa->vars_count; ++i) {
+ if (ssa->vars[i].alias) {
+ MAKE_BOT(&sccp->values[i]);
+ } else {
+ MAKE_TOP(&sccp->values[i]);
+ }
+ }
+}
+
+static void sccp_context_free(sccp_ctx *sccp) {
+ int i;
+ for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
+ zval_ptr_dtor_nogc(&sccp->values[i]);
+ }
+}
+
+int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
+{
+ sccp_ctx sccp;
+ int removed_ops = 0;
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+
+ sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
+
+ sccp.scdf.handlers.visit_instr = sccp_visit_instr;
+ sccp.scdf.handlers.visit_phi = sccp_visit_phi;
+ sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
+
+ scdf_init(ctx, &sccp.scdf, op_array, ssa);
+ scdf_solve(&sccp.scdf, "SCCP");
+
+ if (ctx->debug_level & ZEND_DUMP_SCCP) {
+ int i, first = 1;
+
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ zval *zv = &sccp.values[i];
+
+ if (IS_TOP(zv) || IS_BOT(zv)) {
+ continue;
+ }
+ if (first) {
+ first = 0;
+ fprintf(stderr, "\nSCCP Values for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\":\n");
+ }
+ fprintf(stderr, " #%d.", i);
+ zend_dump_var(op_array, IS_CV, ssa->vars[i].var);
+ fprintf(stderr, " =");
+ scp_dump_value(zv);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
+ removed_ops += replace_constant_operands(&sccp);
+
+ sccp_context_free(&sccp);
+ zend_arena_release(&ctx->arena, checkpoint);
+
+ return removed_ops;
+}
diff --git a/Zend/Optimizer/scdf.c b/Zend/Optimizer/scdf.c
new file mode 100644
index 0000000000..e414081987
--- /dev/null
+++ b/Zend/Optimizer/scdf.c
@@ -0,0 +1,229 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Sparse Conditional Data Flow Propagation Framework |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "Optimizer/zend_optimizer_internal.h"
+#include "Optimizer/scdf.h"
+
+/* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is
+ * based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a
+ * generalized implementation as described in chapter 8.3 of the SSA book.
+ *
+ * Every SSA variable is associated with an element on a finite-height lattice, those value can only
+ * ever be lowered during the operation of the algorithm. If a value is lowered all instructions and
+ * phis using that value need to be reconsidered (this is done by adding the variable to a
+ * worklist). For phi functions the result is computed by applying the meet operation to the
+ * operands. This continues until a fixed point is reached.
+ *
+ * The algorithm is control-flow sensitive: All blocks except the start block are initially assumed
+ * to be unreachable. When considering a branch instruction, we determine the feasible successors
+ * based on the current state of the variable lattice. If a new edge becomes feasible we either have
+ * to mark the successor block executable and consider all instructions in it, or, if the target is
+ * already executable, we only have to reconsider the phi functions (as we only consider phi
+ * operands which are associated with a feasible edge).
+ *
+ * The generic framework requires the definition of three functions:
+ * * visit_instr() should recompute the lattice values of all SSA variables defined by an
+ * instruction.
+ * * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While
+ * doing this it should only consider operands for which scfg_is_edge_feasible() returns true.
+ * * get_feasible_successors() should determine the feasible successors for a branch instruction.
+ * Note that this callback only needs to handle conditional branches (with two successors).
+ */
+
+#if 0
+#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DEBUG_PRINT(...)
+#endif
+
+void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
+ uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
+
+ if (zend_bitset_in(scdf->feasible_edges, edge)) {
+ /* We already handled this edge */
+ return;
+ }
+
+ DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
+ zend_bitset_incl(scdf->feasible_edges, edge);
+
+ if (!zend_bitset_in(scdf->executable_blocks, to)) {
+ if (!zend_bitset_in(scdf->block_worklist, to)) {
+ DEBUG_PRINT("Adding block %d to worklist\n", to);
+ }
+ zend_bitset_incl(scdf->block_worklist, to);
+ } else {
+ /* Block is already executable, only a new edge became feasible.
+ * Reevaluate phi nodes to account for changed source operands. */
+ zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
+ zend_ssa_phi *phi;
+ for (phi = ssa_block->phis; phi; phi = phi->next) {
+ zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
+ scdf->handlers.visit_phi(scdf, phi);
+ }
+ }
+}
+
+void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) {
+ scdf->op_array = op_array;
+ scdf->ssa = ssa;
+
+ scdf->instr_worklist_len = zend_bitset_len(op_array->last);
+ scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count);
+ scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count);
+
+ scdf->instr_worklist = zend_arena_calloc(&ctx->arena,
+ scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count),
+ sizeof(zend_ulong));
+
+ scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len;
+ scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len;
+ scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len;
+ scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len;
+
+ zend_bitset_incl(scdf->block_worklist, 0);
+ zend_bitset_incl(scdf->executable_blocks, 0);
+}
+
+void scdf_solve(scdf_ctx *scdf, const char *name) {
+ zend_ssa *ssa = scdf->ssa;
+ DEBUG_PRINT("Start SCDF solve (%s)\n", name);
+ while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
+ || !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
+ || !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
+ ) {
+ int i;
+ while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
+ zend_ssa_phi *phi = ssa->vars[i].definition_phi;
+ ZEND_ASSERT(phi);
+ if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
+ scdf->handlers.visit_phi(scdf, phi);
+ }
+ }
+
+ while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
+ int block_num = ssa->cfg.map[i];
+ if (zend_bitset_in(scdf->executable_blocks, block_num)) {
+ zend_basic_block *block = &ssa->cfg.blocks[block_num];
+ zend_op *opline = &scdf->op_array->opcodes[i];
+ zend_ssa_op *ssa_op = &ssa->ops[i];
+ if (opline->opcode == ZEND_OP_DATA) {
+ opline--;
+ ssa_op--;
+ }
+ scdf->handlers.visit_instr(scdf, opline, ssa_op);
+ if (i == block->start + block->len - 1) {
+ if (block->successors_count == 1) {
+ scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
+ } else if (block->successors_count > 1) {
+ scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
+ }
+ }
+ }
+ }
+
+ while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
+ /* This block is now live. Interpret phis and instructions in it. */
+ zend_basic_block *block = &ssa->cfg.blocks[i];
+ zend_ssa_block *ssa_block = &ssa->blocks[i];
+
+ DEBUG_PRINT("Pop block %d from worklist\n", i);
+ zend_bitset_incl(scdf->executable_blocks, i);
+
+ {
+ zend_ssa_phi *phi;
+ for (phi = ssa_block->phis; phi; phi = phi->next) {
+ zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
+ scdf->handlers.visit_phi(scdf, phi);
+ }
+ }
+
+ if (block->len == 0) {
+ /* 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 = NULL;
+ int j, end = block->start + block->len;
+ for (j = block->start; j < end; j++) {
+ opline = &scdf->op_array->opcodes[j];
+ zend_bitset_excl(scdf->instr_worklist, j);
+ if (opline->opcode != ZEND_OP_DATA) {
+ scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
+ }
+ }
+ 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--;
+ }
+ scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
+ }
+ }
+ }
+ }
+}
+
+/* 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 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;
+ 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;
+}
+
+/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
+ * blocks, as well as remove them from the successor / predecessor lists and mark them
+ * unreachable. Blocks already marked unreachable are not removed. */
+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_loop_var_free(scdf, i)) {
+ removed_ops += ssa->cfg.blocks[i].len;
+ zend_ssa_remove_block(scdf->op_array, ssa, i);
+ }
+ }
+ return removed_ops;
+}
diff --git a/Zend/Optimizer/scdf.h b/Zend/Optimizer/scdf.h
new file mode 100644
index 0000000000..1ab1cec3bd
--- /dev/null
+++ b/Zend/Optimizer/scdf.h
@@ -0,0 +1,99 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef _SCDF_H
+#define _SCDF_H
+
+#include "zend_bitset.h"
+
+typedef struct _scdf_ctx {
+ zend_op_array *op_array;
+ zend_ssa *ssa;
+ zend_bitset instr_worklist;
+ /* Represent phi-instructions through the defining var */
+ zend_bitset phi_var_worklist;
+ zend_bitset block_worklist;
+ zend_bitset executable_blocks;
+ /* 1 bit per edge, see scdf_edge(cfg, from, to) */
+ zend_bitset feasible_edges;
+ uint32_t instr_worklist_len;
+ uint32_t phi_var_worklist_len;
+ uint32_t block_worklist_len;
+
+ struct {
+ void (*visit_instr)(
+ struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
+ void (*visit_phi)(
+ struct _scdf_ctx *scdf, zend_ssa_phi *phi);
+ void (*mark_feasible_successors)(
+ struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
+ zend_op *opline, zend_ssa_op *ssa_op);
+ } handlers;
+} scdf_ctx;
+
+void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa);
+void scdf_solve(scdf_ctx *scdf, const char *name);
+
+int scdf_remove_unreachable_blocks(scdf_ctx *scdf);
+
+/* Add uses to worklist */
+static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
+ zend_ssa *ssa = scdf->ssa;
+ zend_ssa_var *var = &ssa->vars[var_num];
+ int use;
+ zend_ssa_phi *phi;
+ FOREACH_USE(var, use) {
+ zend_bitset_incl(scdf->instr_worklist, use);
+ } FOREACH_USE_END();
+ FOREACH_PHI_USE(var, phi) {
+ zend_bitset_incl(scdf->phi_var_worklist, phi->ssa_var);
+ } FOREACH_PHI_USE_END();
+}
+
+/* This should usually not be necessary, however it's used for type narrowing. */
+static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
+ zend_ssa_var *var = &scdf->ssa->vars[var_num];
+ if (var->definition >= 0) {
+ zend_bitset_incl(scdf->instr_worklist, var->definition);
+ } else if (var->definition_phi) {
+ zend_bitset_incl(scdf->phi_var_worklist, var_num);
+ }
+}
+
+static inline uint32_t scdf_edge(zend_cfg *cfg, int from, int to) {
+ zend_basic_block *to_block = cfg->blocks + to;
+ int i;
+
+ for (i = 0; i < to_block->predecessors_count; i++) {
+ uint32_t edge = to_block->predecessor_offset + i;
+
+ if (cfg->predecessors[edge] == from) {
+ return edge;
+ }
+ }
+ ZEND_UNREACHABLE();
+}
+
+static inline bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to) {
+ uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
+ return zend_bitset_in(scdf->feasible_edges, edge);
+}
+
+void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to);
+
+#endif
diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c
new file mode 100644
index 0000000000..4bd7705816
--- /dev/null
+++ b/Zend/Optimizer/ssa_integrity.c
@@ -0,0 +1,378 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA validation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Nikita Popov <nikic@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "Optimizer/zend_optimizer_internal.h"
+
+/* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and
+ * CFG are upheld and prints messages to stderr if this is not the case. */
+
+static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
+ int use;
+ FOREACH_USE(&ssa->vars[var], use) {
+ if (use == check) {
+ return 1;
+ }
+ } FOREACH_USE_END();
+ return 0;
+}
+
+static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
+ zend_ssa_phi *phi;
+ FOREACH_PHI_USE(&ssa->vars[var], phi) {
+ if (phi == check) {
+ return 1;
+ }
+ } FOREACH_PHI_USE_END();
+ return 0;
+}
+
+static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) {
+ zend_ssa_op *ssa_op = &ssa->ops[op];
+ return (ssa_op->op1_use == check)
+ || (ssa_op->op2_use == check)
+ || (ssa_op->result_use == check);
+}
+
+static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) {
+ zend_ssa_op *ssa_op = &ssa->ops[op];
+ return (ssa_op->op1_def == check)
+ || (ssa_op->op2_def == check)
+ || (ssa_op->result_def == check);
+}
+
+static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) {
+ int source;
+ FOREACH_PHI_SOURCE(phi, source) {
+ if (source == check) {
+ return 1;
+ }
+ } FOREACH_PHI_SOURCE_END();
+ return 0;
+}
+
+static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
+ int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
+ for (i = 0; i < block->predecessors_count; i++) {
+ if (predecessors[i] == check) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static inline bool is_in_successors(zend_basic_block *block, int check) {
+ int s;
+ for (s = 0; s < block->successors_count; s++) {
+ if (block->successors[s] == check) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static inline bool is_var_type(zend_uchar type) {
+ return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0;
+}
+
+#define FAIL(...) do { \
+ if (status == SUCCESS) { \
+ fprintf(stderr, "\nIn function %s::%s (%s):\n", \
+ op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \
+ op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \
+ } \
+ fprintf(stderr, __VA_ARGS__); \
+ status = FAILURE; \
+} while (0)
+
+#define VARFMT "%d (%s%s)"
+#define VAR(i) \
+ (i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \
+ (ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "")
+
+#define INSTRFMT "%d (%s)"
+#define INSTR(i) \
+ (i), (zend_get_opcode_name(op_array->opcodes[i].opcode))
+
+int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) {
+ zend_cfg *cfg = &ssa->cfg;
+ zend_ssa_phi *phi;
+ int i, status = SUCCESS;
+
+ /* Vars */
+ for (i = 0; i < ssa->vars_count; i++) {
+ zend_ssa_var *var = &ssa->vars[i];
+ int use, c;
+ uint32_t type = ssa->var_info[i].type;
+
+ if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
+ if (var->use_chain >= 0) {
+ FAIL("var " VARFMT " without def has op uses\n", VAR(i));
+ }
+ if (var->phi_use_chain) {
+ FAIL("var " VARFMT " without def has phi uses\n", VAR(i));
+ }
+ }
+ if (var->definition >= 0 && var->definition_phi) {
+ FAIL("var " VARFMT " has both def and def_phi\n", VAR(i));
+ }
+ if (var->definition >= 0) {
+ if (!is_defined_by_op(ssa, var->definition, i)) {
+ FAIL("var " VARFMT " not defined by op " INSTRFMT "\n",
+ VAR(i), INSTR(var->definition));
+ }
+ }
+ if (var->definition_phi) {
+ if (var->definition_phi->ssa_var != i) {
+ FAIL("var " VARFMT " not defined by given phi\n", VAR(i));
+ }
+ }
+
+ c = 0;
+ FOREACH_USE(var, use) {
+ if (++c > 10000) {
+ FAIL("cycle in uses of " VARFMT "\n", VAR(i));
+ return status;
+ }
+ if (!is_used_by_op(ssa, use, i)) {
+ fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use);
+ }
+ } FOREACH_USE_END();
+
+ c = 0;
+ FOREACH_PHI_USE(var, phi) {
+ if (++c > 10000) {
+ FAIL("cycle in phi uses of " VARFMT "\n", VAR(i));
+ return status;
+ }
+ if (!is_in_phi_sources(ssa, phi, i)) {
+ FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var);
+ }
+ } FOREACH_PHI_USE_END();
+
+ if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
+ FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
+ }
+ if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
+ FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
+ }
+ }
+
+ /* Instructions */
+ FOREACH_INSTR_NUM(i) {
+ zend_ssa_op *ssa_op = &ssa->ops[i];
+ zend_op *opline = &op_array->opcodes[i];
+ if (is_var_type(opline->op1_type)) {
+ if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) {
+ FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
+ }
+ } else {
+ if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) {
+ FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
+ }
+ }
+ if (is_var_type(opline->op2_type)) {
+ if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) {
+ FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
+ }
+ } else {
+ if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) {
+ FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
+ }
+ }
+ if (is_var_type(opline->result_type)) {
+ if (ssa_op->result_use < 0 && ssa_op->result_def < 0) {
+ FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
+ }
+ } else {
+ if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) {
+ FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
+ }
+ }
+
+ if (ssa_op->op1_use >= 0) {
+ if (ssa_op->op1_use >= ssa->vars_count) {
+ FAIL("op1 use %d out of range\n", ssa_op->op1_use);
+ }
+ if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) {
+ FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n",
+ VAR(ssa_op->op1_use), INSTR(i));
+ }
+ if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) {
+ FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i));
+ }
+ }
+ if (ssa_op->op2_use >= 0) {
+ if (ssa_op->op2_use >= ssa->vars_count) {
+ FAIL("op2 use %d out of range\n", ssa_op->op2_use);
+ }
+ if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) {
+ FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n",
+ VAR(ssa_op->op2_use), INSTR(i));
+ }
+ if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) {
+ FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i));
+ }
+ }
+ if (ssa_op->result_use >= 0) {
+ if (ssa_op->result_use >= ssa->vars_count) {
+ FAIL("result use %d out of range\n", ssa_op->result_use);
+ }
+ if (!is_in_use_chain(ssa, ssa_op->result_use, i)) {
+ FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n",
+ VAR(ssa_op->result_use), INSTR(i));
+ }
+ if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) {
+ FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i));
+ }
+ }
+ if (ssa_op->op1_def >= 0) {
+ if (ssa_op->op1_def >= ssa->vars_count) {
+ FAIL("op1 def %d out of range\n", ssa_op->op1_def);
+ }
+ if (ssa->vars[ssa_op->op1_def].definition != i) {
+ FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n",
+ VAR(ssa_op->op1_def), INSTR(i));
+ }
+ if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) {
+ FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i));
+ }
+ }
+ if (ssa_op->op2_def >= 0) {
+ if (ssa_op->op2_def >= ssa->vars_count) {
+ FAIL("op2 def %d out of range\n", ssa_op->op2_def);
+ }
+ if (ssa->vars[ssa_op->op2_def].definition != i) {
+ FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n",
+ VAR(ssa_op->op2_def), INSTR(i));
+ }
+ if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) {
+ FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i));
+ }
+ }
+ if (ssa_op->result_def >= 0) {
+ if (ssa_op->result_def >= ssa->vars_count) {
+ FAIL("result def %d out of range\n", ssa_op->result_def);
+ }
+ if (ssa->vars[ssa_op->result_def].definition != i) {
+ FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n",
+ VAR(ssa_op->result_def), INSTR(i));
+ }
+ if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) {
+ FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n",
+ VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i));
+ }
+ }
+ } FOREACH_INSTR_NUM_END();
+
+ /* Phis */
+ FOREACH_PHI(phi) {
+ unsigned num_sources = NUM_PHI_SOURCES(phi);
+ for (i = 0; i < num_sources; i++) {
+ int source = phi->sources[i];
+ if (source < 0) {
+ FAIL(VARFMT " negative source\n", VAR(phi->ssa_var));
+ }
+ if (!is_in_phi_use_chain(ssa, source, phi)) {
+ FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source);
+ }
+ if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) {
+ FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var));
+ }
+ if (phi->use_chains[i]) {
+ int j;
+ for (j = i + 1; j < num_sources; j++) {
+ if (phi->sources[j] == source && phi->use_chains[j]) {
+ FAIL("use chain for source " VARFMT " of phi " VARFMT
+ " at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j);
+ }
+ }
+ }
+ }
+ if (ssa->vars[phi->ssa_var].definition_phi != phi) {
+ FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var));
+ }
+ } FOREACH_PHI_END();
+
+ /* Blocks */
+ for (i = 0; i < cfg->blocks_count; i++) {
+ zend_basic_block *block = &cfg->blocks[i];
+ int *predecessors = &cfg->predecessors[block->predecessor_offset];
+ int s, j;
+
+ if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
+ FAIL("Block %d start %d smaller previous end %d\n",
+ i, block->start, (block-1)->start + (block-1)->len);
+ }
+ if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) {
+ FAIL("Block %d end %d greater next start %d\n",
+ i, block->start + block->len, (block+1)->start);
+ }
+
+ for (j = block->start; j < block->start + block->len; j++) {
+ if (cfg->map[j] != i) {
+ FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i);
+ }
+ }
+
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
+ if (ssa->blocks[i].phis) {
+ FAIL("Unreachable block %d has phis\n", i);
+ }
+ continue;
+ }
+
+ for (s = 0; s < block->successors_count; s++) {
+ zend_basic_block *next_block;
+ if (block->successors[s] < 0) {
+ FAIL("Successor number %d of %d negative", s, i);
+ }
+ next_block = &cfg->blocks[block->successors[s]];
+ if (!(next_block->flags & ZEND_BB_REACHABLE)) {
+ FAIL("Successor %d of %d not reachable\n", block->successors[s], i);
+ }
+ if (!is_in_predecessors(cfg, next_block, i)) {
+ FAIL("Block %d predecessors missing %d\n", block->successors[s], i);
+ }
+ }
+
+ for (j = 0; j < block->predecessors_count; j++) {
+ if (predecessors[j] >= 0) {
+ int k;
+ zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
+ if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
+ FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
+ }
+ if (!is_in_successors(prev_block, i)) {
+ FAIL("Block %d successors missing %d\n", predecessors[j], i);
+ }
+ for (k = 0; k < block->predecessors_count; k++) {
+ if (k != j && predecessors[k] == predecessors[j]) {
+ FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
+ }
+ }
+ }
+ }
+ }
+
+ return status;
+}
diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c
new file mode 100644
index 0000000000..b0f3247cd7
--- /dev/null
+++ b/Zend/Optimizer/zend_call_graph.c
@@ -0,0 +1,271 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "Optimizer/zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+
+static void zend_op_array_calc(zend_op_array *op_array, void *context)
+{
+ zend_call_graph *call_graph = context;
+ call_graph->op_arrays_count++;
+}
+
+static void zend_op_array_collect(zend_op_array *op_array, void *context)
+{
+ zend_call_graph *call_graph = context;
+ zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
+
+ ZEND_SET_FUNC_INFO(op_array, func_info);
+ call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
+ func_info->num = call_graph->op_arrays_count;
+ call_graph->op_arrays_count++;
+}
+
+ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
+{
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+ zend_function *func;
+ zend_call_info *call_info;
+ int call = 0;
+ zend_call_info **call_stack;
+ ALLOCA_FLAG(use_heap);
+ bool is_prototype;
+
+ call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
+ call_info = NULL;
+ while (opline != end) {
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ call_stack[call] = call_info;
+ func = zend_optimizer_get_called_func(
+ script, op_array, opline, &is_prototype);
+ /* TODO: Support prototypes? */
+ if (func && !is_prototype) {
+ call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
+ call_info->caller_op_array = op_array;
+ call_info->caller_init_opline = opline;
+ call_info->caller_call_opline = NULL;
+ call_info->callee_func = func;
+ call_info->num_args = opline->extended_value;
+ call_info->next_callee = func_info->callee_info;
+ func_info->callee_info = call_info;
+
+ if (build_flags & ZEND_CALL_TREE) {
+ call_info->next_caller = NULL;
+ } else if (func->type == ZEND_INTERNAL_FUNCTION) {
+ call_info->next_caller = NULL;
+ } else {
+ zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
+ if (callee_func_info) {
+ call_info->next_caller = callee_func_info->caller_info;
+ callee_func_info->caller_info = call_info;
+ } else {
+ call_info->next_caller = NULL;
+ }
+ }
+ } else {
+ call_info = NULL;
+ }
+ call++;
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_NEW:
+ case ZEND_INIT_USER_CALL:
+ call_stack[call] = call_info;
+ call_info = NULL;
+ call++;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ func_info->flags |= ZEND_FUNC_HAS_CALLS;
+ if (call_info) {
+ call_info->caller_call_opline = opline;
+ }
+ call--;
+ call_info = call_stack[call];
+ break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_USER:
+ if (call_info) {
+ if (opline->op2_type == IS_CONST) {
+ call_info->named_args = 1;
+ break;
+ }
+
+ uint32_t num = opline->op2.num;
+ if (num > 0) {
+ num--;
+ }
+ call_info->arg_info[num].opline = opline;
+ }
+ break;
+ case ZEND_SEND_ARRAY:
+ case ZEND_SEND_UNPACK:
+ if (call_info) {
+ call_info->send_unpack = 1;
+ }
+ break;
+ case ZEND_EXIT:
+ /* In this case the DO_CALL opcode may have been dropped
+ * and caller_call_opline will be NULL. */
+ break;
+ }
+ opline++;
+ }
+ free_alloca(call_stack, use_heap);
+ return SUCCESS;
+}
+
+static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
+{
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int ret = 0;
+
+ if (op_array == root) {
+ return 1;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (zend_bitset_in(visited, func_info->num)) {
+ return 0;
+ }
+ zend_bitset_incl(visited, func_info->num);
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ ret = 1;
+ }
+ call_info = call_info->next_caller;
+ }
+ return ret;
+}
+
+static void zend_analyze_recursion(zend_call_graph *call_graph)
+{
+ zend_op_array *op_array;
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int i;
+ int set_len = zend_bitset_len(call_graph->op_arrays_count);
+ zend_bitset visited;
+ ALLOCA_FLAG(use_heap);
+
+ visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ op_array = call_graph->op_arrays[i];
+ func_info = call_graph->func_infos + i;
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (call_info->caller_op_array == op_array) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
+ } else {
+ memset(visited, 0, sizeof(zend_ulong) * set_len);
+ if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
+ }
+ }
+ call_info = call_info->next_caller;
+ }
+ }
+
+ free_alloca(visited, use_heap);
+}
+
+static void zend_sort_op_arrays(zend_call_graph *call_graph)
+{
+ (void) call_graph;
+
+ // TODO: perform topological sort of cyclic call graph
+}
+
+ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
+{
+ call_graph->op_arrays_count = 0;
+ zend_foreach_op_array(script, zend_op_array_calc, call_graph);
+
+ call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
+ call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
+ call_graph->op_arrays_count = 0;
+ zend_foreach_op_array(script, zend_op_array_collect, call_graph);
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i);
+ }
+ zend_analyze_recursion(call_graph);
+ zend_sort_op_arrays(call_graph);
+}
+/* }}} */
+
+ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */
+{
+ zend_call_info **map, *call;
+ if (!info->callee_info) {
+ /* Don't build call map if function contains no calls */
+ return NULL;
+ }
+
+ map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
+ for (call = info->callee_info; call; call = call->next_callee) {
+ int i;
+ map[call->caller_init_opline - op_array->opcodes] = call;
+ if (call->caller_call_opline) {
+ map[call->caller_call_opline - op_array->opcodes] = call;
+ }
+ for (i = 0; i < call->num_args; i++) {
+ if (call->arg_info[i].opline) {
+ map[call->arg_info[i].opline - op_array->opcodes] = call;
+ }
+ }
+ }
+ return map;
+}
+/* }}} */
diff --git a/Zend/Optimizer/zend_call_graph.h b/Zend/Optimizer/zend_call_graph.h
new file mode 100644
index 0000000000..a456dcfbb8
--- /dev/null
+++ b/Zend/Optimizer/zend_call_graph.h
@@ -0,0 +1,69 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CALL_GRAPH_H
+#define ZEND_CALL_GRAPH_H
+
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_optimizer.h"
+
+typedef struct _zend_send_arg_info {
+ zend_op *opline;
+} zend_send_arg_info;
+
+struct _zend_call_info {
+ zend_op_array *caller_op_array;
+ zend_op *caller_init_opline;
+ zend_op *caller_call_opline;
+ zend_function *callee_func;
+ zend_call_info *next_caller;
+ zend_call_info *next_callee;
+ bool recursive;
+ bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
+ bool named_args; /* Function has named arguments */
+ int num_args;
+ zend_send_arg_info arg_info[1];
+};
+
+struct _zend_func_info {
+ int num;
+ uint32_t flags;
+ zend_ssa ssa; /* Static Single Assignmnt Form */
+ zend_call_info *caller_info; /* where this function is called from */
+ zend_call_info *callee_info; /* which functions are called from this one */
+ zend_call_info **call_map; /* Call info associated with init/call/send opnum */
+ zend_ssa_var_info return_info;
+};
+
+typedef struct _zend_call_graph {
+ int op_arrays_count;
+ zend_op_array **op_arrays;
+ zend_func_info *func_infos;
+} zend_call_graph;
+
+BEGIN_EXTERN_C()
+
+ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
+ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
+ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array);
+ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CALL_GRAPH_H */
diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c
new file mode 100644
index 0000000000..973a93ef6c
--- /dev/null
+++ b/Zend/Optimizer/zend_cfg.c
@@ -0,0 +1,909 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_worklist.h"
+#include "zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+
+static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+
+ while (1) {
+ int i;
+
+ b->flags |= ZEND_BB_REACHABLE;
+ if (b->successors_count == 0) {
+ b->flags |= ZEND_BB_EXIT;
+ return;
+ }
+
+ for (i = 0; i < b->successors_count; i++) {
+ zend_basic_block *succ = blocks + b->successors[i];
+
+ if (b->len != 0) {
+ zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
+ if (b->successors_count == 1) {
+ if (opcode == ZEND_JMP) {
+ succ->flags |= ZEND_BB_TARGET;
+ } else {
+ succ->flags |= ZEND_BB_FOLLOW;
+
+ if ((cfg->flags & ZEND_CFG_STACKLESS)) {
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
+ opcode == ZEND_GENERATOR_CREATE ||
+ opcode == ZEND_YIELD ||
+ opcode == ZEND_YIELD_FROM ||
+ opcode == ZEND_DO_FCALL ||
+ opcode == ZEND_DO_UCALL ||
+ opcode == ZEND_DO_FCALL_BY_NAME) {
+ succ->flags |= ZEND_BB_ENTRY;
+ }
+ }
+ if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
+ if (opcode == ZEND_RECV ||
+ opcode == ZEND_RECV_INIT) {
+ succ->flags |= ZEND_BB_RECV_ENTRY;
+ }
+ }
+ }
+ } else if (b->successors_count == 2) {
+ if (i == 0 || opcode == ZEND_JMPZNZ) {
+ succ->flags |= ZEND_BB_TARGET;
+ } else {
+ succ->flags |= ZEND_BB_FOLLOW;
+ }
+ } else {
+ ZEND_ASSERT(
+ opcode == ZEND_SWITCH_LONG
+ || opcode == ZEND_SWITCH_STRING
+ || opcode == ZEND_MATCH
+ );
+ if (i == b->successors_count - 1) {
+ succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
+ } else {
+ succ->flags |= ZEND_BB_TARGET;
+ }
+ }
+ } else {
+ succ->flags |= ZEND_BB_FOLLOW;
+ }
+
+ if (i == b->successors_count - 1) {
+ /* Tail call optimization */
+ if (succ->flags & ZEND_BB_REACHABLE) {
+ return;
+ }
+
+ b = succ;
+ break;
+ } else {
+ /* Recursively check reachability */
+ if (!(succ->flags & ZEND_BB_REACHABLE)) {
+ zend_mark_reachable(opcodes, cfg, succ);
+ }
+ }
+ }
+ }
+}
+/* }}} */
+
+static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+
+ blocks[start].flags = ZEND_BB_START;
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
+
+ if (op_array->last_try_catch) {
+ zend_basic_block *b;
+ int j, changed;
+ uint32_t *block_map = cfg->map;
+
+ do {
+ changed = 0;
+
+ /* Add exception paths */
+ for (j = 0; j < op_array->last_try_catch; j++) {
+
+ /* check for jumps into the middle of try block */
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ zend_basic_block *end;
+
+ if (op_array->try_catch_array[j].catch_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = b->start;
+ break;
+ }
+ b++;
+ }
+ }
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ if (op_array->try_catch_array[j].finally_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
+ break;
+ }
+ b++;
+ }
+ }
+ }
+ }
+
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->flags |= ZEND_BB_TRY;
+ if (op_array->try_catch_array[j].catch_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ b->flags |= ZEND_BB_CATCH;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ b->flags |= ZEND_BB_FINALLY;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
+ b->flags |= ZEND_BB_FINALLY_END;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, cfg, b);
+ }
+ }
+ } else {
+ if (op_array->try_catch_array[j].catch_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
+ }
+ }
+ }
+ } 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;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+/* }}} */
+
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int i;
+ int start = 0;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ if (blocks[i].flags & ZEND_BB_REACHABLE) {
+ start = i;
+ i++;
+ break;
+ }
+ }
+
+ /* clear all flags */
+ for (i = 0; i < cfg->blocks_count; i++) {
+ blocks[i].flags = 0;
+ }
+
+ zend_mark_reachable_blocks(op_array, cfg, start);
+}
+/* }}} */
+
+static void initialize_block(zend_basic_block *block) {
+ block->flags = 0;
+ block->successors = block->successors_storage;
+ block->successors_count = 0;
+ block->predecessors_count = 0;
+ block->predecessor_offset = -1;
+ block->idom = -1;
+ block->loop_header = -1;
+ block->level = -1;
+ block->children = -1;
+ block->next_child = -1;
+}
+
+#define BB_START(i) do { \
+ if (!block_map[i]) { blocks_count++;} \
+ block_map[i]++; \
+ } while (0)
+
+ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
+{
+ uint32_t flags = 0;
+ uint32_t i;
+ int j;
+ uint32_t *block_map;
+ zend_function *fn;
+ int blocks_count = 0;
+ zend_basic_block *blocks;
+ zval *zv;
+ bool extra_entry_block = 0;
+
+ 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));
+
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
+ BB_START(0);
+ for (i = 0; i < op_array->last; i++) {
+ zend_op *opline = op_array->opcodes + i;
+ switch (opline->opcode) {
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ if (build_flags & ZEND_CFG_RECV_ENTRY) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_MATCH_ERROR:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_THROW:
+ /* Don't treat THROW as terminator if it's used in expression context,
+ * as we may lose live ranges when eliminating unreachable code. */
+ if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_INCLUDE_OR_EVAL:
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ case ZEND_GENERATOR_CREATE:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_ICALL:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ break;
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ zv = CRT_CONSTANT(opline->op2);
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+ /* The third literal is the lowercased unqualified name */
+ zv += 2;
+ }
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
+ flags |= zend_optimizer_classify_function(
+ Z_STR_P(zv), opline->extended_value);
+ }
+ }
+ break;
+ case ZEND_FAST_CALL:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_FAST_RET:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMP:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZNZ:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ }
+ BB_START(i + 1);
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ BB_START(i + 1);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
+ zval *zv;
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
+ } ZEND_HASH_FOREACH_END();
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ BB_START(i + 1);
+ 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)) &&
+ !op_array->function_name) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ }
+ break;
+ case ZEND_FUNC_GET_ARGS:
+ flags |= ZEND_FUNC_VARARG;
+ break;
+ 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_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;
+ }
+ }
+
+ /* If the entry block has predecessors, we may need to split it */
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
+ && op_array->last > 0 && block_map[0] > 1) {
+ extra_entry_block = 1;
+ }
+
+ 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);
+ if (op_array->try_catch_array[j].catch_op) {
+ BB_START(op_array->try_catch_array[j].catch_op);
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ BB_START(op_array->try_catch_array[j].finally_op);
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ BB_START(op_array->try_catch_array[j].finally_end);
+ }
+ }
+ }
+
+ blocks_count += extra_entry_block;
+ cfg->blocks_count = blocks_count;
+
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
+
+ blocks_count = -1;
+
+ if (extra_entry_block) {
+ initialize_block(&blocks[0]);
+ blocks[0].start = 0;
+ blocks[0].len = 0;
+ blocks_count++;
+ }
+
+ for (i = 0; i < op_array->last; i++) {
+ if (block_map[i]) {
+ if (blocks_count >= 0) {
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ }
+ blocks_count++;
+ initialize_block(&blocks[blocks_count]);
+ blocks[blocks_count].start = i;
+ }
+ block_map[i] = blocks_count;
+ }
+
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ blocks_count++;
+
+ /* Build CFG, Step 3: Calculate successors */
+ for (j = 0; j < blocks_count; j++) {
+ zend_basic_block *block = &blocks[j];
+ zend_op *opline;
+ if (block->len == 0) {
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
+ continue;
+ }
+
+ opline = op_array->opcodes + block->start + block->len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_RET:
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ case ZEND_MATCH_ERROR:
+ break;
+ case ZEND_JMP:
+ block->successors_count = 1;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
+ break;
+ case ZEND_JMPZNZ:
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = j + 1;
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = j + 1;
+ } else {
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ block->successors_count = 2;
+ block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
+ block->successors[1] = j + 1;
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
+ block->successors[1] = j + 1;
+ break;
+ case ZEND_FAST_CALL:
+ block->successors_count = 2;
+ block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
+ block->successors[1] = j + 1;
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
+ zval *zv;
+ uint32_t s = 0;
+
+ block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable);
+ block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
+
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
+ } ZEND_HASH_FOREACH_END();
+
+ block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
+ if (opline->opcode != ZEND_MATCH) {
+ block->successors[s++] = j + 1;
+ }
+ break;
+ }
+ default:
+ block->successors_count = 1;
+ block->successors[0] = j + 1;
+ break;
+ }
+ }
+
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
+ cfg->flags |= flags;
+ zend_mark_reachable_blocks(op_array, cfg, 0);
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
+{
+ int j, s, edges;
+ zend_basic_block *b;
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ int *predecessors;
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ b->predecessors_count = 0;
+ }
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ b->successors_count = 0;
+ b->predecessors_count = 0;
+ } else {
+ for (s = 0; s < b->successors_count; s++) {
+ edges++;
+ blocks[b->successors[s]].predecessors_count++;
+ }
+ }
+ }
+
+ cfg->edges_count = edges;
+ cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->predecessor_offset = edges;
+ edges += b->predecessors_count;
+ b->predecessors_count = 0;
+ }
+ }
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ if (blocks[j].flags & ZEND_BB_REACHABLE) {
+ /* SWITCH_STRING/LONG may have few identical successors */
+ for (s = 0; s < blocks[j].successors_count; s++) {
+ int duplicate = 0;
+ int p;
+
+ for (p = 0; p < s; p++) {
+ if (blocks[j].successors[p] == blocks[j].successors[s]) {
+ duplicate = 1;
+ break;
+ }
+ }
+ if (!duplicate) {
+ zend_basic_block *b = blocks + blocks[j].successors[s];
+
+ predecessors[b->predecessor_offset + b->predecessors_count] = j;
+ b->predecessors_count++;
+ }
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* Computes a postorder numbering of the CFG */
+static void compute_postnum_recursive(
+ int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
+{
+ int s;
+ zend_basic_block *block = &cfg->blocks[block_num];
+ if (postnum[block_num] != -1) {
+ return;
+ }
+
+ postnum[block_num] = -2; /* Marker for "currently visiting" */
+ for (s = 0; s < block->successors_count; s++) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
+ }
+ postnum[block_num] = (*cur)++;
+}
+/* }}} */
+
+/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
+ * Cooper, Harvey and Kennedy. */
+ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ int j, k, changed;
+
+ ALLOCA_FLAG(use_heap)
+ int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
+ memset(postnum, -1, sizeof(int) * cfg->blocks_count);
+ j = 0;
+ compute_postnum_recursive(postnum, &j, cfg, 0);
+
+ /* FIXME: move declarations */
+ blocks[0].idom = 0;
+ do {
+ changed = 0;
+ /* Iterating in RPO here would converge faster */
+ for (j = 1; j < blocks_count; j++) {
+ int idom = -1;
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
+
+ if (idom < 0) {
+ if (blocks[pred].idom >= 0)
+ idom = pred;
+ continue;
+ }
+
+ if (blocks[pred].idom >= 0) {
+ while (idom != pred) {
+ while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
+ while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
+ }
+ }
+ }
+
+ if (idom >= 0 && blocks[j].idom != idom) {
+ blocks[j].idom = idom;
+ changed = 1;
+ }
+ }
+ } while (changed);
+ blocks[0].idom = -1;
+
+ for (j = 1; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].idom >= 0) {
+ /* Sort by block number to traverse children in pre-order */
+ if (blocks[blocks[j].idom].children < 0 ||
+ j < blocks[blocks[j].idom].children) {
+ blocks[j].next_child = blocks[blocks[j].idom].children;
+ blocks[blocks[j].idom].children = j;
+ } else {
+ int k = blocks[blocks[j].idom].children;
+ while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
+ k = blocks[k].next_child;
+ }
+ blocks[j].next_child = blocks[k].next_child;
+ blocks[k].next_child = j;
+ }
+ }
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ int idom = blocks[j].idom, level = 0;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ while (idom >= 0) {
+ level++;
+ if (blocks[idom].level >= 0) {
+ level += blocks[idom].level;
+ break;
+ } else {
+ idom = blocks[idom].idom;
+ }
+ }
+ blocks[j].level = level;
+ }
+
+ free_alloca(postnum, use_heap);
+ return SUCCESS;
+}
+/* }}} */
+
+static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
+{
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+/* }}} */
+
+typedef struct {
+ int id;
+ int level;
+} block_info;
+static int compare_block_level(const block_info *a, const block_info *b) {
+ return b->level - a->level;
+}
+static void swap_blocks(block_info *a, block_info *b) {
+ block_info tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ int i, j, k, n;
+ int time;
+ zend_basic_block *blocks = cfg->blocks;
+ int *entry_times, *exit_times;
+ zend_worklist work;
+ int flag = ZEND_FUNC_NO_LOOPS;
+ block_info *sorted_blocks;
+ ALLOCA_FLAG(list_use_heap)
+ ALLOCA_FLAG(tree_use_heap)
+ ALLOCA_FLAG(sorted_blocks_use_heap)
+
+ ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
+
+ /* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
+ * queries. These are implemented by checking entry/exit times of the DFS search. */
+ entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
+ exit_times = entry_times + cfg->blocks_count;
+ memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
+
+ zend_worklist_push(&work, 0);
+ time = 0;
+ while (zend_worklist_len(&work)) {
+ next:
+ i = zend_worklist_peek(&work);
+ if (entry_times[i] == -1) {
+ entry_times[i] = time++;
+ }
+ /* Visit blocks immediately dominated by i. */
+ for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
+ if (zend_worklist_push(&work, j)) {
+ goto next;
+ }
+ }
+ /* Visit join edges. */
+ for (j = 0; j < blocks[i].successors_count; j++) {
+ int succ = blocks[i].successors[j];
+ if (blocks[succ].idom == i) {
+ continue;
+ } else if (zend_worklist_push(&work, succ)) {
+ goto next;
+ }
+ }
+ exit_times[i] = time++;
+ zend_worklist_pop(&work);
+ }
+
+ /* Sort blocks by decreasing level, which is the order in which we want to process them */
+ sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
+ for (i = 0; i < cfg->blocks_count; i++) {
+ sorted_blocks[i].id = i;
+ sorted_blocks[i].level = blocks[i].level;
+ }
+ zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
+ (compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
+
+ /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
+ Graphs". */
+
+ for (n = 0; n < cfg->blocks_count; n++) {
+ i = sorted_blocks[n].id;
+
+ zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
+ for (j = 0; j < blocks[i].predecessors_count; j++) {
+ int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
+
+ /* A join edge is one for which the predecessor does not
+ immediately dominate the successor. */
+ if (blocks[i].idom == pred) {
+ continue;
+ }
+
+ /* In a loop back-edge (back-join edge), the successor dominates
+ the predecessor. */
+ if (dominates(blocks, i, pred)) {
+ blocks[i].flags |= ZEND_BB_LOOP_HEADER;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ zend_worklist_push(&work, pred);
+ } else {
+ /* Otherwise it's a cross-join edge. See if it's a branch
+ to an ancestor on the DJ spanning tree. */
+ if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
+ blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
+ flag |= ZEND_FUNC_IRREDUCIBLE;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ }
+ }
+ }
+ while (zend_worklist_len(&work)) {
+ j = zend_worklist_pop(&work);
+ while (blocks[j].loop_header >= 0) {
+ j = blocks[j].loop_header;
+ }
+ if (j != i) {
+ blocks[j].loop_header = i;
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
+ }
+ }
+ }
+ }
+
+ free_alloca(sorted_blocks, sorted_blocks_use_heap);
+ free_alloca(entry_times, tree_use_heap);
+ ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
+
+ cfg->flags |= flag;
+
+ return SUCCESS;
+}
+/* }}} */
diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h
new file mode 100644
index 0000000000..6fff720ed3
--- /dev/null
+++ b/Zend/Optimizer/zend_cfg.h
@@ -0,0 +1,127 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CFG_H
+#define ZEND_CFG_H
+
+/* zend_basic_block.flags */
+#define ZEND_BB_START (1<<0) /* first block */
+#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
+#define ZEND_BB_TARGET (1<<2) /* jump target */
+#define ZEND_BB_EXIT (1<<3) /* without successors */
+#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
+#define ZEND_BB_TRY (1<<5) /* start of try block */
+#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_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 (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_UNREACHABLE_FREE)
+
+typedef struct _zend_basic_block {
+ int *successors; /* successor block indices */
+ uint32_t flags;
+ uint32_t start; /* first opcode number */
+ uint32_t len; /* number of opcodes */
+ int successors_count; /* number of successors */
+ int predecessors_count; /* number of predecessors */
+ int predecessor_offset; /* offset of 1-st predecessor */
+ int idom; /* immediate dominator block */
+ int loop_header; /* closest loop header, or -1 */
+ int level; /* steps away from the entry in the dom. tree */
+ int children; /* list of dominated blocks */
+ int next_child; /* next dominated block */
+ int successors_storage[2]; /* up to 2 successor blocks */
+} zend_basic_block;
+
+/*
++------------+---+---+---+---+---+
+| |OP1|OP2|EXT| 0 | 1 |
++------------+---+---+---+---+---+
+|JMP |ADR| | |OP1| - |
+|JMPZ | |ADR| |OP2|FOL|
+|JMPNZ | |ADR| |OP2|FOL|
+|JMPZNZ | |ADR|ADR|OP2|EXT|
+|JMPZ_EX | |ADR| |OP2|FOL|
+|JMPNZ_EX | |ADR| |OP2|FOL|
+|JMP_SET | |ADR| |OP2|FOL|
+|COALESCE | |ADR| |OP2|FOL|
+|ASSERT_CHK | |ADR| |OP2|FOL|
+|NEW | |ADR| |OP2|FOL|
+|DCL_ANON* |ADR| | |OP1|FOL|
+|FE_RESET_* | |ADR| |OP2|FOL|
+|FE_FETCH_* | | |ADR|EXT|FOL|
+|CATCH | | |ADR|EXT|FOL|
+|FAST_CALL |ADR| | |OP1|FOL|
+|FAST_RET | | | | - | - |
+|RETURN* | | | | - | - |
+|EXIT | | | | - | - |
+|THROW | | | | - | - |
+|* | | | |FOL| - |
++------------+---+---+---+---+---+
+*/
+
+typedef struct _zend_cfg {
+ int blocks_count; /* number of basic blocks */
+ int edges_count; /* number of edges */
+ zend_basic_block *blocks; /* array of basic blocks */
+ int *predecessors;
+ uint32_t *map;
+ uint32_t flags;
+} zend_cfg;
+
+/* Build Flags */
+#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_NO_ENTRY_PREDECESSORS (1<<25)
+#define ZEND_CFG_RECV_ENTRY (1<<24)
+#define ZEND_CALL_TREE (1<<23)
+#define ZEND_SSA_USE_CV_RESULTS (1<<22)
+
+#define CRT_CONSTANT_EX(op_array, opline, node) \
+ (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \
+ RT_CONSTANT(opline, (node)) \
+ : \
+ CT_CONSTANT_EX(op_array, (node).constant) \
+ )
+
+#define CRT_CONSTANT(node) \
+ CRT_CONSTANT_EX(op_array, opline, node)
+
+#define RETURN_VALUE_USED(opline) \
+ ((opline)->result_type != IS_UNUSED)
+
+BEGIN_EXTERN_C()
+
+ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
+ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
+ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
+ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CFG_H */
diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c
new file mode 100644
index 0000000000..b059f1f0ea
--- /dev/null
+++ b/Zend/Optimizer/zend_dfg.c
@@ -0,0 +1,333 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+
+static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
+{
+ uint32_t var_num;
+ const zend_op *next;
+
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ }
+ if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
+ && opline->opcode != ZEND_FE_FETCH_R
+ && opline->opcode != ZEND_FE_FETCH_RW)
+ || (opline->op2_type == IS_CV)) {
+ var_num = EX_VAR_TO_NUM(opline->op2.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ }
+ if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
+ && opline->result_type == IS_CV
+ && opline->opcode != ZEND_RECV) {
+ var_num = EX_VAR_TO_NUM(opline->result.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ }
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ if (opline->op1_type == IS_CV) {
+add_op1_def:
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+ if (opline->op2_type == IS_CV) {
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
+ zend_bitset_incl(def, var_num);
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_OBJ_REF:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ if (next->op1_type == IS_CV) {
+ zend_bitset_incl(def, var_num);
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
+ zend_bitset_incl(def, var_num);
+ }
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP_REF:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ if (next->op1_type == IS_CV) {
+ zend_bitset_incl(def, var_num);
+ }
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP_OP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIM_OP:
+ case ZEND_ASSIGN_OBJ_OP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_OP:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ case ZEND_FE_RESET_RW:
+ case ZEND_MAKE_REF:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ case ZEND_CAST:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_FE_RESET_R:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ADD_ARRAY_UNPACK:
+ var_num = EX_VAR_TO_NUM(opline->result.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ var_num = EX_VAR_TO_NUM(opline->result.var);
+ if (!zend_bitset_in(def, var_num)) {
+ zend_bitset_incl(use, var_num);
+ }
+ /* break missing intentionally */
+ case ZEND_INIT_ARRAY:
+ if (((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
+ && opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_YIELD:
+ if (opline->op1_type == IS_CV
+ && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ || (build_flags & ZEND_SSA_RC_INFERENCE))) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_UNSET_CV:
+ goto add_op1_def;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+#if 0
+ /* This special case was handled above the switch */
+ if (opline->op2_type != IS_CV) {
+ op2_use = -1; /* not used */
+ }
+#endif
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
+ break;
+ case ZEND_BIND_LEXICAL:
+ if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
+ }
+}
+/* }}} */
+
+ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
+{
+ _zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
+}
+/* }}} */
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
+{
+ int set_size;
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ zend_bitset tmp, def, use, in, out;
+ int k;
+ int j;
+
+ set_size = dfg->size;
+ tmp = dfg->tmp;
+ def = dfg->def;
+ use = dfg->use;
+ in = dfg->in;
+ out = dfg->out;
+
+ /* Collect "def" and "use" sets */
+ for (j = 0; j < blocks_count; j++) {
+ zend_op *opline, *end;
+ zend_bitset b_use, b_def;
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + blocks[j].start;
+ end = opline + blocks[j].len;
+ b_use = DFG_BITSET(use, set_size, j);
+ b_def = DFG_BITSET(def, set_size, j);
+ for (; opline < end; opline++) {
+ if (opline->opcode != ZEND_OP_DATA) {
+ _zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
+ }
+ }
+ }
+
+ /* Calculate "in" and "out" sets */
+ {
+ uint32_t worklist_len = zend_bitset_len(blocks_count);
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+ worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
+ memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset_incl(worklist, j);
+ }
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ /* We use the last block on the worklist, because predecessors tend to be located
+ * before the succeeding block, so this converges faster. */
+ j = zend_bitset_last(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].successors_count != 0) {
+ zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
+ for (k = 1; k < blocks[j].successors_count; k++) {
+ zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
+ }
+ } else {
+ zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
+ }
+ zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
+ if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
+ zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
+
+ /* Add predecessors of changed block to worklist */
+ {
+ int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_bitset_incl(worklist, predecessors[k]);
+ }
+ }
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
diff --git a/Zend/Optimizer/zend_dfg.h b/Zend/Optimizer/zend_dfg.h
new file mode 100644
index 0000000000..6ec92be307
--- /dev/null
+++ b/Zend/Optimizer/zend_dfg.h
@@ -0,0 +1,51 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DFG_H
+#define ZEND_DFG_H
+
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_dfg {
+ int vars;
+ uint32_t size;
+ zend_bitset tmp;
+ zend_bitset def;
+ zend_bitset use;
+ zend_bitset in;
+ zend_bitset out;
+} zend_dfg;
+
+#define DFG_BITSET(set, set_size, block_num) \
+ ((set) + ((block_num) * (set_size)))
+
+#define DFG_SET(set, set_size, block_num, var_num) \
+ zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
+
+#define DFG_ISSET(set, set_size, block_num, var_num) \
+ zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
+
+BEGIN_EXTERN_C()
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
+ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DFG_H */
diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c
new file mode 100644
index 0000000000..e2fdbbcbf7
--- /dev/null
+++ b/Zend/Optimizer/zend_dump.c
@@ -0,0 +1,1239 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_dump.h"
+
+void zend_dump_ht(HashTable *ht)
+{
+ zend_ulong index;
+ zend_string *key;
+ zval *val;
+ int first = 1;
+
+ ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, val) {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ if (key) {
+ fprintf(stderr, "\"%s\"", ZSTR_VAL(key));
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT, index);
+ }
+ fprintf(stderr, " =>");
+ zend_dump_const(val);
+ } ZEND_HASH_FOREACH_END();
+}
+
+void zend_dump_const(const zval *zv)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_NULL:
+ fprintf(stderr, " null");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " bool(false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " bool(true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv));
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
+ break;
+ case IS_STRING:
+ fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv));
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " array(...)");
+ break;
+ default:
+ fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
+ break;
+ }
+}
+
+static void zend_dump_class_fetch_type(uint32_t fetch_type)
+{
+ switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ fprintf(stderr, " (self)");
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ fprintf(stderr, " (parent)");
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ fprintf(stderr, " (static)");
+ break;
+ case ZEND_FETCH_CLASS_AUTO:
+ fprintf(stderr, " (auto)");
+ break;
+ case ZEND_FETCH_CLASS_INTERFACE:
+ fprintf(stderr, " (interface)");
+ break;
+ case ZEND_FETCH_CLASS_TRAIT:
+ fprintf(stderr, " (trait)");
+ break;
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
+ fprintf(stderr, " (no-autolod)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_SILENT) {
+ fprintf(stderr, " (silent)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) {
+ fprintf(stderr, " (exception)");
+ }
+}
+
+static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) {
+ if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " %u", op.num);
+ } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num != (uint32_t)-1) {
+ fprintf(stderr, " try-catch(%u)", op.num);
+ }
+ } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " THIS");
+ } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " NEXT");
+ } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ zend_dump_class_fetch_type(op.num);
+ } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " CONSTRUCTOR");
+ } else if (ZEND_VM_OP_CONST_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
+ fprintf(stderr, " (unqualified-in-namespace)");
+ }
+ }
+}
+
+ZEND_API void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num)
+{
+ if (var_type == IS_CV && var_num < op_array->last_var) {
+ fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val);
+ } else if (var_type == IS_VAR) {
+ fprintf(stderr, "V%d", var_num);
+ } else if ((var_type & (IS_VAR|IS_TMP_VAR)) == IS_TMP_VAR) {
+ fprintf(stderr, "T%d", var_num);
+ } else {
+ fprintf(stderr, "X%d", var_num);
+ }
+}
+
+static void zend_dump_range(const zend_ssa_range *r)
+{
+ if (r->underflow && r->overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE[");
+ if (r->underflow) {
+ fprintf(stderr, "--..");
+ } else if (r->min == ZEND_LONG_MIN) {
+ fprintf(stderr, "MIN..");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "..", r->min);
+ }
+ if (r->overflow) {
+ fprintf(stderr, "++]");
+ } else if (r->max == ZEND_LONG_MAX) {
+ fprintf(stderr, "MAX]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->max);
+ }
+}
+
+static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
+{
+ int first = 1;
+
+ fprintf(stderr, " [");
+ if (info & MAY_BE_GUARD) {
+ fprintf(stderr, "!");
+ }
+ if (info & MAY_BE_UNDEF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "undef");
+ }
+ if (info & MAY_BE_REF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
+ if (info & MAY_BE_RC1) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rc1");
+ }
+ if (info & MAY_BE_RCN) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rcn");
+ }
+ }
+ if (info & MAY_BE_CLASS) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "class");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ } else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_NULL) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "bool");
+ } else if (info & MAY_BE_FALSE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ } else if (info & MAY_BE_TRUE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_LONG) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_DOUBLE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_STRING) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
+ ((info & MAY_BE_ARRAY_KEY_LONG) == 0 ||
+ (info & MAY_BE_ARRAY_KEY_STRING) == 0)) {
+ int afirst = 1;
+ fprintf(stderr, " [");
+ if (info & MAY_BE_ARRAY_KEY_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_KEY_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ fprintf(stderr, "]");
+ }
+ if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
+ int afirst = 1;
+ fprintf(stderr, " of [");
+ if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_ARRAY_OF_NULL) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if (info & MAY_BE_ARRAY_OF_FALSE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ }
+ if (info & MAY_BE_ARRAY_OF_TRUE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_ARRAY_OF_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_OF_DOUBLE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_ARRAY_OF_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY_OF_ARRAY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ }
+ if (info & MAY_BE_ARRAY_OF_OBJECT) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ }
+ if (info & MAY_BE_ARRAY_OF_RESOURCE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ if (info & MAY_BE_ARRAY_OF_REF) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ fprintf(stderr, "]");
+ }
+ }
+ if (info & MAY_BE_OBJECT) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ }
+ if (info & MAY_BE_RESOURCE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ fprintf(stderr, "]");
+}
+
+static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags)
+{
+ zend_dump_type_info(
+ ssa->var_info[ssa_var_num].type,
+ ssa->var_info[ssa_var_num].ce,
+ ssa->var_info[ssa_var_num].ce ?
+ ssa->var_info[ssa_var_num].is_instanceof : 0,
+ dump_flags);
+}
+
+ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
+{
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, "#%d.", ssa_var_num);
+ } else {
+ fprintf(stderr, "#?.");
+ }
+ zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
+
+ if (ssa_var_num >= 0 && ssa->vars) {
+ if (ssa->vars[ssa_var_num].no_val) {
+ fprintf(stderr, " NOVAL");
+ }
+ if (ssa->vars[ssa_var_num].escape_state == ESCAPE_STATE_NO_ESCAPE) {
+ fprintf(stderr, " NOESC");
+ }
+ if (ssa->var_info) {
+ zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags);
+ if (ssa->var_info[ssa_var_num].has_range) {
+ zend_dump_range(&ssa->var_info[ssa_var_num].range);
+ }
+ }
+ }
+}
+
+static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags)
+{
+ fprintf(stderr, " TYPE");
+ zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags);
+}
+
+static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags)
+{
+ if (r->range.underflow && r->range.overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE");
+ if (r->negative) {
+ fprintf(stderr, "~");
+ }
+ fprintf(stderr, "[");
+ if (r->range.underflow) {
+ fprintf(stderr, "-- .. ");
+ } else {
+ if (r->min_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var, dump_flags);
+ if (r->range.min > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min);
+ } else if (r->range.min < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min);
+ }
+ fprintf(stderr, " .. ");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min);
+ }
+ }
+ if (r->range.overflow) {
+ fprintf(stderr, "++]");
+ } else {
+ if (r->max_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var, dump_flags);
+ if (r->range.max > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max);
+ } else if (r->range.max < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max);
+ }
+ fprintf(stderr, "]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->range.max);
+ }
+ }
+}
+
+ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op)
+{
+ const char *name = zend_get_opcode_name(opline->opcode);
+ uint32_t flags = zend_get_opcode_flags(opline->opcode);
+ uint32_t n = 0;
+
+ if (!ssa_op || ssa_op->result_use < 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa_op && ssa_op->result_def >= 0) {
+ int ssa_var_num = ssa_op->result_def;
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ } else {
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ fprintf(stderr, " = ");
+ }
+ }
+
+ if (name) {
+ fprintf(stderr, "%s", (name + 5));
+ } else {
+ fprintf(stderr, "OP_%d", (int)opline->opcode);
+ }
+
+ if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
+ fprintf(stderr, " %u", opline->extended_value);
+ } 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:
+ fprintf(stderr, " (null)");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " (false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " (true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " (long)");
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " (double)");
+ break;
+ case IS_STRING:
+ fprintf(stderr, " (string)");
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " (array)");
+ break;
+ case IS_OBJECT:
+ fprintf(stderr, " (object)");
+ break;
+ case IS_RESOURCE:
+ fprintf(stderr, " (resource)");
+ break;
+ case _IS_BOOL:
+ fprintf(stderr, " (bool)");
+ break;
+ case IS_CALLABLE:
+ fprintf(stderr, " (callable)");
+ break;
+ case IS_VOID:
+ fprintf(stderr, " (void)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } else if (ZEND_VM_EXT_TYPE_MASK == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case (1<<IS_NULL):
+ fprintf(stderr, " (null)");
+ break;
+ case (1<<IS_FALSE):
+ fprintf(stderr, " (false)");
+ break;
+ case (1<<IS_TRUE):
+ fprintf(stderr, " (true)");
+ break;
+ case (1<<IS_LONG):
+ fprintf(stderr, " (long)");
+ break;
+ case (1<<IS_DOUBLE):
+ fprintf(stderr, " (double)");
+ break;
+ case (1<<IS_STRING):
+ fprintf(stderr, " (string)");
+ break;
+ case (1<<IS_ARRAY):
+ fprintf(stderr, " (array)");
+ break;
+ case (1<<IS_OBJECT):
+ fprintf(stderr, " (object)");
+ break;
+ case (1<<IS_RESOURCE):
+ fprintf(stderr, " (resource)");
+ break;
+ case ((1<<IS_FALSE)|(1<<IS_TRUE)):
+ fprintf(stderr, " (bool)");
+ break;
+ default:
+ fprintf(stderr, " TYPE");
+ zend_dump_type_info(opline->extended_value, NULL, 0, dump_flags);
+ break;
+ }
+ } else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case ZEND_EVAL:
+ fprintf(stderr, " (eval)");
+ break;
+ case ZEND_INCLUDE:
+ fprintf(stderr, " (include)");
+ break;
+ case ZEND_INCLUDE_ONCE:
+ fprintf(stderr, " (include_once)");
+ break;
+ case ZEND_REQUIRE:
+ fprintf(stderr, " (require)");
+ break;
+ case ZEND_REQUIRE_ONCE:
+ fprintf(stderr, " (require_once)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } 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) {
+ fprintf(stderr, " (function)");
+ }
+ } else {
+ if (ZEND_VM_EXT_VAR_FETCH & flags) {
+ if (opline->extended_value & ZEND_FETCH_GLOBAL) {
+ fprintf(stderr, " (global)");
+ } else if (opline->extended_value & ZEND_FETCH_LOCAL) {
+ fprintf(stderr, " (local)");
+ } else if (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK) {
+ fprintf(stderr, " (global+lock)");
+ }
+ }
+ if (ZEND_VM_EXT_ISSET & flags) {
+ if (!(opline->extended_value & ZEND_ISEMPTY)) {
+ fprintf(stderr, " (isset)");
+ } else {
+ fprintf(stderr, " (empty)");
+ }
+ }
+ if (ZEND_VM_EXT_ARRAY_INIT & flags) {
+ fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
+ if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
+ fprintf(stderr, " (packed)");
+ }
+ }
+ if (ZEND_VM_EXT_REF & flags) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ fprintf(stderr, " (ref)");
+ }
+ }
+ if ((ZEND_VM_EXT_DIM_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)");
+ }
+ }
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT(opline->op1));
+ } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->op1_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ } else if (ssa_op->op1_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->op1_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes));
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op1, op1_flags);
+ }
+ }
+
+ if (opline->op2_type == IS_CONST) {
+ zval *op = CRT_CONSTANT(opline->op2);
+ if (
+ opline->opcode == ZEND_SWITCH_LONG
+ || opline->opcode == ZEND_SWITCH_STRING
+ || opline->opcode == ZEND_MATCH
+ ) {
+ HashTable *jumptable = Z_ARRVAL_P(op);
+ zend_string *key;
+ zend_ulong num_key;
+ zval *zv;
+ ZEND_HASH_FOREACH_KEY_VAL(jumptable, num_key, key, zv) {
+ if (key) {
+ fprintf(stderr, " \"%s\":", ZSTR_VAL(key));
+ } else {
+ fprintf(stderr, " " ZEND_LONG_FMT ":", num_key);
+ }
+ if (b) {
+ fprintf(stderr, " BB%d,", b->successors[n++]);
+ } else {
+ fprintf(stderr, " %04u,", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
+ }
+ } ZEND_HASH_FOREACH_END();
+ fprintf(stderr, " default:");
+ } else {
+ zend_dump_const(op);
+ }
+ } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->op2_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ } else if (ssa_op->op2_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->op2_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) {
+ if (opline->opcode != ZEND_CATCH || !(opline->extended_value & ZEND_LAST_CATCH)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes));
+ }
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op2, op2_flags);
+ }
+ }
+
+ if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " %04u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ }
+ if (opline->result_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT(opline->result));
+#if 0
+ } else if (opline->result_type & IS_SMART_BRANCH_JMPZ) {
+ fprintf(stderr, " jmpz");
+ } else if (opline->result_type & IS_SMART_BRANCH_JMPNZ) {
+ fprintf(stderr, " jmpnz");
+#endif
+ } else if (ssa_op && ssa_op->result_use >= 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->result_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ if (ssa_op) {
+ int ssa_var_num = ssa_op->result_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ }
+ }
+ }
+}
+
+static void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
+{
+ int len = 0;
+ const zend_ssa *ssa = NULL;
+ zend_ssa_op *ssa_op = NULL;
+
+ len = fprintf(stderr, "%04u", (uint32_t)(opline - op_array->opcodes));
+ fprintf(stderr, "%*c", 5-len, ' ');
+
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ if (ssa && ssa->ops) {
+ ssa_op = &ssa->ops[opline - op_array->opcodes];
+ }
+ }
+
+ zend_dump_op(op_array, b, opline, dump_flags, ssa, ssa_op);
+ fprintf(stderr, "\n");
+}
+
+static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
+{
+ zend_basic_block *b = cfg->blocks + n;
+
+ if (n > 0) {
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "BB%d:\n ;", n);
+ if (b->flags & ZEND_BB_START) {
+ fprintf(stderr, " start");
+ }
+ if (b->flags & ZEND_BB_RECV_ENTRY) {
+ fprintf(stderr, " recv");
+ }
+ if (b->flags & ZEND_BB_FOLLOW) {
+ fprintf(stderr, " follow");
+ }
+ if (b->flags & ZEND_BB_TARGET) {
+ fprintf(stderr, " target");
+ }
+ if (b->flags & ZEND_BB_EXIT) {
+ fprintf(stderr, " exit");
+ }
+ if (b->flags & (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) {
+ fprintf(stderr, " entry");
+ }
+ if (b->flags & ZEND_BB_TRY) {
+ fprintf(stderr, " try");
+ }
+ if (b->flags & ZEND_BB_CATCH) {
+ fprintf(stderr, " catch");
+ }
+ if (b->flags & ZEND_BB_FINALLY) {
+ fprintf(stderr, " finally");
+ }
+ if (b->flags & ZEND_BB_FINALLY_END) {
+ fprintf(stderr, " finally_end");
+ }
+ 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");
+ }
+ if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ fprintf(stderr, " irreducible");
+ }
+ if (b->len != 0) {
+ fprintf(stderr, " lines=[%d-%d]", b->start, b->start + b->len - 1);
+ } else {
+ fprintf(stderr, " empty");
+ }
+ fprintf(stderr, "\n");
+
+ if (b->predecessors_count) {
+ int *p = cfg->predecessors + b->predecessor_offset;
+ int *end = p + b->predecessors_count;
+
+ fprintf(stderr, " ; from=(BB%d", *p);
+ for (p++; p < end; p++) {
+ fprintf(stderr, ", BB%d", *p);
+ }
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->successors_count > 0) {
+ int s;
+ fprintf(stderr, " ; to=(BB%d", b->successors[0]);
+ for (s = 1; s < b->successors_count; s++) {
+ fprintf(stderr, ", BB%d", b->successors[s]);
+ }
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->idom >= 0) {
+ fprintf(stderr, " ; idom=BB%d\n", b->idom);
+ }
+ if (b->level >= 0) {
+ fprintf(stderr, " ; level=%d\n", b->level);
+ }
+ if (b->loop_header >= 0) {
+ fprintf(stderr, " ; loop_header=%d\n", b->loop_header);
+ }
+ if (b->children >= 0) {
+ int j = b->children;
+ fprintf(stderr, " ; children=(BB%d", j);
+ j = cfg->blocks[j].next_child;
+ while (j >= 0) {
+ fprintf(stderr, ", BB%d", j);
+ j = cfg->blocks[j].next_child;
+ }
+ fprintf(stderr, ")\n");
+ }
+}
+
+static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags)
+{
+ zend_dump_block_info(cfg, n, dump_flags);
+ if (ssa && ssa->blocks && ssa->blocks[n].phis) {
+ zend_ssa_phi *p = ssa->blocks[n].phis;
+
+ do {
+ int j;
+
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags);
+ if (p->pi < 0) {
+ fprintf(stderr, " = Phi(");
+ for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
+ if (j > 0) {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ } else {
+ fprintf(stderr, " = Pi<BB%d>(", p->pi);
+ zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
+ fprintf(stderr, " &");
+ if (p->has_range_constraint) {
+ zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags);
+ } else {
+ zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ }
+ p = p->next;
+ } while (p);
+ }
+}
+
+void zend_dump_op_array_name(const zend_op_array *op_array)
+{
+ if (op_array->function_name) {
+ if (op_array->scope && op_array->scope->name) {
+ fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
+ } else {
+ fprintf(stderr, "%s", op_array->function_name->val);
+ }
+ } else {
+ fprintf(stderr, "%s", "$_main");
+ }
+}
+
+ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data)
+{
+ int i;
+ const zend_cfg *cfg = NULL;
+ const zend_ssa *ssa = NULL;
+ zend_func_info *func_info = NULL;
+ uint32_t func_flags = 0;
+
+ if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
+ cfg = (const zend_cfg*)data;
+ if (!cfg->blocks) {
+ cfg = data = NULL;
+ }
+ }
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ func_flags = func_info->flags;
+ }
+
+ fprintf(stderr, "\n");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, ":\n ; (lines=%d, args=%d",
+ op_array->last,
+ op_array->num_args);
+ fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T);
+ if (ssa) {
+ fprintf(stderr, ", ssa_vars=%d", ssa->vars_count);
+ }
+ if (func_flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
+ fprintf(stderr, ", dynamic");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE) {
+ fprintf(stderr, ", recursive");
+ if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) {
+ fprintf(stderr, " directly");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) {
+ fprintf(stderr, " indirectly");
+ }
+ }
+ if (func_flags & ZEND_FUNC_IRREDUCIBLE) {
+ fprintf(stderr, ", irreducable");
+ }
+ if (func_flags & ZEND_FUNC_NO_LOOPS) {
+ fprintf(stderr, ", no_loops");
+ }
+ 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
+ if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) {
+ fprintf(stderr, ", no_in_mem_cvs");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) {
+ fprintf(stderr, ", no_used_args");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) {
+ fprintf(stderr, ", no_symtab");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_FRAME) {
+ fprintf(stderr, ", no_frame");
+ }
+ if (info->flags & ZEND_JIT_FUNC_INLINE) {
+ fprintf(stderr, ", inline");
+ }
+#endif
+ fprintf(stderr, ")\n");
+ if (msg) {
+ fprintf(stderr, " ; (%s)\n", msg);
+ }
+ fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end);
+
+ if (func_info) {
+ fprintf(stderr, " ; return ");
+ zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags);
+ zend_dump_range(&func_info->return_info.range);
+ fprintf(stderr, "\n");
+ }
+
+ if (ssa && ssa->var_info) {
+ for (i = 0; i < op_array->last_var; i++) {
+ fprintf(stderr, " ; ");
+ zend_dump_ssa_var(op_array, ssa, i, IS_CV, i, dump_flags);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (cfg) {
+ int n;
+ zend_basic_block *b;
+
+ for (n = 0; n < cfg->blocks_count; n++) {
+ b = cfg->blocks + n;
+ if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
+ const zend_op *opline;
+ const zend_op *end;
+
+ zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
+ opline = op_array->opcodes + b->start;
+ end = opline + b->len;
+ while (opline < end) {
+ zend_dump_op_line(op_array, b, opline, dump_flags, data);
+ opline++;
+ }
+ }
+ }
+ 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: %04u - %04u ",
+ 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");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ case ZEND_LIVE_NEW:
+ fprintf(stderr, "(new)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr, " BB%u",
+ cfg->map[op_array->try_catch_array[i].try_op]);
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].catch_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].finally_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr, ", BB%u\n",
+ cfg->map[op_array->try_catch_array[i].finally_end]);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ } else {
+ const zend_op *opline = op_array->opcodes;
+ const zend_op *end = opline + op_array->last;
+
+ while (opline < end) {
+ zend_dump_op_line(op_array, NULL, opline, dump_flags, data);
+ opline++;
+ }
+ 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: %04u - %04u ",
+ 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");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ case ZEND_LIVE_NEW:
+ fprintf(stderr, "(new)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr,
+ " %04u",
+ op_array->try_catch_array[i].try_op);
+
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr,
+ ", %04u",
+ op_array->try_catch_array[i].catch_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr,
+ ", %04u",
+ op_array->try_catch_array[i].finally_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr,
+ ", %04u",
+ op_array->try_catch_array[i].finally_end);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ }
+}
+
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
+{
+ int j;
+
+ fprintf(stderr, "\nDOMINATORS-TREE for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < cfg->blocks_count; j++) {
+ zend_basic_block *b = cfg->blocks + j;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_dump_block_info(cfg, j, 0);
+ }
+ }
+}
+
+void zend_dump_variables(const zend_op_array *op_array)
+{
+ int j;
+
+ fprintf(stderr, "\nCV Variables for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < op_array->last_var; j++) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, IS_CV, j);
+ fprintf(stderr, "\n");
+ }
+}
+
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags)
+{
+ int j;
+
+ if (ssa->vars) {
+ fprintf(stderr, "\nSSA Variable for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags);
+ if (ssa->vars[j].scc >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ fprintf(stderr, " *");
+ } else {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "SCC=%d", ssa->vars[j].scc);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
+{
+ int first = 1;
+ uint32_t i;
+
+ fprintf(stderr, " ; %s = {", name);
+ for (i = 0; i < op_array->last_var + op_array->T; i++) {
+ if (zend_bitset_in(set, i)) {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, i);
+ }
+ }
+ fprintf(stderr, "}\n");
+}
+
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
+{
+ int j;
+ fprintf(stderr, "\nVariable Liveness for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ fprintf(stderr, " BB%d:\n", j);
+ zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
+ zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
+ zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j));
+ zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j));
+ }
+}
+
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
+{
+ int j;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+
+ fprintf(stderr, "\nSSA Phi() Placement for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < blocks_count; j++) {
+ if (ssa_blocks && ssa_blocks[j].phis) {
+ zend_ssa_phi *p = ssa_blocks[j].phis;
+ int first = 1;
+
+ fprintf(stderr, " BB%d:\n", j);
+ if (p->pi >= 0) {
+ fprintf(stderr, " ; pi={");
+ } else {
+ fprintf(stderr, " ; phi={");
+ }
+ do {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, p->var);
+ p = p->next;
+ } while (p);
+ fprintf(stderr, "}\n");
+ }
+ }
+}
diff --git a/Zend/Optimizer/zend_dump.h b/Zend/Optimizer/zend_dump.h
new file mode 100644
index 0000000000..b0e0d7966b
--- /dev/null
+++ b/Zend/Optimizer/zend_dump.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DUMP_H
+#define ZEND_DUMP_H
+
+#include "zend_ssa.h"
+#include "zend_dfg.h"
+
+#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
+#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)
+
+BEGIN_EXTERN_C()
+
+ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
+ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op);
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
+void zend_dump_variables(const zend_op_array *op_array);
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
+ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags);
+ZEND_API void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
+void zend_dump_op_array_name(const zend_op_array *op_array);
+void zend_dump_const(const zval *zv);
+void zend_dump_ht(HashTable *ht);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DUMP_H */
diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c
new file mode 100644
index 0000000000..533f9f8594
--- /dev/null
+++ b/Zend/Optimizer/zend_func_info.c
@@ -0,0 +1,973 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ | Xinchen Hui <laruence@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "zend_ssa.h"
+#include "zend_optimizer_internal.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+#ifdef _WIN32
+#include "win32/ioutil.h"
+#endif
+
+typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
+
+typedef struct _func_info_t {
+ const char *name;
+ int name_len;
+ uint32_t info;
+ info_func_t info_func;
+} func_info_t;
+
+#define F0(name, info) \
+ {name, sizeof(name)-1, (info), NULL}
+#define F1(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
+#define FN(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
+#define FR(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_REF | (info)), NULL}
+#define FX(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL}
+#define FC(name, callback) \
+ {name, sizeof(name)-1, 0, callback}
+
+static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (!call_info->send_unpack
+ && (call_info->num_args == 2 || call_info->num_args == 3)
+ && ssa
+ && !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
+ zend_op_array *op_array = call_info->caller_op_array;
+ uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
+ &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
+ uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
+ &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
+ uint32_t t3 = 0;
+ uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED;
+
+ if (call_info->num_args == 3) {
+ t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
+ &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
+ }
+ if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ }
+ if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
+ || (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
+ || (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
+ tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+ }
+ if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
+ if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_ARRAY_OF_LONG;
+ }
+ }
+ return tmp;
+ } else {
+ /* May throw */
+ return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ }
+}
+
+#define UNKNOWN_INFO (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)
+
+static const func_info_t func_infos[] = {
+ /* zend */
+ F1("zend_version", MAY_BE_STRING),
+ FN("func_get_arg", UNKNOWN_INFO),
+ FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ F0("restore_error_handler", MAY_BE_TRUE),
+ F0("restore_exception_handler", MAY_BE_TRUE),
+ F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("get_resource_type", MAY_BE_STRING),
+ F1("get_defined_constants", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
+ F1("debug_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+
+ /* ext/standard */
+ FN("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("bin2hex", MAY_BE_STRING),
+ F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING),
+#if HAVE_NANOSLEEP
+ F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#if HAVE_STRPTIME
+ F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F1("wordwrap", MAY_BE_STRING),
+ F1("htmlspecialchars", MAY_BE_STRING),
+ F1("htmlentities", MAY_BE_STRING),
+ F1("get_html_translation_table", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("sha1", MAY_BE_STRING),
+ F1("sha1_file", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("md5", MAY_BE_STRING),
+ F1("md5_file", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iptcparse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("iptcembed", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("getimagesize", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("getimagesizefromstring", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("image_type_to_mime_type", MAY_BE_STRING),
+ F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("phpinfo", MAY_BE_TRUE),
+ F1("phpversion", MAY_BE_FALSE | MAY_BE_STRING),
+ F0("phpcredits", MAY_BE_TRUE),
+ F1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_uname", MAY_BE_STRING),
+ F1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strtok", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strrev", MAY_BE_STRING),
+ F1("hebrev", MAY_BE_STRING),
+ F1("basename", MAY_BE_STRING),
+ F1("dirname", MAY_BE_STRING),
+ F1("pathinfo", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("stripslashes", MAY_BE_STRING),
+ F1("stripcslashes", MAY_BE_STRING),
+ F1("strstr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stristr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strrchr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("str_shuffle", MAY_BE_STRING),
+ F1("str_word_count", MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("str_split", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
+ FN("substr_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("quotemeta", MAY_BE_STRING),
+ F1("ucwords", MAY_BE_STRING),
+ F1("addcslashes", MAY_BE_STRING),
+ FN("str_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ FN("str_ireplace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ F1("str_repeat", MAY_BE_STRING),
+ F1("count_chars", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("chunk_split", MAY_BE_STRING),
+ F1("strip_tags", MAY_BE_STRING),
+ F1("explode", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("localeconv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_NL_LANGINFO
+ F1("nl_langinfo", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("soundex", MAY_BE_STRING),
+ F1("chr", MAY_BE_STRING),
+ F1("str_getcsv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("strchr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sprintf", MAY_BE_STRING),
+ F1("vsprintf", MAY_BE_STRING),
+ F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
+ F1("urlencode", MAY_BE_STRING),
+ F1("urldecode", MAY_BE_STRING),
+ F1("rawurlencode", MAY_BE_STRING),
+ F1("rawurldecode", MAY_BE_STRING),
+ F1("http_build_query", MAY_BE_STRING),
+#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
+ F1("readlink", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("system", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("escapeshellcmd", MAY_BE_STRING),
+ F1("escapeshellarg", MAY_BE_STRING),
+ F0("passthru", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
+ F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("proc_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F1("random_bytes", MAY_BE_STRING),
+#if HAVE_GETSERVBYPORT
+ F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if HAVE_GETPROTOBYNUMBER
+ F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("base64_encode", MAY_BE_STRING),
+ F1("password_hash", MAY_BE_STRING),
+ F1("password_get_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("convert_uuencode", MAY_BE_STRING),
+ F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pow", MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT),
+ F1("decbin", MAY_BE_STRING),
+ F1("decoct", MAY_BE_STRING),
+ F1("dechex", MAY_BE_STRING),
+ F1("base_convert", MAY_BE_STRING),
+ F1("number_format", MAY_BE_STRING),
+#ifdef HAVE_INET_NTOP
+ F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_INET_PTON
+ F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("long2ip", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_PUTENV
+#endif
+ F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#ifdef HAVE_GETLOADAVG
+ 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_DOUBLE | MAY_BE_STRING),
+ F1("gettimeofday", MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#ifdef HAVE_GETRUSAGE
+ F1("getrusage", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ F1("uniqid", MAY_BE_STRING),
+#endif
+ F1("quoted_printable_decode", MAY_BE_STRING),
+ F1("quoted_printable_encode", MAY_BE_STRING),
+ F1("get_current_user", MAY_BE_STRING),
+ F1("get_cfg_var", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ FN("call_user_func", UNKNOWN_INFO),
+ FN("call_user_func_array", UNKNOWN_INFO),
+ FN("call_user_method", UNKNOWN_INFO),
+ FN("call_user_method_array", UNKNOWN_INFO),
+ FN("forward_static_call", UNKNOWN_INFO),
+ FN("forward_static_call_array", UNKNOWN_INFO),
+ F1("serialize", MAY_BE_STRING),
+ FN("unserialize", UNKNOWN_INFO),
+ F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
+ F1("print_r", MAY_BE_TRUE | MAY_BE_STRING),
+ F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("show_source", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("php_strip_whitespace", MAY_BE_STRING),
+ F1("ini_get_all", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("ini_alter", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("set_include_path", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if ZEND_DEBUG
+ F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#endif
+ F1("gethostbyaddr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gethostbyname", MAY_BE_STRING),
+ F1("gethostbynamel", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GETHOSTNAME
+ F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
+# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
+ F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+# endif
+#endif
+ F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_context_create", MAY_BE_RESOURCE),
+ F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING),
+#if HAVE_SOCKETPAIR
+ F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+#endif
+ F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgetcsv", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_get_meta_data", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_get_wrappers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_get_transports", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("realpath", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pack", MAY_BE_STRING),
+ F1("unpack", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("crypt", MAY_BE_STRING),
+ FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("readdir", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("scandir", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GLOB
+ F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F1("filetype", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("lstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#ifdef HAVE_SYSLOG_H
+ F0("syslog", MAY_BE_TRUE),
+ F0("closelog", MAY_BE_TRUE),
+#endif
+ F1("metaphone", MAY_BE_STRING),
+ F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("ob_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F0("array_walk", MAY_BE_TRUE),
+ F0("array_walk_recursive", MAY_BE_TRUE),
+ F0("arsort", MAY_BE_TRUE),
+ F0("asort", MAY_BE_TRUE),
+ F0("krsort", MAY_BE_TRUE),
+ F0("ksort", MAY_BE_TRUE),
+ F0("shuffle", MAY_BE_TRUE),
+ F0("sort", MAY_BE_TRUE),
+ F0("usort", MAY_BE_TRUE),
+ F0("uasort", MAY_BE_TRUE),
+ F0("uksort", MAY_BE_TRUE),
+ FN("end", UNKNOWN_INFO),
+ FN("prev", UNKNOWN_INFO),
+ FN("next", UNKNOWN_INFO),
+ FN("reset", UNKNOWN_INFO),
+ FN("current", UNKNOWN_INFO),
+ FN("min", UNKNOWN_INFO),
+ FN("max", UNKNOWN_INFO),
+ F1("compact", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FC("range", zend_range_info),
+ FN("array_pop", UNKNOWN_INFO),
+ FN("array_shift", UNKNOWN_INFO),
+ F1("array_splice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_slice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace_recursive", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FN("array_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ FN("array_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_count_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("array_column", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reverse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reduce", UNKNOWN_INFO),
+ F1("array_flip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_change_key_case", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FN("array_rand", MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_intersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_chunk", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_combine", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("pos", UNKNOWN_INFO),
+ F1("assert_options", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ F1("str_rot13", MAY_BE_STRING),
+ F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT),
+ F1("stream_bucket_new", MAY_BE_OBJECT),
+ F1("sys_get_temp_dir", MAY_BE_STRING),
+
+ /* ext/date */
+ F1("date", MAY_BE_STRING),
+ F1("gmdate", MAY_BE_STRING),
+ F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("localtime", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("getdate", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_parse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_parse_from_format", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("date_format", MAY_BE_STRING),
+ F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_diff", MAY_BE_OBJECT),
+ F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("timezone_name_get", MAY_BE_STRING),
+ F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_identifiers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("timezone_version_get", MAY_BE_STRING),
+ F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_interval_format", MAY_BE_STRING),
+ F1("date_default_timezone_get", MAY_BE_STRING),
+ F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sun_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
+
+ /* ext/preg */
+ FN("preg_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_filter", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+
+ /* ext/mysqli */
+ F1("mysqli_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F0("mysqli_close", MAY_BE_TRUE),
+ F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("mysqli_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("mysqli_get_links_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+ F1("mysqli_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
+ F1("mysqli_get_charset", MAY_BE_NULL | MAY_BE_OBJECT),
+ F1("mysqli_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mysqli_fetch_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("mysqli_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mysqli_fetch_object", MAY_BE_NULL | MAY_BE_OBJECT),
+ F1("mysqli_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_character_set_name", MAY_BE_STRING),
+ F0("mysqli_debug", MAY_BE_TRUE),
+ F1("mysqli_error", MAY_BE_STRING),
+ F1("mysqli_reap_async_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
+ F1("mysqli_stmt_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_stmt_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("mysqli_stmt_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_fetch_field", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_fetch_fields", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_OBJECT),
+ F1("mysqli_fetch_field_direct", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_fetch_lengths", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mysqli_fetch_row", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("mysqli_get_client_info", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysqli_get_host_info", MAY_BE_STRING),
+ F1("mysqli_get_server_info", MAY_BE_STRING),
+ F1("mysqli_info", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysqli_init", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_insert_id", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_num_rows", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_real_escape_string", MAY_BE_STRING),
+ F1("mysqli_stmt_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_stmt_insert_id", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_stmt_num_rows", MAY_BE_LONG | MAY_BE_STRING),
+ F1("mysqli_sqlstate", MAY_BE_STRING),
+ F0("mysqli_ssl_set", MAY_BE_TRUE),
+ F1("mysqli_stat", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysqli_stmt_error", MAY_BE_STRING),
+ F1("mysqli_stmt_init", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_stmt_result_metadata", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_stmt_sqlstate", MAY_BE_STRING),
+ F1("mysqli_store_result", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysqli_use_result", MAY_BE_FALSE | MAY_BE_OBJECT),
+
+ /* ext/curl */
+ F1("curl_init", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("curl_copy_handle", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("curl_version", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("curl_error", MAY_BE_STRING),
+ F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_escape", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_unescape", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_multi_init", MAY_BE_OBJECT),
+ F1("curl_multi_info_read", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT),
+ F1("curl_share_init", MAY_BE_OBJECT),
+ F1("curl_file_create", MAY_BE_OBJECT),
+
+ /* ext/mbstring */
+ F1("mb_convert_case", MAY_BE_STRING),
+ F1("mb_strtoupper", MAY_BE_STRING),
+ F1("mb_strtolower", MAY_BE_STRING),
+ F1("mb_language", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_http_input", MAY_BE_FALSE | MAY_BE_STRING| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_http_output", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_detect_order", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_substitute_character", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("mb_output_handler", MAY_BE_STRING),
+ F1("mb_preferred_mime_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strstr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrchr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_stristr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrichr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_substr", MAY_BE_STRING),
+ F1("mb_strcut", MAY_BE_STRING),
+ F1("mb_strimwidth", MAY_BE_STRING),
+ F1("mb_convert_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mb_detect_encoding", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_encoding_aliases", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_convert_kana", MAY_BE_STRING),
+ F1("mb_encode_mimeheader", MAY_BE_STRING),
+ F1("mb_decode_mimeheader", MAY_BE_STRING),
+ F1("mb_convert_variables", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_encode_numericentity", MAY_BE_STRING),
+ F1("mb_decode_numericentity", MAY_BE_STRING),
+ F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+
+ F1("mb_regex_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("mb_regex_set_options", MAY_BE_STRING),
+ F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_ereg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_ereg_search_pos", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mb_ereg_search_regs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
+
+ /* ext/iconv */
+ F1("iconv", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_get_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+
+ /* ext/json */
+ F1("json_encode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("json_last_error_msg", MAY_BE_STRING),
+
+ /* ext/xml */
+ F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("xml_parser_get_option", MAY_BE_LONG | MAY_BE_STRING),
+ F1("utf8_encode", MAY_BE_STRING),
+ F1("utf8_decode", MAY_BE_STRING),
+
+ /* ext/zlib */
+ F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdeflate", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzinflate", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzencode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdecode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_encode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_decode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_get_coding_type", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/hash */
+ F1("hash", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_file", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_hmac", 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_FALSE | MAY_BE_STRING),
+ F1("hash_hkdf", MAY_BE_STRING),
+ F1("hash_init", MAY_BE_OBJECT),
+ F1("hash_final", MAY_BE_STRING),
+ F1("hash_copy", MAY_BE_OBJECT),
+ F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("hash_pbkdf2", MAY_BE_STRING),
+ F1("mhash_keygen_s2k", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash_get_hash_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash", MAY_BE_FALSE | MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/sodium */
+ F1("sodium_crypto_shorthash", MAY_BE_STRING),
+ F1("sodium_crypto_secretbox", MAY_BE_STRING),
+ F1("sodium_crypto_secretbox_open", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_generichash", MAY_BE_STRING),
+ F1("sodium_crypto_generichash_init", MAY_BE_STRING),
+ F0("sodium_crypto_generichash_update", MAY_BE_TRUE),
+ F1("sodium_crypto_generichash_final", MAY_BE_STRING),
+ F1("sodium_crypto_box_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_box_seed_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_box_secretkey", MAY_BE_STRING),
+ F1("sodium_crypto_box_publickey", MAY_BE_STRING),
+ F1("sodium_crypto_box", MAY_BE_STRING),
+ F1("sodium_crypto_box_open", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_box_seal", MAY_BE_STRING),
+ F1("sodium_crypto_box_seal_open", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_sign_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_sign_seed_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_sign_secretkey", MAY_BE_STRING),
+ F1("sodium_crypto_sign_publickey", MAY_BE_STRING),
+ F1("sodium_crypto_sign", MAY_BE_STRING),
+ F1("sodium_crypto_sign_open", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_sign_detached", MAY_BE_STRING),
+ F1("sodium_crypto_stream", MAY_BE_STRING),
+ F1("sodium_crypto_stream_xor", MAY_BE_STRING),
+ F1("sodium_crypto_pwhash", MAY_BE_STRING),
+ F1("sodium_crypto_pwhash_str", MAY_BE_STRING),
+ F1("sodium_crypto_aead_aes256gcm_encrypt", MAY_BE_STRING),
+ F1("sodium_crypto_aead_aes256gcm_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_bin2hex", MAY_BE_STRING),
+ F1("sodium_hex2bin", MAY_BE_STRING),
+ F1("sodium_crypto_scalarmult", MAY_BE_STRING),
+ F1("sodium_crypto_kx_seed_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_kx_keypair", MAY_BE_STRING),
+ F1("sodium_crypto_kx_secretkey", MAY_BE_STRING),
+ F1("sodium_crypto_kx_publickey", MAY_BE_STRING),
+ F1("sodium_crypto_kx_client_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("sodium_crypto_kx_server_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("sodium_crypto_auth", MAY_BE_STRING),
+ F1("sodium_crypto_aead_aes256gcm_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_auth_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_generichash_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_kdf_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_secretbox_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_shorthash_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_stream_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_kdf_derive_from_key", MAY_BE_STRING),
+ F1("sodium_pad", MAY_BE_STRING),
+ F1("sodium_unpad", MAY_BE_STRING),
+
+ F1("sodium_crypto_box_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
+ F1("sodium_crypto_box_publickey_from_secretkey", MAY_BE_STRING),
+ F1("sodium_crypto_sign_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
+ F1("sodium_crypto_sign_publickey_from_secretkey", MAY_BE_STRING),
+ F1("sodium_crypto_pwhash_scryptsalsa208sha256", MAY_BE_STRING),
+ F1("sodium_crypto_pwhash_scryptsalsa208sha256_str", MAY_BE_STRING),
+ F1("sodium_crypto_sign_ed25519_sk_to_curve25519", MAY_BE_STRING),
+ F1("sodium_crypto_sign_ed25519_pk_to_curve25519", MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_encrypt", MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_ietf_encrypt", MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", MAY_BE_STRING),
+ F1("sodium_crypto_aead_xchacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_aead_chacha20poly1305_ietf_keygen", MAY_BE_STRING),
+ F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING),
+
+ /* ext/session */
+ F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("session_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("session_module_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("session_save_path", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("session_create_id", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("session_cache_limiter", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/pgsql */
+ F1("pg_connect", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("pg_pconnect", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_dbname", MAY_BE_STRING),
+ F1("pg_last_error", MAY_BE_STRING),
+ F1("pg_options", MAY_BE_STRING),
+ F1("pg_port", MAY_BE_STRING),
+ F1("pg_tty", MAY_BE_STRING),
+ F1("pg_host", MAY_BE_STRING),
+ F1("pg_version", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_NULL),
+ F1("pg_parameter_status", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_query", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_query_params", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_prepare", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_execute", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ FN("pg_last_notice", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY ),
+ F1("pg_field_table", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_field_name", MAY_BE_STRING),
+ F1("pg_field_type", MAY_BE_STRING),
+ F1("pg_field_type_oid", MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_fetch_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_fetch_row", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("pg_fetch_assoc", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("pg_fetch_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("pg_fetch_object", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("pg_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("pg_fetch_all_columns", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("pg_last_oid", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_lo_create", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_lo_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_lo_read", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_lo_import", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_copy_to", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("pg_escape_string", MAY_BE_STRING),
+ F1("pg_escape_bytea", MAY_BE_STRING),
+ F1("pg_unescape_bytea", MAY_BE_STRING),
+ F1("pg_escape_literal", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_escape_identifier", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_result_error", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_result_error_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("pg_get_result", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING),
+ F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("pg_socket", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_STRING),
+ F1("pg_update", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("pg_delete", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("pg_select", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+
+ /* ext/bcmath */
+ F1("bcadd", MAY_BE_STRING),
+ F1("bcsub", MAY_BE_STRING),
+ F1("bcmul", MAY_BE_STRING),
+ F1("bcdiv", MAY_BE_STRING),
+ F1("bcmod", MAY_BE_STRING),
+ F1("bcpowmod", MAY_BE_STRING),
+ F1("bcpow", MAY_BE_STRING),
+ F1("bcsqrt", MAY_BE_STRING),
+
+ /* ext/exif */
+ F1("exif_tagname", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("exif_read_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("exif_thumbnail", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/filter */
+ FN("filter_input", UNKNOWN_INFO),
+ FN("filter_var", UNKNOWN_INFO),
+ F1("filter_input_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("filter_var_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+
+ /* ext/gettext */
+ F1("textdomain", MAY_BE_STRING),
+ F1("gettext", MAY_BE_STRING),
+ F1("_", MAY_BE_STRING),
+ F1("dgettext", MAY_BE_STRING),
+ F1("dcgettext", MAY_BE_STRING),
+ F1("bindtextdomain", MAY_BE_FALSE | MAY_BE_STRING),
+#if HAVE_NGETTEXT
+ F1("ngettext", MAY_BE_STRING),
+#endif
+#if HAVE_DNGETTEXT
+ F1("dcngettext", MAY_BE_STRING),
+#endif
+#if HAVE_BIND_TEXTDOMAIN_CODESET
+ F1("bind_textdomain_codeset", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+
+ /* ext/fileinfo */
+ F1("finfo_open", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("finfo_file", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("finfo_buffer", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mime_content_type", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/gd */
+ F1("gd_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE),
+ F1("imagecreatetruecolor", MAY_BE_FALSE | MAY_BE_OBJECT),
+#ifdef PHP_WIN32
+ F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+ F1("imagerotate", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreate", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreatefromstring", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreatefromgif", MAY_BE_FALSE | MAY_BE_OBJECT),
+#ifdef HAVE_GD_JPG
+ F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+#ifdef HAVE_GD_PNG
+ F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+#ifdef HAVE_GD_WEBP
+ F1("imagecreatefromwebp", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+ F1("imagecreatefromxbm", MAY_BE_FALSE | MAY_BE_OBJECT),
+#if defined(HAVE_GD_XPM)
+ F1("imagecreatefromxpm", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+ F1("imagecreatefromwbmp", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreatefromgd", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreatefromgd2", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecreatefromgd2part", MAY_BE_FALSE | MAY_BE_OBJECT),
+#if defined(HAVE_GD_BMP)
+ F1("imagecreatefrombmp", MAY_BE_FALSE | MAY_BE_OBJECT),
+#endif
+ F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("imagecolorsforindex", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+ F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("imageftbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("imagefttext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("imagettfbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("imagettftext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("imagecrop", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagecropauto", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imagescale", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imageaffine", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("imageaffinematrixget", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
+ F1("imageaffinematrixconcat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
+ F1("imageresolution", 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),
+ F1("iterator_to_array", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("spl_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("spl_object_hash", MAY_BE_STRING),
+
+};
+
+static HashTable func_info;
+ZEND_API int zend_func_info_rid = -1;
+
+static uint32_t get_internal_func_info(
+ const zend_call_info *call_info, const zend_ssa *ssa, zend_string *lcname) {
+ if (call_info->callee_func->common.scope) {
+ /* This is a method, not a function. */
+ return 0;
+ }
+
+ zval *zv = zend_hash_find_ex(&func_info, lcname, 1);
+ if (!zv) {
+ return 0;
+ }
+
+ func_info_t *info = Z_PTR_P(zv);
+ if (info->info_func) {
+ return info->info_func(call_info, ssa);
+ } else {
+ return info->info;
+ }
+}
+
+ZEND_API uint32_t zend_get_func_info(
+ const zend_call_info *call_info, const zend_ssa *ssa,
+ zend_class_entry **ce, bool *ce_is_instanceof)
+{
+ uint32_t ret = 0;
+ const zend_function *callee_func = call_info->callee_func;
+ *ce = NULL;
+ *ce_is_instanceof = 0;
+
+ if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
+ zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline, call_info->caller_init_opline->op2));
+
+ uint32_t internal_ret = get_internal_func_info(call_info, ssa, lcname);
+#if !ZEND_DEBUG
+ if (internal_ret) {
+ return internal_ret;
+ }
+#endif
+
+ if (callee_func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ ret = zend_fetch_arg_info_type(NULL, callee_func->common.arg_info - 1, ce);
+ *ce_is_instanceof = 1;
+ } else {
+#if 0
+ fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name);
+#endif
+ ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret |= MAY_BE_REF;
+ }
+
+#if ZEND_DEBUG
+ if (internal_ret) {
+ /* Check whether the func_info information is a subset of the information we can
+ * compute from the specified return type, otherwise it contains redundant types. */
+ if (internal_ret & ~ret) {
+ fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(lcname));
+ }
+ /* Check whether the func info is completely redundant with arginfo.
+ * Ignore UNKNOWN_INFO for now. */
+ if (internal_ret == ret && (internal_ret & MAY_BE_ANY) != MAY_BE_ANY) {
+ fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(lcname));
+ }
+ /* If the return type is not mixed, check that the types match exactly if we exclude
+ * RC and array information. */
+ uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY;
+ if (ret_any != MAY_BE_ANY) {
+ uint32_t diff = internal_ret_any ^ ret_any;
+ /* Func info may contain "true" types as well as isolated "null" and "false". */
+ if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE))
+ && (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
+ fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(lcname));
+ }
+ }
+ return internal_ret;
+ }
+#endif
+ } else {
+ // FIXME: the order of functions matters!!!
+ zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
+ if (info) {
+ ret = info->return_info.type;
+ *ce = info->return_info.ce;
+ *ce_is_instanceof = info->return_info.is_instanceof;
+ }
+ if (!ret) {
+ ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | MAY_BE_RC1 | MAY_BE_RCN;
+ /* For generators RETURN_REFERENCE refers to the yielded values. */
+ if ((callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ && !(callee_func->common.fn_flags & ZEND_ACC_GENERATOR)) {
+ ret |= MAY_BE_REF;
+ }
+ }
+ }
+ return ret;
+}
+
+int zend_func_info_startup(void)
+{
+ size_t i;
+
+ if (zend_func_info_rid == -1) {
+ zend_func_info_rid = zend_get_resource_handle("Zend Optimizer");
+ if (zend_func_info_rid < 0) {
+ return FAILURE;
+ }
+
+ zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
+ for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
+ zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
+
+ if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
+ fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
+ }
+ zend_string_release_ex(key, 1);
+ }
+ }
+
+ return SUCCESS;
+}
+
+int zend_func_info_shutdown(void)
+{
+ if (zend_func_info_rid != -1) {
+ zend_hash_destroy(&func_info);
+ zend_func_info_rid = -1;
+ }
+ return SUCCESS;
+}
diff --git a/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h
new file mode 100644
index 0000000000..53ad99c22e
--- /dev/null
+++ b/Zend/Optimizer/zend_func_info.h
@@ -0,0 +1,68 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_FUNC_INFO_H
+#define ZEND_FUNC_INFO_H
+
+#include "zend_ssa.h"
+
+/* func/cfg flags */
+#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
+#define ZEND_FUNC_HAS_CALLS (1<<1)
+#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_FCALL (1<<10)
+#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
+#define ZEND_SSA_TSSA (1<<12) /* used by tracing JIT */
+
+#define ZEND_FUNC_JIT_ON_FIRST_EXEC (1<<13) /* used by JIT */
+#define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */
+#define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */
+#define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */
+
+
+typedef struct _zend_func_info zend_func_info;
+typedef struct _zend_call_info zend_call_info;
+
+#define ZEND_FUNC_INFO(op_array) \
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
+
+#define ZEND_SET_FUNC_INFO(op_array, info) do { \
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
+ *pinfo = info; \
+ } while (0)
+
+BEGIN_EXTERN_C()
+
+extern ZEND_API int zend_func_info_rid;
+
+ZEND_API uint32_t zend_get_func_info(
+ const zend_call_info *call_info, const zend_ssa *ssa,
+ zend_class_entry **ce, bool *ce_is_instanceof);
+
+int zend_func_info_startup(void);
+int zend_func_info_shutdown(void);
+
+END_EXTERN_C()
+
+#endif /* ZEND_FUNC_INFO_H */
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
new file mode 100644
index 0000000000..a61a18176c
--- /dev/null
+++ b/Zend/Optimizer/zend_inference.c
@@ -0,0 +1,4677 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_generators.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_worklist.h"
+
+/* The used range inference algorithm is described in:
+ * V. Campos, R. Rodrigues, I. de Assis Costa and F. Pereira.
+ * "Speed and Precision in Range Analysis", SBLP'12.
+ *
+ * There are a couple degrees of freedom, we use:
+ * * Propagation on SCCs.
+ * * e-SSA for live range splitting.
+ * * Only intra-procedural inference.
+ * * Widening with warmup passes, but without jump sets.
+ */
+
+/* Whether to handle symbolic range constraints */
+#define SYM_RANGE
+
+/* Whether to handle negative range constraints */
+/* Negative range inference is buggy, so disabled for now */
+#undef NEG_RANGE
+
+/* Number of warmup passes to use prior to widening */
+#define RANGE_WARMUP_PASSES 16
+
+/* Logging for range inference in general */
+#if 0
+#define LOG_SSA_RANGE(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG_SSA_RANGE(...)
+#endif
+
+/* Logging for negative range constraints */
+#if 0
+#define LOG_NEG_RANGE(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG_NEG_RANGE(...)
+#endif
+
+/* Pop elements in unspecified order from worklist until it is empty */
+#define WHILE_WORKLIST(worklist, len, i) do { \
+ bool _done = 0; \
+ while (!_done) { \
+ _done = 1; \
+ ZEND_BITSET_FOREACH(worklist, len, i) { \
+ zend_bitset_excl(worklist, i); \
+ _done = 0;
+
+#define WHILE_WORKLIST_END() \
+ } ZEND_BITSET_FOREACH_END(); \
+ } \
+} while (0)
+
+#define CHECK_SCC_VAR(var2) \
+ do { \
+ if (!ssa->vars[var2].no_val) { \
+ if (dfs[var2] < 0) { \
+ zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \
+ } \
+ if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \
+ root[var] = root[var2]; \
+ } \
+ } \
+ } while (0)
+
+#define CHECK_SCC_ENTRY(var2) \
+ do { \
+ if (ssa->vars[var2].scc != ssa->vars[var].scc) { \
+ ssa->vars[var2].scc_entry = 1; \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR_1(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc && \
+ !zend_bitset_in(visited, _var)) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define FOR_EACH_DEFINED_VAR(line, MACRO) \
+ do { \
+ if (ssa->ops[line].op1_def >= 0) { \
+ MACRO(ssa->ops[line].op1_def); \
+ } \
+ if (ssa->ops[line].op2_def >= 0) { \
+ MACRO(ssa->ops[line].op2_def); \
+ } \
+ if (ssa->ops[line].result_def >= 0) { \
+ MACRO(ssa->ops[line].result_def); \
+ } \
+ if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line-1].op1_def >= 0) { \
+ MACRO(ssa->ops[line-1].op1_def); \
+ } \
+ if (ssa->ops[line-1].op2_def >= 0) { \
+ MACRO(ssa->ops[line-1].op2_def); \
+ } \
+ if (ssa->ops[line-1].result_def >= 0) { \
+ MACRO(ssa->ops[line-1].result_def); \
+ } \
+ } else if ((uint32_t)line+1 < op_array->last && \
+ op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line+1].op1_def >= 0) { \
+ MACRO(ssa->ops[line+1].op1_def); \
+ } \
+ if (ssa->ops[line+1].op2_def >= 0) { \
+ MACRO(ssa->ops[line+1].op2_def); \
+ } \
+ if (ssa->ops[line+1].result_def >= 0) { \
+ MACRO(ssa->ops[line+1].result_def); \
+ } \
+ } \
+ } while (0)
+
+
+#define FOR_EACH_VAR_USAGE(_var, MACRO) \
+ do { \
+ zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \
+ int use = ssa->vars[_var].use_chain; \
+ while (use >= 0) { \
+ FOR_EACH_DEFINED_VAR(use, MACRO); \
+ use = zend_ssa_next_use(ssa->ops, _var, use); \
+ } \
+ p = ssa->vars[_var].phi_use_chain; \
+ while (p) { \
+ MACRO(p->ssa_var); \
+ p = zend_ssa_next_use_phi(ssa, _var, p); \
+ } \
+ } while (0)
+
+static inline 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);
+}
+#if 0
+static inline 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);
+}
+#endif
+
+static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */
+{
+#ifdef SYM_RANGE
+ zend_ssa_phi *p;
+#endif
+
+ dfs[var] = *index;
+ (*index)++;
+ root[var] = var;
+
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR);
+
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[var].sym_use_chain;
+ while (p) {
+ CHECK_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+
+ if (root[var] == var) {
+ ssa->vars[var].scc = ssa->sccs;
+ while (stack->len > 0) {
+ int var2 = zend_worklist_stack_peek(stack);
+ if (dfs[var2] <= dfs[var]) {
+ break;
+ }
+ zend_worklist_stack_pop(stack);
+ ssa->vars[var2].scc = ssa->sccs;
+ }
+ ssa->sccs++;
+ } else {
+ zend_worklist_stack_push(stack, var);
+ }
+}
+/* }}} */
+
+ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int index = 0, *dfs, *root;
+ zend_worklist_stack stack;
+ int j;
+ ALLOCA_FLAG(dfs_use_heap)
+ ALLOCA_FLAG(root_use_heap)
+ ALLOCA_FLAG(stack_use_heap)
+
+ dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap);
+ memset(dfs, -1, sizeof(int) * ssa->vars_count);
+ root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap);
+ ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
+
+ /* Find SCCs using Tarjan's algorithm. */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (!ssa->vars[j].no_val && dfs[j] < 0) {
+ zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack);
+ }
+ }
+
+ /* Revert SCC order. This results in a topological order. */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1);
+ }
+ }
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ int var = j;
+ if (root[j] == j) {
+ ssa->vars[j].scc_entry = 1;
+ }
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
+ }
+ }
+
+ ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
+ free_alloca(root, root_use_heap);
+ free_alloca(dfs, dfs_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ zend_bitset worklist;
+ int i, j, use;
+ zend_ssa_phi *p;
+ ALLOCA_FLAG(use_heap);
+
+ if (!op_array->function_name || !ssa->vars || !ssa->ops) {
+ return SUCCESS;
+ }
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ for (i = 0; i < ssa_vars_count; i++) {
+ ssa_vars[i].no_val = 1; /* mark as unused */
+ use = ssa->vars[i].use_chain;
+ while (use >= 0) {
+ if (!zend_ssa_is_no_val_use(&op_array->opcodes[use], &ssa->ops[use], i)) {
+ ssa_vars[i].no_val = 0; /* used directly */
+ zend_bitset_incl(worklist, i);
+ break;
+ }
+ use = zend_ssa_next_use(ssa_ops, i, use);
+ }
+ }
+
+ WHILE_WORKLIST(worklist, zend_bitset_len(ssa_vars_count), i) {
+ if (ssa_vars[i].definition_phi) {
+ /* mark all possible sources as used */
+ p = ssa_vars[i].definition_phi;
+ if (p->pi >= 0) {
+ if (ssa_vars[p->sources[0]].no_val) {
+ ssa_vars[p->sources[0]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[0]);
+ }
+ } else {
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ ZEND_ASSERT(p->sources[j] >= 0);
+ if (ssa->vars[p->sources[j]].no_val) {
+ ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[j]);
+ }
+ }
+ }
+ }
+ } WHILE_WORKLIST_END();
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* From "Hacker's Delight" */
+zend_ulong minOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ } else if (a & ~c & m) {
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a | c;
+}
+
+zend_ulong maxOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & d & m) {
+ temp = (b - m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ temp = (d - m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b | d;
+}
+
+zend_ulong minAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & ~c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a & c;
+}
+
+zend_ulong maxAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & ~d & m) {
+ temp = (b | ~m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ } else if (~b & d & m) {
+ temp = (d | ~m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b & d;
+}
+
+zend_ulong minXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d);
+}
+
+zend_ulong maxXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return maxOR(0, maxAND(a, b, ~d, ~c), 0, maxAND(~b, ~a, c, d));
+}
+
+/* Based on "Hacker's Delight" */
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+3: + + - - 0 0 1 1 => 1 1 - min/max
+8: - + + + 1 0 0 0 => 1 0 ? min(a,-1,b,d)/max(0,b,c,d)
+a: - + - + 1 0 1 0 => 1 0 ? MIN(a,c)/max(0,b,0,d)
+b: - + - - 1 0 1 1 => 1 1 - c/-1
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 1 - a/-1
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_or(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minOR(a, b, c, d);
+ tmp->max = maxOR(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = minOR(a, b, c, -1);
+ tmp->max = maxOR(a, b, 0, d);
+ break;
+ case 0x8:
+ tmp->min = minOR(a, -1, c, d);
+ tmp->max = maxOR(0, b, c, d);
+ break;
+ case 0xa:
+ tmp->min = MIN(a, c);
+ tmp->max = maxOR(0, b, 0, d);
+ break;
+ case 0xb:
+ tmp->min = c;
+ tmp->max = -1;
+ break;
+ case 0xe:
+ tmp->min = a;
+ tmp->max = -1;
+ break;
+ }
+}
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 0 0 + 0/b
+3: + + - - 0 0 1 1 => 0 0 + min/max
+8: - + + + 1 0 0 0 => 0 0 + 0/d
+a: - + - + 1 0 1 0 => 1 0 ? min(a,-1,c,-1)/NAX(b,d)
+b: - + - - 1 0 1 1 => 1 0 ? min(a,-1,c,d)/max(0,b,c,d)
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_and(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minAND(a, b, c, d);
+ tmp->max = maxAND(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = 0;
+ tmp->max = b;
+ break;
+ case 0x8:
+ tmp->min = 0;
+ tmp->max = d;
+ break;
+ case 0xa:
+ tmp->min = minAND(a, -1, c, -1);
+ tmp->max = MAX(b, d);
+ break;
+ case 0xb:
+ tmp->min = minAND(a, -1, c, d);
+ tmp->max = maxAND(0, b, c, d);
+ break;
+ case 0xe:
+ tmp->min = minAND(a, b, c, -1);
+ tmp->max = maxAND(a, b, 0, d);
+ break;
+ }
+}
+
+static inline bool zend_abs_range(
+ zend_long min, zend_long max, zend_long *abs_min, zend_long *abs_max) {
+ if (min == ZEND_LONG_MIN) {
+ /* Cannot take absolute value of LONG_MIN */
+ return 0;
+ }
+
+ if (min >= 0) {
+ *abs_min = min;
+ *abs_max = max;
+ } else if (max <= 0) {
+ *abs_min = -max;
+ *abs_max = -min;
+ } else {
+ /* Range crossing zero */
+ *abs_min = 0;
+ *abs_max = MAX(max, -min);
+ }
+
+ 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 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 || safe_shift_left(n, s) < n;
+ } else {
+ return s >= SIZEOF_ZEND_LONG * 8 || safe_shift_left(n, s) > n;
+ }
+}
+
+/* If b does not divide a exactly, return the two adjacent values between which the real result
+ * lies. */
+static void float_div(zend_long a, zend_long b, zend_long *r1, zend_long *r2) {
+ *r1 = *r2 = a / b;
+ if (a % b != 0) {
+ if (*r2 < 0) {
+ (*r2)--;
+ } else {
+ (*r2)++;
+ }
+ }
+}
+
+static int zend_inference_calc_binary_op_range(
+ const zend_op_array *op_array, zend_ssa *ssa,
+ zend_op *opline, zend_ssa_op *ssa_op, zend_uchar opcode, zend_ssa_range *tmp) {
+ zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+
+ switch (opcode) {
+ case ZEND_ADD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ 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() ||
+ 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;
+ }
+ break;
+ case ZEND_SUB:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ 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() ||
+ 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();
+ /* 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() ||
+ t1_overflow || t2_overflow || t3_overflow || t4_overflow
+ ) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_DIV:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min <= 0 && op2_max >= 0) {
+ /* If op2 crosses zero, then floating point values close to zero might be
+ * possible, which will result in arbitrarily large results. As such, we can't
+ * do anything useful in that case. */
+ break;
+ }
+ if (op1_min == ZEND_LONG_MIN && op2_max == -1) {
+ /* Avoid ill-defined division, which may trigger SIGFPE. */
+ break;
+ }
+
+ zend_long t1_, t2_, t3_, t4_;
+ float_div(op1_min, op2_min, &t1, &t1_);
+ float_div(op1_min, op2_max, &t2, &t2_);
+ float_div(op1_max, op2_min, &t3, &t3_);
+ float_div(op1_max, op2_max, &t4, &t4_);
+
+ /* The only case in which division can "overflow" either a division by an absolute
+ * value smaller than one, or LONG_MIN / -1 in particular. Both cases have already
+ * been excluded above. */
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(MIN(t1, t2), MIN(t3, t4)), MIN(MIN(t1_, t2_), MIN(t3_, t4_)));
+ tmp->max = MAX(MAX(MAX(t1, t2), MAX(t3, t4)), MAX(MAX(t1_, t2_), MAX(t3_, t4_)));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_MOD:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ zend_long op2_abs_min, op2_abs_max;
+
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (!zend_abs_range(op2_min, op2_max, &op2_abs_min, &op2_abs_max)) {
+ break;
+ }
+
+ if (op2_abs_max == 0) {
+ /* Always modulus by zero, nothing we can do */
+ break;
+ }
+ if (op2_abs_min == 0) {
+ /* Ignore the modulus by zero case, which will throw */
+ op2_abs_min++;
+ }
+
+ if (op1_min >= 0) {
+ tmp->min = op1_max < op2_abs_min ? op1_min : 0;
+ tmp->max = MIN(op1_max, op2_abs_max - 1);
+ } else if (op1_max <= 0) {
+ tmp->min = MAX(op1_min, -op2_abs_max + 1);
+ tmp->max = op1_min > -op2_abs_min ? op1_max : 0;
+ } else {
+ tmp->min = MAX(op1_min, -op2_abs_max + 1);
+ tmp->max = MIN(op1_max, op2_abs_max - 1);
+ }
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SL:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ /* Shifts by negative numbers will throw, ignore them */
+ if (op2_min < 0) {
+ op2_min = 0;
+ }
+ if (op2_max < 0) {
+ op2_max = 0;
+ }
+
+ if (shift_left_overflows(op1_min, op2_max)
+ || shift_left_overflows(op1_max, op2_max)) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ 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));
+ }
+ }
+ return 1;
+ }
+ break;
+ case ZEND_SR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ /* Shifts by negative numbers will throw, ignore them */
+ if (op2_min < 0) {
+ op2_min = 0;
+ }
+ if (op2_max < 0) {
+ op2_max = 0;
+ }
+
+ /* Shifts by more than the integer size will be 0 or -1 */
+ if (op2_min >= SIZEOF_ZEND_LONG * 8) {
+ op2_min = SIZEOF_ZEND_LONG * 8 - 1;
+ }
+ if (op2_max >= SIZEOF_ZEND_LONG * 8) {
+ op2_max = SIZEOF_ZEND_LONG * 8 - 1;
+ }
+
+ t1 = op1_min >> op2_min;
+ t2 = op1_min >> op2_max;
+ t3 = op1_max >> op2_min;
+ t4 = op1_max >> op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_OR:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_AND:
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ break;
+ case ZEND_BW_XOR:
+ // TODO
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return 0;
+}
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
+{
+ uint32_t line;
+ zend_op *opline;
+ zend_ssa_op *ssa_op;
+
+ if (ssa->vars[var].definition_phi) {
+ zend_ssa_phi *p = ssa->vars[var].definition_phi;
+ int i;
+
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MAX;
+ tmp->max = ZEND_LONG_MIN;
+ tmp->overflow = 0;
+ if (p->pi >= 0 && p->has_range_constraint) {
+ zend_ssa_range_constraint *constraint = &p->constraint.range;
+ if (constraint->negative) {
+ if (ssa->var_info[p->sources[0]].has_range) {
+ *tmp = ssa->var_info[p->sources[0]].range;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+
+#ifdef NEG_RANGE
+ if (constraint->min_ssa_var < 0 &&
+ constraint->max_ssa_var < 0 &&
+ ssa->var_info[p->ssa_var].has_range) {
+ LOG_NEG_RANGE("%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
+ ZSTR_VAL(op_array->function_name),
+ p->ssa_var,
+ ssa->var_info[p->ssa_var].range.min,
+ ssa->var_info[p->ssa_var].range.max,
+ tmp->min,
+ tmp->max);
+ if (constraint->negative == NEG_USE_LT &&
+ tmp->max >= constraint->range.min) {
+ tmp->overflow = 0;
+ tmp->max = constraint->range.min - 1;
+ LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
+ } else if (constraint->negative == NEG_USE_GT &&
+ tmp->min <= constraint->range.max) {
+ tmp->underflow = 0;
+ tmp->min = constraint->range.max + 1;
+ LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
+ }
+ }
+#endif
+ } else if (ssa->var_info[p->sources[0]].has_range) {
+ /* intersection */
+ *tmp = ssa->var_info[p->sources[0]].range;
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow && tmp->underflow;
+ tmp->min = MAX(constraint->range.min, tmp->min);
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow;
+ if (!add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) {
+ tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min);
+ }
+#endif
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = MIN(constraint->range.max, tmp->max);
+ tmp->overflow = constraint->range.overflow && tmp->overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ if (!add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) {
+ tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max);
+ }
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow;
+#endif
+ }
+ } else if (narrowing) {
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow;
+ tmp->min = constraint->range.min;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ if (add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ } else {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow;
+ tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min;
+ }
+#endif
+ } else {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = constraint->range.max;
+ tmp->overflow = constraint->range.overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ if (add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max;
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow;
+ }
+#endif
+ } else {
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ } else {
+ for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
+ ZEND_ASSERT(p->sources[i] >= 0);
+ if (ssa->var_info[p->sources[i]].has_range) {
+ /* union */
+ tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow;
+ tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min);
+ tmp->max = MAX(tmp->max, ssa->var_info[p->sources[i]].range.max);
+ tmp->overflow |= ssa->var_info[p->sources[i]].range.overflow;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ }
+ return (tmp->min <= tmp->max);
+ } else if (ssa->vars[var].definition < 0) {
+ if (var < op_array->last_var &&
+ op_array->function_name) {
+
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ return 1;
+ }
+ return 0;
+ }
+ line = ssa->vars[var].definition;
+ opline = op_array->opcodes + line;
+ ssa_op = &ssa->ops[line];
+
+ return zend_inference_propagate_range(op_array, ssa, opline, ssa_op, var, tmp);
+}
+
+ZEND_API int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp)
+{
+ zend_long op1_min, op2_min, op1_max, op2_max;
+
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if (ssa_op->result_def == var) {
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, ssa_op, opline->opcode, tmp);
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = ~op1_max;
+ tmp->max = ~op1_min;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_CAST:
+ if (ssa_op->op1_def == var) {
+ if (ssa_op->op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ } else if (ssa_op->result_def == var) {
+ if (opline->extended_value == IS_LONG) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ return 1;
+ } else {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_BOOL:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min > 0 || op1_max < 0);
+ tmp->max = (op1_min != 0 || op1_max != 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_NOT:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min == 0 && op1_max == 0);
+ tmp->max = (op1_min <= 0 && op1_max >= 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_XOR:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ op1_min = (op1_min > 0 || op1_max < 0);
+ op1_max = (op1_min != 0 || op1_max != 0);
+ op2_min = (op2_min > 0 || op2_max < 0);
+ op2_max = (op2_min != 0 || op2_max != 0);
+ tmp->min = 0;
+ tmp->max = 1;
+ if (op1_min == op1_max && op2_min == op2_max) {
+ if (op1_min == op2_min) {
+ tmp->max = 0;
+ } else {
+ tmp->min = 1;
+ }
+ }
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min == op1_max &&
+ op2_min == op2_max &&
+ op1_min == op2_max);
+ tmp->max = (op1_min <= op2_max && op1_max >= op2_min);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min > op2_max || op1_max < op2_min);
+ tmp->max = (op1_min != op1_max ||
+ op2_min != op2_max ||
+ op1_min != op2_max);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max < op2_min;
+ tmp->max = op1_min < op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max <= op2_min;
+ tmp->max = op1_min <= op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_COPY_TMP:
+ if (ssa_op->op1_def == var) {
+ if (ssa_op->op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ if (ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSERT_CHECK:
+ if (ssa_op->result_def == var) {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ if (ssa_op->op1_def == var) {
+ if (ssa_op->op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_PRE_INC:
+ if (ssa_op->op1_def == var || ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_PRE_DEC:
+ if (ssa_op->op1_def == var || ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_INC:
+ if (ssa_op->op1_def == var || ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa_op->result_def == var) {
+ return 1;
+ }
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_DEC:
+ if (ssa_op->op1_def == var || ssa_op->result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa_op->result_def == var) {
+ return 1;
+ }
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa_op->op1_def == var) {
+ /* If op1 is scalar, UNSET_DIM and UNSET_OBJ have no effect, so we can keep
+ * the previous ranges. */
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (ssa_op->op1_def == var || ssa_op->op2_def == var || ssa_op->result_def == var) {
+ if (OP2_HAS_RANGE()) {
+ tmp->min = OP2_MIN_RANGE();
+ tmp->max = OP2_MAX_RANGE();
+ tmp->underflow = OP2_RANGE_UNDERFLOW();
+ tmp->overflow = OP2_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_ASSIGN_STATIC_PROP:
+ case ZEND_ASSIGN_DIM_OP:
+ case ZEND_ASSIGN_OBJ_OP:
+ case ZEND_ASSIGN_STATIC_PROP_OP:
+ if ((ssa_op+1)->op1_def == var) {
+ opline++;
+ ssa_op++;
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ }
+ return 1;
+ }
+ break;
+ case ZEND_ASSIGN_OP:
+ if (opline->extended_value != ZEND_CONCAT
+ && opline->extended_value != ZEND_POW) {
+ if (ssa_op->op1_def == var || ssa_op->result_def == var) {
+ return zend_inference_calc_binary_op_range(
+ op_array, ssa, opline, ssa_op,
+ opline->extended_value, tmp);
+ }
+ }
+ break;
+ case ZEND_OP_DATA:
+ if (ssa_op->op1_def == var) {
+ if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+ (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
+ (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP ||
+ (opline-1)->opcode == ZEND_ASSIGN_DIM_OP ||
+ (opline-1)->opcode == ZEND_ASSIGN_OBJ_OP ||
+ (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP_OP) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ }
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ if (ssa_op->result_def == var) {
+ if (op_array->arg_info &&
+ opline->op1.num <= op_array->num_args) {
+ zend_type type = op_array->arg_info[opline->op1.num-1].type;
+ uint32_t mask = ZEND_TYPE_PURE_MASK_WITHOUT_NULL(type);
+ if (mask == MAY_BE_LONG) {
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 0;
+ return 1;
+ } else if (mask == MAY_BE_BOOL) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 1;
+ tmp->overflow = 0;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_STRLEN:
+ if (ssa_op->result_def == var) {
+#if SIZEOF_ZEND_LONG == 4
+ /* The length of a string is a non-negative integer. However, on 32-bit
+ * platforms overflows into negative lengths may occur, so it's better
+ * to not assume any particular range. */
+ tmp->min = ZEND_LONG_MIN;
+#else
+ tmp->min = 0;
+#endif
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ }
+ break;
+ case ZEND_FUNC_NUM_ARGS:
+ tmp->min = 0;
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ case ZEND_COUNT:
+ /* count() on Countable objects may return negative numbers */
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa_op->result_def == var) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+ if (!func_info || !func_info->call_map) {
+ break;
+ }
+
+ call_info = func_info->call_map[opline - op_array->opcodes];
+ if (!call_info) {
+ break;
+ }
+ if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+ func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+ if (func_info && func_info->return_info.has_range) {
+ *tmp = func_info->return_info.range;
+ return 1;
+ }
+ }
+//TODO: we can't use type inference for internal functions at this point ???
+#if 0
+ uint32_t type;
+
+ type = zend_get_func_info(call_info, ssa);
+ if (!(type & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)))) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->overflow = 0;
+ if (type & MAY_BE_LONG) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else if (type & MAY_BE_TRUE) {
+ if (!(type & (MAY_BE_NULL|MAY_BE_FALSE))) {
+ tmp->min = 1;
+ }
+ tmp->max = 1;
+ }
+ return 1;
+ }
+#endif
+ }
+ break;
+ // FIXME: support for more opcodes
+ default:
+ break;
+ }
+ return 0;
+}
+
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow)
+{
+ if (underflow) {
+ min = ZEND_LONG_MIN;
+ }
+ if (overflow) {
+ max = ZEND_LONG_MAX;
+ }
+ ssa->var_info[var].has_range = 1;
+ ssa->var_info[var].range.underflow = underflow;
+ ssa->var_info[var].range.min = min;
+ ssa->var_info[var].range.max = max;
+ ssa->var_info[var].range.overflow = overflow;
+ LOG_SSA_RANGE(" change range (init SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":""));
+}
+
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (r->underflow ||
+ var_info->range.underflow ||
+ r->min < var_info->range.min) {
+ r->underflow = 1;
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow ||
+ var_info->range.overflow ||
+ r->max > var_info->range.max) {
+ r->overflow = 1;
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 1, 0, &tmp)) {
+ if (zend_inference_widening_meet(&ssa->var_info[var], &tmp)) {
+ LOG_SSA_RANGE(" change range (widening SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (!r->underflow &&
+ !var_info->range.underflow &&
+ var_info->range.min < r->min) {
+ r->min = var_info->range.min;
+ }
+ if (!r->overflow &&
+ !var_info->range.overflow &&
+ var_info->range.max > r->max) {
+ r->max = var_info->range.max;
+ }
+ if (r->underflow) {
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow) {
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 0, 1, &tmp)) {
+ if (zend_inference_narrowing_meet(&ssa->var_info[var], &tmp)) {
+ LOG_SSA_RANGE(" change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef NEG_RANGE
+# define CHECK_INNER_CYCLE(var2) \
+ do { \
+ if (ssa->vars[var2].scc == ssa->vars[var].scc && \
+ !ssa->vars[var2].scc_entry && \
+ !zend_bitset_in(visited, var2) && \
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, var2)) { \
+ return 1; \
+ } \
+ } while (0)
+
+static int zend_check_inner_cycles(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, zend_bitset visited, int var)
+{
+ if (zend_bitset_in(worklist, var)) {
+ return 1;
+ }
+ zend_bitset_incl(worklist, var);
+ FOR_EACH_VAR_USAGE(var, CHECK_INNER_CYCLE);
+ zend_bitset_incl(visited, var);
+ return 0;
+}
+#endif
+
+static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ssa, int *scc_var, int *next_scc_var, int scc)
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ int j, n;
+ zend_ssa_range tmp;
+ ALLOCA_FLAG(use_heap)
+ zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap);
+ zend_bitset visited = worklist + worklist_len;
+#ifdef NEG_RANGE
+ int has_inner_cycles = 0;
+
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+ j = scc_var[scc];
+ while (j >= 0) {
+ if (!zend_bitset_in(visited, j) &&
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) {
+ has_inner_cycles = 1;
+ break;
+ }
+ j = next_scc_var[j];
+ }
+#endif
+
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+
+ for (n = 0; n < RANGE_WARMUP_PASSES; n++) {
+ j= scc_var[scc];
+ while (j >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ }
+
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 0, &tmp)) {
+#ifdef NEG_RANGE
+ if (!has_inner_cycles &&
+ ssa->var_info[j].has_range &&
+ ssa->vars[j].definition_phi &&
+ ssa->vars[j].definition_phi->pi >= 0 &&
+ ssa->vars[j].definition_phi->has_range_constraint &&
+ ssa->vars[j].definition_phi->constraint.range.negative &&
+ ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 &&
+ ssa->vars[j].definition_phi->constraint.range.max_ssa_var < 0) {
+ zend_ssa_range_constraint *constraint =
+ &ssa->vars[j].definition_phi->constraint.range;
+ if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max) {
+ if (constraint->negative == NEG_INIT) {
+ LOG_NEG_RANGE("#%d INVARIANT\n", j);
+ constraint->negative = NEG_INVARIANT;
+ }
+ } else if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max + 1 &&
+ tmp.max < constraint->range.min) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+ LOG_NEG_RANGE("#%d LT\n", j);
+ constraint->negative = NEG_USE_LT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_GT) {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else if (tmp.max == ssa->var_info[j].range.max &&
+ tmp.min == ssa->var_info[j].range.min - 1 &&
+ tmp.min > constraint->range.max) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+ LOG_NEG_RANGE("#%d GT\n", j);
+ constraint->negative = NEG_USE_GT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_LT) {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else {
+ LOG_NEG_RANGE("#%d UNKNOWN\n", j);
+ constraint->negative = NEG_UNKNOWN;
+ }
+ }
+#endif
+ if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) {
+ LOG_SSA_RANGE(" change range (warmup %2d SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+ zend_bitset_incl(visited, j);
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1);
+ }
+ }
+ } WHILE_WORKLIST_END();
+ }
+ free_alloca(worklist, use_heap);
+}
+
+static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset worklist;
+ int *next_scc_var;
+ int *scc_var;
+ zend_ssa_phi *p;
+ zend_ssa_range tmp;
+ int scc, j;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count) +
+ sizeof(int) * ssa->sccs, use_heap);
+ next_scc_var = (int*)((char*)worklist + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len));
+ scc_var = (int*)((char*)next_scc_var + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count));
+
+ LOG_SSA_RANGE("Range Inference\n");
+
+ /* Create linked lists of SSA variables for each SCC */
+ memset(scc_var, -1, sizeof(int) * ssa->sccs);
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ next_scc_var[j] = scc_var[ssa->vars[j].scc];
+ scc_var[ssa->vars[j].scc] = j;
+ }
+ }
+
+ for (scc = 0; scc < ssa->sccs; scc++) {
+ j = scc_var[scc];
+ if (next_scc_var[j] < 0) {
+ /* SCC with a single element */
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) {
+ zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow);
+ } else {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ }
+ } else {
+ /* Find SCC entry points */
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ do {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ } while (j >= 0);
+
+#if RANGE_WARMUP_PASSES > 0
+ zend_infer_ranges_warmup(op_array, ssa, scc_var, next_scc_var, scc);
+ j = scc_var[scc];
+ do {
+ zend_bitset_incl(worklist, j);
+ j = next_scc_var[j];
+ } while (j >= 0);
+#endif
+
+ /* widening */
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+ }
+ } WHILE_WORKLIST_END();
+
+ /* initialize missing ranges */
+ for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
+ if (!ssa->var_info[j].has_range) {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+ }
+ }
+
+ /* widening (second round) */
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+ }
+ } WHILE_WORKLIST_END();
+
+ /* Add all SCC entry variables into worklist for narrowing */
+ for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
+ if (ssa->vars[j].definition_phi
+ && ssa->vars[j].definition_phi->pi < 0) {
+ /* narrowing Phi functions first */
+ zend_ssa_range_narrowing(op_array, ssa, j, scc);
+ }
+ zend_bitset_incl(worklist, j);
+ }
+
+ /* narrowing */
+ WHILE_WORKLIST(worklist, worklist_len, j) {
+ if (zend_ssa_range_narrowing(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[j].sym_use_chain;
+ while (p) {
+ ADD_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+ }
+ } WHILE_WORKLIST_END();
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) {
+ if (alias == HTTP_RESPONSE_HEADER_ALIAS) {
+ return MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+}
+
+#define UPDATE_SSA_TYPE(_type, _var) \
+ do { \
+ uint32_t __type = (_type) & ~MAY_BE_GUARD; \
+ int __var = (_var); \
+ if (__type & MAY_BE_REF) { \
+ __type |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \
+ } \
+ if (__var >= 0) { \
+ zend_ssa_var *__ssa_var = &ssa_vars[__var]; \
+ if (__ssa_var->var < op_array->last_var) { \
+ if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \
+ __type |= MAY_BE_RC1 | MAY_BE_RCN; \
+ } \
+ if ((__type & MAY_BE_RC1) && (__type & MAY_BE_STRING)) {\
+ /* TODO: support for array keys and ($str . "")*/ \
+ __type |= MAY_BE_RCN; \
+ } \
+ if (__ssa_var->alias) { \
+ __type |= get_ssa_alias_types(__ssa_var->alias); \
+ } \
+ } \
+ if (ssa_var_info[__var].type != __type) { \
+ if (ssa_var_info[__var].type & ~__type) { \
+ emit_type_narrowing_warning(op_array, ssa, __var); \
+ return FAILURE; \
+ } \
+ ssa_var_info[__var].type = __type; \
+ if (update_worklist) { \
+ add_usages(op_array, ssa, worklist, __var); \
+ } \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define UPDATE_SSA_OBJ_TYPE(_ce, _is_instanceof, var) \
+ do { \
+ if (var >= 0) { \
+ if (ssa_var_info[var].ce != (_ce) || \
+ ssa_var_info[var].is_instanceof != (_is_instanceof)) { \
+ ssa_var_info[var].ce = (_ce); \
+ ssa_var_info[var].is_instanceof = (_is_instanceof); \
+ if (update_worklist) { \
+ add_usages(op_array, ssa, worklist, var); \
+ } \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define COPY_SSA_OBJ_TYPE(from_var, to_var) do { \
+ if ((from_var) >= 0 && (ssa_var_info[(from_var)].type & MAY_BE_OBJECT) \
+ && ssa_var_info[(from_var)].ce) { \
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[(from_var)].ce, \
+ ssa_var_info[(from_var)].is_instanceof, (to_var)); \
+ } else { \
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, (to_var)); \
+ } \
+} while (0)
+
+static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+ if (ssa->vars[var].phi_use_chain) {
+ zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
+ do {
+ zend_bitset_incl(worklist, p->ssa_var);
+ p = zend_ssa_next_use_phi(ssa, var, p);
+ } while (p);
+ }
+ if (ssa->vars[var].use_chain >= 0) {
+ int use = ssa->vars[var].use_chain;
+ zend_ssa_op *op;
+
+ do {
+ op = ssa->ops + use;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
+ op--;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ }
+ use = zend_ssa_next_use(ssa->ops, var, use);
+ } while (use >= 0);
+ }
+}
+
+static void emit_type_narrowing_warning(const zend_op_array *op_array, zend_ssa *ssa, int var)
+{
+ int def_op_num = ssa->vars[var].definition;
+ const zend_op *def_opline = def_op_num >= 0 ? &op_array->opcodes[def_op_num] : NULL;
+ const char *def_op_name = def_opline ? zend_get_opcode_name(def_opline->opcode) : "PHI";
+ zend_error(E_WARNING, "Narrowing occurred during type inference of %s. Please file a bug report on bugs.php.net", def_op_name);
+}
+
+ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert)
+{
+ uint32_t tmp = 0;
+
+ if (t1 & MAY_BE_OBJECT) {
+ if (!write) {
+ /* can't be REF because of ZVAL_COPY_DEREF() usage */
+ tmp |= MAY_BE_ANY | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (write) {
+ tmp |= MAY_BE_INDIRECT;
+ }
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (insert) {
+ tmp |= MAY_BE_NULL;
+ } else {
+ tmp |= MAY_BE_NULL | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ if (!write) {
+ /* can't be REF because of ZVAL_COPY_DEREF() usage */
+ tmp |= MAY_BE_RCN;
+ if ((op_type & (IS_VAR|IS_TMP_VAR)) && (t1 & MAY_BE_RC1)) {
+ tmp |= MAY_BE_RC1;
+ }
+ } else if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ if (write) {
+ tmp |= MAY_BE_INDIRECT;
+ }
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ if (write) {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp |= MAY_BE_NULL;
+ if (write) {
+ tmp |= MAY_BE_INDIRECT;
+ }
+ }
+ if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
+ if (!write) {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ return tmp;
+}
+
+static uint32_t assign_dim_result_type(
+ uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
+ uint32_t tmp = arr_type & ~(MAY_BE_RC1|MAY_BE_RCN);
+
+ if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ tmp |= MAY_BE_ARRAY|MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_ARRAY|MAY_BE_STRING)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (tmp & MAY_BE_ARRAY) {
+ /* Only add key type if we have a value type. We want to maintain the invariant that a
+ * key type exists iff a value type exists even in dead code that may use empty types. */
+ if (value_type & (MAY_BE_ANY|MAY_BE_UNDEF)) {
+ if (value_type & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_ARRAY_OF_NULL;
+ }
+ if (dim_op_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (dim_type & MAY_BE_STRING) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (dim_op_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ }
+ /* Only add value type if we have a key type. It might be that the key type is illegal
+ * for arrays. */
+ if (tmp & MAY_BE_ARRAY_KEY_ANY) {
+ tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ }
+ }
+ return tmp;
+}
+
+/* For binary ops that have compound assignment operators */
+static uint32_t binary_op_result_type(
+ zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, int result_var,
+ zend_long optimization_level) {
+ uint32_t tmp = 0;
+ uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+ uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+
+ if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) {
+ /* Handle potentially overloaded operators.
+ * This could be made more precise by checking the class type, if known. */
+ if ((t1_type & MAY_BE_OBJECT) || (t2_type & MAY_BE_OBJECT)) {
+ /* This is somewhat GMP specific. */
+ tmp |= MAY_BE_OBJECT | MAY_BE_FALSE | MAY_BE_RC1;
+ }
+ }
+
+ switch (opcode) {
+ case ZEND_ADD:
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (result_var < 0 ||
+ !ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ }
+ }
+ break;
+ case ZEND_SUB:
+ case ZEND_MUL:
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (result_var < 0 ||
+ !ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ break;
+ case ZEND_DIV:
+ case ZEND_POW:
+ if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special
+ * handling */
+ break;
+ case ZEND_MOD:
+ tmp |= MAY_BE_LONG;
+ /* Division by zero results in an exception, so it doesn't need any special handling */
+ break;
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ }
+ if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ break;
+ case ZEND_SL:
+ case ZEND_SR:
+ tmp |= MAY_BE_LONG;
+ break;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ /* TODO: +MAY_BE_OBJECT ??? */
+ tmp = MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN;
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return tmp;
+}
+
+static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) {
+ zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
+ if (ce) {
+ return ce;
+ }
+
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (ce && ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ }
+
+ return NULL;
+}
+
+static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) {
+ uint32_t result_mask = type_mask & MAY_BE_ANY;
+ if (type_mask & MAY_BE_VOID) {
+ result_mask |= MAY_BE_NULL;
+ }
+ if (type_mask & MAY_BE_CALLABLE) {
+ result_mask |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (type_mask & MAY_BE_ITERABLE) {
+ result_mask |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (type_mask & MAY_BE_STATIC) {
+ result_mask |= MAY_BE_OBJECT;
+ }
+ if (type_mask & MAY_BE_ARRAY) {
+ result_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ return result_mask;
+}
+
+ZEND_API uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
+{
+ uint32_t tmp;
+
+ *pce = NULL;
+ if (!ZEND_TYPE_IS_SET(arg_info->type)) {
+ 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;
+ }
+
+ tmp = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(arg_info->type));
+ if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
+ tmp |= MAY_BE_OBJECT;
+ /* As we only have space to store one CE, we use a plain object type for class unions. */
+ if (ZEND_TYPE_HAS_NAME(arg_info->type)) {
+ zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(arg_info->type));
+ *pce = get_class_entry(script, lcname);
+ zend_string_release_ex(lcname, 0);
+ }
+ }
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ 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, zend_ssa_op *ssa_op)
+{
+ 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_op->op1_use >= 0) {
+ ce = ssa->var_info[ssa_op->op1_use].ce;
+ }
+ if (ce) {
+ prop_info = lookup_prop_info(ce,
+ Z_STR_P(CRT_CONSTANT(opline->op2)),
+ 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(opline->op2);
+ ce = get_class_entry(script, Z_STR_P(zv + 1));
+ }
+
+ if (ce) {
+ zval *zv = CRT_CONSTANT(opline->op1);
+ 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 (pce) {
+ *pce = NULL;
+ }
+ if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) {
+ uint32_t type = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(prop_info->type));
+
+ if (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ type |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ if (ZEND_TYPE_HAS_CLASS(prop_info->type)) {
+ type |= MAY_BE_OBJECT;
+ if (pce) {
+ if (ZEND_TYPE_HAS_CE(prop_info->type)) {
+ *pce = ZEND_TYPE_CE(prop_info->type);
+ } else if (ZEND_TYPE_HAS_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);
+ }
+ }
+ }
+ return type;
+ }
+ 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 zend_always_inline int _zend_update_type_info(
+ const zend_op_array *op_array,
+ zend_ssa *ssa,
+ const zend_script *script,
+ zend_bitset worklist,
+ zend_op *opline,
+ zend_ssa_op *ssa_op,
+ const zend_op **ssa_opcodes,
+ zend_long optimization_level,
+ bool update_worklist)
+{
+ uint32_t t1, t2;
+ uint32_t tmp, orig;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ zend_class_entry *ce;
+ int j;
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ opline--;
+ ssa_op--;
+ }
+
+ t1 = OP1_INFO();
+ t2 = OP2_INFO();
+
+ /* If one of the operands cannot have any type, this means the operand derives from
+ * unreachable code. Propagate the empty result early, so that that the following
+ * code may assume that operands have at least one type. */
+ if (!(t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))
+ || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))) {
+ tmp = 0;
+ if (ssa_op->result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ if (ssa_op->op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ if (ssa_op->op2_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
+ }
+ return 1;
+ }
+
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_POW:
+ case ZEND_MOD:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_CONCAT:
+ tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_op->result_def, optimization_level);
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_BW_NOT:
+ tmp = 0;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) {
+ if (t1 & MAY_BE_OBJECT) {
+ /* Potentially overloaded operator. */
+ tmp |= MAY_BE_OBJECT | MAY_BE_RC1;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_BEGIN_SILENCE:
+ UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
+ break;
+ case ZEND_BOOL_NOT:
+ case ZEND_BOOL_XOR:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_INSTANCEOF:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_BOOL:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_IN_ARRAY:
+ case ZEND_ARRAY_KEY_EXISTS:
+ UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def);
+ break;
+ case ZEND_CAST:
+ if (ssa_op->op1_def >= 0) {
+ tmp = t1;
+ if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) &&
+ (opline->extended_value == IS_ARRAY ||
+ opline->extended_value == IS_OBJECT)) {
+ tmp |= MAY_BE_RCN;
+ } else if ((t1 & MAY_BE_STRING) &&
+ opline->extended_value == IS_STRING) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ tmp = 1 << opline->extended_value;
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ if ((tmp & MAY_BE_ANY) == (t1 & MAY_BE_ANY)) {
+ tmp |= (t1 & MAY_BE_RC1) | MAY_BE_RCN;
+ } else if ((opline->extended_value == IS_ARRAY ||
+ opline->extended_value == IS_OBJECT) &&
+ (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (opline->extended_value == IS_STRING &&
+ (t1 & (MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1;
+ }
+ }
+ if (opline->extended_value == IS_ARRAY) {
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF);
+ }
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY) ? MAY_BE_ARRAY_PACKED : 0);
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_COPY_TMP:
+ if (ssa_op->op1_def >= 0) {
+ tmp = t1;
+ if (t1 & (MAY_BE_RC1|MAY_BE_REF)) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ tmp = t1 & ~(MAY_BE_UNDEF|MAY_BE_REF);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= (t1 & (MAY_BE_RC1|MAY_BE_RCN));
+ if (opline->op1_type == IS_CV) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if (opline->opcode != ZEND_QM_ASSIGN) {
+ /* COALESCE and JMP_SET result can't be null */
+ tmp &= ~MAY_BE_NULL;
+ if (opline->opcode == ZEND_JMP_SET) {
+ /* JMP_SET result can't be false either */
+ tmp &= ~MAY_BE_FALSE;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
+ break;
+ case ZEND_JMP_NULL:
+ if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
+ tmp = MAY_BE_NULL;
+ } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
+ tmp = MAY_BE_FALSE;
+ } else {
+ ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
+ tmp = MAY_BE_TRUE;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ 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->opcode == ZEND_ASSIGN_OBJ_OP) {
+ prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
+ orig = t1;
+ t1 = zend_fetch_prop_type(script, prop_info, &ce);
+ t2 = OP1_DATA_INFO();
+ } 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, opline->op1_type, 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;
+ }
+ }
+
+ tmp |= binary_op_result_type(
+ ssa, opline->extended_value, t1, t2,
+ opline->opcode == ZEND_ASSIGN_OP ? ssa_op->op1_def : -1, optimization_level);
+ if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+
+ 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_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
+ if (opline->op1_type == IS_CV) {
+ orig = (orig & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
+ UPDATE_SSA_TYPE(orig, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP) {
+ /* Nothing to do */
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ 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. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
+ /* Arrays and objects cannot be used as keys. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
+ /* null and false are implicitly converted to array, anything else
+ * results in a null return value. */
+ tmp |= MAY_BE_NULL;
+ }
+ } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
+ /* 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_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
+ }
+ }
+ break;
+ }
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ if (ssa_op->result_def >= 0) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_op->op1_use].has_range ||
+ (opline->opcode == ZEND_PRE_DEC &&
+ (ssa_var_info[ssa_op->op1_use].range.underflow ||
+ ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_PRE_INC &&
+ (ssa_var_info[ssa_op->op1_use].range.overflow ||
+ ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_PRE_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_op->op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (ssa_op->result_def >= 0) {
+ tmp = 0;
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1|MAY_BE_RCN;
+ }
+ tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_op->op1_use].has_range ||
+ (opline->opcode == ZEND_POST_DEC &&
+ (ssa_var_info[ssa_op->op1_use].range.underflow ||
+ ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_POST_INC &&
+ (ssa_var_info[ssa_op->op1_use].range.overflow ||
+ ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_POST_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_op->op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ if (opline->op1_type == IS_CV) {
+ tmp = assign_dim_result_type(t1, t2, OP1_DATA_INFO(), opline->op2_type);
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ tmp = 0;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING;
+ }
+ if (t1 & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_STRING)) {
+ tmp |= (OP1_DATA_INFO() & (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+
+ if (opline->op2_type == IS_UNUSED) {
+ /* When appending to an array and the LONG_MAX key is already used
+ * null will be returned. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
+ /* Arrays and objects cannot be used as keys. */
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
+ /* undef, null and false are implicitly converted to array, anything else
+ * results in a null return value. */
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ if ((ssa_op+1)->op1_def >= 0) {
+ opline++;
+ ssa_op++;
+ tmp = OP1_INFO();
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_OBJ:
+ if (opline->op1_type == IS_CV) {
+ tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ // TODO: If there is no __set we might do better
+ tmp = zend_fetch_prop_type(script,
+ zend_fetch_prop_info(op_array, ssa, opline, ssa_op), &ce);
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
+ }
+ }
+ if ((ssa_op+1)->op1_def >= 0) {
+ opline++;
+ ssa_op++;
+ tmp = OP1_INFO();
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP:
+ if (ssa_op->result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ if ((ssa_op+1)->op1_def >= 0) {
+ opline++;
+ ssa_op++;
+ tmp = OP1_INFO();
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ if (opline->op1_type == IS_CV) {
+ tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ // TODO: ???
+ tmp = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (ssa_op->op2_def >= 0) {
+ tmp = t2;
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
+ }
+ tmp = t2 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t2 & MAY_BE_REF) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ tmp |= t2 & (MAY_BE_RC1|MAY_BE_RCN);
+ } else if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (ssa_op->op1_def >= 0) {
+ if (ssa_var_info[ssa_op->op1_def].use_as_double) {
+ tmp &= ~MAY_BE_LONG;
+ tmp |= MAY_BE_DOUBLE;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp & ~MAY_BE_REF, ssa_op->result_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def);
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+// TODO: ???
+ if (opline->op2_type == IS_CV) {
+ 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_op->op2_def);
+ }
+ if (opline->op2_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_RC1|MAY_BE_RCN);
+ }
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ if (ssa_op->result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ case ZEND_ASSIGN_OBJ_REF:
+ if (opline->op1_type == IS_CV) {
+ tmp = t1;
+ if (tmp & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->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_RC1|MAY_BE_RCN);
+ }
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (ssa_op->result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ if ((opline+1)->op1_type == IS_CV) {
+ opline++;
+ ssa_op++;
+ 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_op->op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP_REF:
+ if ((opline+1)->op1_type == IS_CV) {
+ opline++;
+ ssa_op++;
+ UPDATE_SSA_TYPE(MAY_BE_REF, ssa_op->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;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ break;
+ 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_op->op1_def);
+ break;
+ case ZEND_SEND_VAR:
+ if (ssa_op->op1_def >= 0) {
+ tmp = t1;
+ if (t1 & (MAY_BE_RC1|MAY_BE_REF)) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_BIND_LEXICAL:
+ if (ssa_op->op2_def >= 0) {
+ if (opline->extended_value & ZEND_BIND_REF) {
+ tmp = t2 | MAY_BE_REF;
+ } else {
+ tmp = t2 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op2_def);
+ }
+ break;
+ case ZEND_YIELD:
+ if (ssa_op->op1_def >= 0) {
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ tmp = t1 | MAY_BE_REF;
+ } else {
+ tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | MAY_BE_RC1 | MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ if (ssa_op->op1_def >= 0) {
+ tmp = (t1 & MAY_BE_UNDEF)|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;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_SEND_REF:
+ if (ssa_op->op1_def >= 0) {
+ 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;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_SEND_UNPACK:
+ if (ssa_op->op1_def >= 0) {
+ tmp = t1;
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (t1 & MAY_BE_ARRAY_OF_ANY) {
+ /* SEND_UNPACK may acquire references into the array */
+ tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_FAST_CONCAT:
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def);
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ case ZEND_RECV_VARIADIC:
+ {
+ /* Typehinting */
+ zend_arg_info *arg_info = &op_array->arg_info[opline->op1.num-1];
+
+ ce = NULL;
+ tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
+ if (ZEND_ARG_SEND_MODE(arg_info)) {
+ tmp |= MAY_BE_REF;
+ }
+
+ if (opline->opcode == ZEND_RECV_VARIADIC) {
+ uint32_t elem_type = tmp & MAY_BE_REF
+ ? MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF
+ : (tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|elem_type;
+ ce = NULL;
+ }
+
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ break;
+ }
+ case ZEND_DECLARE_ANON_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def);
+ if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT(opline->op1)))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def);
+ if (opline->op2_type == IS_UNUSED) {
+ switch (opline->op1.num & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ if (op_array->scope) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ default:
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ break;
+ }
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT(opline->op2);
+ if (Z_TYPE_P(zv) == IS_STRING) {
+ ce = get_class_entry(script, Z_STR_P(zv+1));
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ } else {
+ COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def);
+ }
+ break;
+ case ZEND_NEW:
+ tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT;
+ if (opline->op1_type == IS_CONST &&
+ (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
+ } else if ((t1 & MAY_BE_CLASS) && ssa_op->op1_use >= 0 && ssa_var_info[ssa_op->op1_use].ce) {
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_op->op1_use].ce, ssa_var_info[ssa_op->op1_use].is_instanceof, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_CLONE:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
+ break;
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ if (ssa_op->op1_def >= 0) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if (t1 & MAY_BE_REF) {
+ tmp = (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | t1);
+ } else {
+ tmp = t1;
+ if (t1 & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ if (ssa_op->result_def >= 0) {
+ tmp = MAY_BE_RC1|MAY_BE_ARRAY;
+ if (ssa_op->result_use >= 0) {
+ tmp |= ssa_var_info[ssa_op->result_use].type;
+ }
+ if (opline->op1_type != IS_UNUSED) {
+ tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_ARRAY_OF_NULL;
+ }
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ 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|MAY_BE_RESOURCE)) {
+ 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
+ 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_op->result_def);
+ }
+ break;
+ case ZEND_ADD_ARRAY_UNPACK:
+ tmp = ssa_var_info[ssa_op->result_use].type;
+ ZEND_ASSERT(tmp & MAY_BE_ARRAY);
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_UNSET_CV:
+ tmp = MAY_BE_UNDEF;
+ if (!op_array->function_name) {
+ /* In global scope, we know nothing */
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa_op->op1_def >= 0) {
+ UPDATE_SSA_TYPE(t1, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ if (ssa_op->op1_def >= 0) {
+ tmp = t1;
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+ tmp |= MAY_BE_REF;
+ } else if (t1 & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+ tmp = MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT));
+ } else {
+ tmp = MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+ }
+ /* The result is set to UNDEF for invalid foreach inputs. */
+ if ((t1 & (MAY_BE_ANY | MAY_BE_UNDEF)) & ~(MAY_BE_ARRAY | MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_UNDEF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ tmp = t2 & MAY_BE_REF;
+ if (t1 & MAY_BE_OBJECT) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp |= MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
+ if (ssa_op->result_def >= 0) {
+ tmp = (ssa_op->result_use >= 0) ? RES_USE_INFO() : 0;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (t1 & MAY_BE_ARRAY_KEY_LONG) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (t1 & MAY_BE_ARRAY_KEY_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_LIST_R:
+ case ZEND_FETCH_LIST_W:
+ if (ssa_op->op1_def >= 0) {
+ uint32_t key_type = 0;
+ tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
+ if (opline->opcode == ZEND_FETCH_DIM_W ||
+ opline->opcode == ZEND_FETCH_DIM_RW ||
+ opline->opcode == ZEND_FETCH_DIM_FUNC_ARG ||
+ opline->opcode == ZEND_FETCH_LIST_W) {
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+ tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
+ }
+ tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_STRING|MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_RC1;
+ if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+ tmp |= t1 & MAY_BE_RCN;
+ }
+ }
+ if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN);
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ key_type |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ key_type |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & MAY_BE_STRING) {
+ key_type |= MAY_BE_ARRAY_KEY_STRING;
+ if (opline->op2_type != IS_CONST) {
+ // FIXME: numeric string
+ key_type |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ key_type |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ } else if (opline->opcode == ZEND_FETCH_DIM_UNSET) {
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN);
+ }
+ }
+ 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_op->result_def].use_chain;
+ while (j >= 0) {
+ zend_uchar opcode;
+
+ if (!ssa_opcodes) {
+ ZEND_ASSERT(j == (opline - op_array->opcodes) + 1 && "Use must be in next opline");
+ opcode = op_array->opcodes[j].opcode;
+ } else {
+ ZEND_ASSERT(ssa_opcodes[j] == opline + 1 && "Use must be in next opline");
+ opcode = ssa_opcodes[j]->opcode;
+ }
+ switch (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 |= key_type | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY;
+ 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 |= key_type | 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 |= key_type | MAY_BE_ARRAY_OF_DOUBLE;
+ } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) {
+ tmp |= key_type | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE;
+ }
+ 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:
+ /* These will result in an error exception, unless the element
+ * is already an object. */
+ 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_op->result_def, j);
+ ZEND_ASSERT(j < 0 && "There should only be one use");
+ }
+ }
+ if ((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ } else {
+ /* invalid key type */
+ tmp = (tmp & (MAY_BE_RC1|MAY_BE_RCN)) | (t1 & ~(MAY_BE_RC1|MAY_BE_RCN));
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
+ }
+ /* FETCH_LIST on a string behaves like FETCH_R on null */
+ tmp = zend_array_element_type(
+ opline->opcode != ZEND_FETCH_LIST_R ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
+ opline->op1_type,
+ opline->result_type == IS_VAR,
+ opline->op2_type == IS_UNUSED);
+ if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_FETCH_THIS:
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_op->result_def);
+ UPDATE_SSA_TYPE(MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def);
+ break;
+ case ZEND_FETCH_OBJ_R:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ if (ssa_op->result_def >= 0) {
+ zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
+
+ tmp = zend_fetch_prop_type(script, prop_info, &ce);
+ if (opline->result_type != IS_TMP_VAR) {
+ tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
+ } else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
+ zend_class_entry *ce = NULL;
+
+ if (opline->op1_type == IS_UNUSED) {
+ ce = op_array->scope;
+ } else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
+ ce = ssa->var_info[ssa_op->op1_use].ce;
+ }
+ if (prop_info) {
+ /* FETCH_OBJ_R/IS for plain property increments reference counter,
+ so it can't be 1 */
+ if (ce && !ce->create_object) {
+ tmp &= ~MAY_BE_RC1;
+ }
+ } else {
+ if (ce && !ce->create_object && !ce->__get) {
+ tmp &= ~MAY_BE_RC1;
+ }
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->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_INDIRECT;
+ } else {
+ tmp &= ~MAY_BE_RC1;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa_op->result_def >= 0) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+
+ if (!func_info || !func_info->call_map) {
+ goto unknown_opcode;
+ }
+ call_info = func_info->call_map[opline - op_array->opcodes];
+ if (!call_info) {
+ goto unknown_opcode;
+ }
+
+ zend_class_entry *ce;
+ bool ce_is_instanceof;
+ tmp = zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof);
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, ce_is_instanceof, ssa_op->result_def);
+ }
+ }
+ break;
+ case ZEND_FETCH_CONSTANT:
+ case ZEND_FETCH_CLASS_CONSTANT:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
+ break;
+ case ZEND_STRLEN:
+ tmp = MAY_BE_LONG;
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ break;
+ case ZEND_COUNT:
+ case ZEND_FUNC_NUM_ARGS:
+ UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
+ break;
+ case ZEND_FUNC_GET_ARGS:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
+ break;
+ case ZEND_GET_CLASS:
+ case ZEND_GET_CALLED_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_RCN, ssa_op->result_def);
+ break;
+ case ZEND_GET_TYPE:
+ UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def);
+ break;
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def);
+ break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (t1 & MAY_BE_REF) {
+ tmp = t1;
+ ce = NULL;
+ } else {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ tmp = zend_fetch_arg_info_type(script, ret_info, &ce);
+ }
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->op1_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->op1_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
+ }
+ }
+ break;
+ case ZEND_MAKE_REF:
+ 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;
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ if (ssa_op->op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ break;
+ case ZEND_CATCH:
+ /* Forbidden opcodes */
+ ZEND_UNREACHABLE();
+ break;
+ default:
+unknown_opcode:
+ if (ssa_op->op1_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
+ }
+ if (ssa_op->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) {
+ if (opline->opcode == ZEND_FETCH_R || opline->opcode == ZEND_FETCH_IS) {
+ tmp |= MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ switch (opline->opcode) {
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_RW:
+ case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_UNSET:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ tmp |= MAY_BE_INDIRECT;
+ break;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
+ }
+ break;
+ }
+
+ return SUCCESS;
+}
+
+ZEND_API int zend_update_type_info(
+ const zend_op_array *op_array,
+ zend_ssa *ssa,
+ const zend_script *script,
+ zend_op *opline,
+ zend_ssa_op *ssa_op,
+ const zend_op **ssa_opcodes,
+ zend_long optimization_level)
+{
+ return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0);
+}
+
+static uint32_t get_class_entry_rank(zend_class_entry *ce) {
+ uint32_t rank = 0;
+ if (ce->ce_flags & ZEND_ACC_LINKED) {
+ while (ce->parent) {
+ rank++;
+ ce = ce->parent;
+ }
+ }
+ return rank;
+}
+
+/* Compute least common ancestor on class inheritance tree only */
+static zend_class_entry *join_class_entries(
+ zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
+ uint32_t rank1, rank2;
+ if (ce1 == ce2) {
+ return ce1;
+ }
+ if (!ce1 || !ce2) {
+ return NULL;
+ }
+
+ rank1 = get_class_entry_rank(ce1);
+ rank2 = get_class_entry_rank(ce2);
+
+ while (rank1 != rank2) {
+ if (rank1 > rank2) {
+ ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
+ rank1--;
+ } else {
+ ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
+ rank2--;
+ }
+ }
+
+ while (ce1 != ce2) {
+ ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
+ ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
+ }
+
+ if (ce1) {
+ *is_instanceof = 1;
+ }
+ return ce1;
+}
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ int i, j;
+ uint32_t tmp, worklist_len = zend_bitset_len(ssa_vars_count);
+ bool update_worklist = 1;
+
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ j = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+ if (ssa_vars[j].definition_phi) {
+ zend_ssa_phi *p = ssa_vars[j].definition_phi;
+ if (p->pi >= 0) {
+ zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
+ int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
+ tmp = get_ssa_var_info(ssa, p->sources[0]);
+
+ 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;
+ is_instanceof = 1;
+ } else if (is_instanceof && instanceof_function(constraint->ce, ce)) {
+ ce = constraint->ce;
+ } else {
+ /* Ignore the constraint (either ce instanceof constraint->ce or
+ * they are unrelated, as far as we can statically determine) */
+ }
+ }
+ }
+
+ UPDATE_SSA_TYPE(tmp, j);
+ UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
+ } else {
+ int first = 1;
+ int is_instanceof = 0;
+ zend_class_entry *ce = NULL;
+
+ tmp = 0;
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ tmp |= get_ssa_var_info(ssa, p->sources[i]);
+ }
+ UPDATE_SSA_TYPE(tmp, j);
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ zend_ssa_var_info *info;
+
+ ZEND_ASSERT(p->sources[i] >= 0);
+ info = &ssa_var_info[p->sources[i]];
+ if (info->type & MAY_BE_OBJECT) {
+ if (first) {
+ ce = info->ce;
+ is_instanceof = info->is_instanceof;
+ first = 0;
+ } else {
+ is_instanceof |= info->is_instanceof;
+ ce = join_class_entries(ce, info->ce, &is_instanceof);
+ }
+ }
+ }
+ UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j);
+ }
+ } else if (ssa_vars[j].definition >= 0) {
+ i = ssa_vars[j].definition;
+ if (_zend_update_type_info(op_array, ssa, script, worklist, op_array->opcodes + i, ssa->ops + i, NULL, optimization_level, 1) == FAILURE) {
+ return FAILURE;
+ }
+ }
+ }
+ return SUCCESS;
+}
+
+static bool is_narrowable_instr(zend_op *opline) {
+ return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV;
+}
+
+static bool is_effective_op1_double_cast(zend_op *opline, zval *op2) {
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1)
+ || (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1);
+}
+static bool is_effective_op2_double_cast(zend_op *opline, zval *op1) {
+ /* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int,
+ * so allowing SUB here is fine. */
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op1) == 1);
+}
+
+/* This function recursively checks whether it's possible to convert an integer variable
+ * initialization to a double initialization. The basic idea is that if the value is used
+ * only in add/sub/mul/div ("narrowable" instructions) with a double result value, then it
+ * will be cast to double at that point anyway, so we may as well do it earlier already.
+ *
+ * The tricky case are chains of operations, where it's not necessarily a given that converting
+ * an integer to double before the chain of operations is the same as converting it after the
+ * chain. What this function does is detect two cases where it is safe:
+ * * If the operations only involve constants, then we can simply verify that performing the
+ * calculation on integers and doubles yields the same value.
+ * * Even if one operand is not known, we may be able to determine that the operations with the
+ * integer replaced by a double only acts as an effective double cast on the unknown operand.
+ * E.g. 0+$i and 0.0+$i only differ by that cast. If then the consuming instruction of this
+ * result will perform a double cast anyway, the conversion is safe.
+ *
+ * The checks happens recursively, while keeping track of which variables are already visisted to
+ * avoid infinite loops. An iterative, worklist driven approach would be possible, but the state
+ * management more cumbersome to implement, so we don't bother for now.
+ */
+static bool can_convert_to_double(
+ const zend_op_array *op_array, zend_ssa *ssa, int var_num,
+ zval *value, zend_bitset visited) {
+ zend_ssa_var *var = &ssa->vars[var_num];
+ zend_ssa_phi *phi;
+ int use;
+ uint32_t type;
+
+ if (zend_bitset_in(visited, var_num)) {
+ return 1;
+ }
+ zend_bitset_incl(visited, var_num);
+
+ for (use = var->use_chain; use >= 0; use = zend_ssa_next_use(ssa->ops, var_num, use)) {
+ zend_op *opline = &op_array->opcodes[use];
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+
+ if (zend_ssa_is_no_val_use(opline, ssa_op, var_num)) {
+ continue;
+ }
+
+ if (!is_narrowable_instr(opline)) {
+ return 0;
+ }
+
+ /* Instruction always returns double, the conversion is certainly fine */
+ type = ssa->var_info[ssa_op->result_def].type;
+ if ((type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+ continue;
+ }
+
+ /* UNDEF signals that the previous result is an effective double cast, this is only allowed
+ * if this instruction would have done the cast anyway (previous check). */
+ if (Z_ISUNDEF_P(value)) {
+ return 0;
+ }
+
+ /* Check that narrowing can actually be useful */
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ {
+ /* For calculation on original values */
+ zval orig_op1, orig_op2, orig_result;
+ /* For calculation with var_num cast to double */
+ zval dval_op1, dval_op2, dval_result;
+
+ ZVAL_UNDEF(&orig_op1);
+ ZVAL_UNDEF(&dval_op1);
+ if (ssa_op->op1_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op1, value);
+ ZVAL_DOUBLE(&dval_op1, (double) Z_LVAL_P(value));
+ } else if (opline->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT(opline->op1);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op1, zv);
+ ZVAL_COPY_VALUE(&dval_op1, zv);
+ }
+ }
+
+ ZVAL_UNDEF(&orig_op2);
+ ZVAL_UNDEF(&dval_op2);
+ if (ssa_op->op2_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op2, value);
+ ZVAL_DOUBLE(&dval_op2, (double) Z_LVAL_P(value));
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT(opline->op2);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op2, zv);
+ ZVAL_COPY_VALUE(&dval_op2, zv);
+ }
+ }
+
+ ZEND_ASSERT(!Z_ISUNDEF(orig_op1) || !Z_ISUNDEF(orig_op2));
+ if (Z_ISUNDEF(orig_op1)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op2) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op1_double_cast(opline, &orig_op2)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else if (Z_ISUNDEF(orig_op2)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op1) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op2_double_cast(opline, &orig_op1)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else {
+ zend_uchar opcode = opline->opcode;
+
+ if (opcode == ZEND_ASSIGN_OP) {
+ opcode = opline->extended_value;
+ }
+
+ /* Avoid division by zero */
+ if (opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) {
+ return 0;
+ }
+
+ 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;
+ }
+ }
+
+ if (!can_convert_to_double(op_array, ssa, ssa_op->result_def, &orig_result, visited)) {
+ return 0;
+ }
+ }
+ }
+
+ for (phi = var->phi_use_chain; phi; phi = zend_ssa_next_use_phi(ssa, var_num, phi)) {
+ /* Check that narrowing can actually be useful */
+ type = ssa->var_info[phi->ssa_var].type;
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ if (!can_convert_to_double(op_array, ssa, phi->ssa_var, value, visited)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level)
+{
+ uint32_t bitset_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset visited, worklist;
+ int i, v;
+ zend_op *opline;
+ bool narrowed = 0;
+ ALLOCA_FLAG(use_heap)
+
+ visited = ZEND_BITSET_ALLOCA(2 * bitset_len, use_heap);
+ worklist = visited + bitset_len;
+
+ zend_bitset_clear(worklist, bitset_len);
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+ if ((ssa->var_info[v].type & (MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF)) != MAY_BE_LONG) continue;
+ if (ssa->vars[v].definition < 0) continue;
+ if (ssa->vars[v].no_val) continue;
+ opline = op_array->opcodes + ssa->vars[v].definition;
+ /* Go through assignments of literal integers and check if they can be converted to
+ * doubles instead, in the hope that we'll narrow long|double to double. */
+ if (opline->opcode == ZEND_ASSIGN && opline->result_type == IS_UNUSED &&
+ opline->op1_type == IS_CV && opline->op2_type == IS_CONST) {
+ zval *value = CRT_CONSTANT(opline->op2);
+
+ zend_bitset_clear(visited, bitset_len);
+ if (can_convert_to_double(op_array, ssa, v, value, visited)) {
+ narrowed = 1;
+ ssa->var_info[v].use_as_double = 1;
+ /* The "visited" vars are exactly those which may change their type due to
+ * narrowing. Reset their types and add them to the type inference worklist */
+ ZEND_BITSET_FOREACH(visited, bitset_len, i) {
+ ssa->var_info[i].type &= ~MAY_BE_ANY;
+ } ZEND_BITSET_FOREACH_END();
+ zend_bitset_union(worklist, visited, bitset_len);
+ }
+ }
+ }
+
+ if (!narrowed) {
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) {
+ free_alloca(visited, use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+}
+
+static int is_recursive_tail_call(const zend_op_array *op_array,
+ zend_op *opline)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+
+ if (info->ssa.ops && info->ssa.vars && info->call_map &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) {
+
+ zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
+
+ if (op->opcode == ZEND_DO_UCALL) {
+ zend_call_info *call_info = info->call_map[op - op_array->opcodes];
+ if (call_info && op_array == &call_info->callee_func->op_array) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+ZEND_API void zend_init_func_return_info(
+ const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret)
+{
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+
+ ret->type = zend_fetch_arg_info_type(script, ret_info, &ret->ce);
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret->type |= MAY_BE_REF;
+ }
+ ret->is_instanceof = (ret->ce) ? 1 : 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ }
+}
+
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_ssa *ssa = &info->ssa;
+ int blocks_count = info->ssa.cfg.blocks_count;
+ zend_basic_block *blocks = info->ssa.cfg.blocks;
+ int j;
+ uint32_t t1;
+ uint32_t tmp = 0;
+ zend_class_entry *tmp_ce = NULL;
+ int tmp_is_instanceof = -1;
+ zend_class_entry *arg_ce;
+ int arg_is_instanceof;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+ int tmp_has_range = -1;
+
+ if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
+ ret->type = MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ ret->ce = zend_ce_generator;
+ ret->is_instanceof = 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ return;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) {
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+
+ if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
+ zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
+ if (!recursive && ssa_op && info->ssa.var_info &&
+ ssa_op->op1_use >= 0 &&
+ info->ssa.var_info[ssa_op->op1_use].recursive) {
+ continue;
+ }
+ if (is_recursive_tail_call(op_array, opline)) {
+ continue;
+ }
+ t1 = OP1_INFO();
+ if (t1 & MAY_BE_UNDEF) {
+ t1 |= MAY_BE_NULL;
+ }
+ if (opline->opcode == ZEND_RETURN) {
+ if (t1 & MAY_BE_RC1) {
+ t1 |= MAY_BE_RCN;
+ }
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF);
+ } else {
+ t1 |= MAY_BE_REF;
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
+ }
+ tmp |= t1;
+
+ if (ssa_op && info->ssa.var_info &&
+ ssa_op->op1_use >= 0 &&
+ info->ssa.var_info[ssa_op->op1_use].ce) {
+ arg_ce = info->ssa.var_info[ssa_op->op1_use].ce;
+ arg_is_instanceof = info->ssa.var_info[ssa_op->op1_use].is_instanceof;
+ } else {
+ arg_ce = NULL;
+ arg_is_instanceof = 0;
+ }
+
+ if (tmp_is_instanceof < 0) {
+ tmp_ce = arg_ce;
+ tmp_is_instanceof = arg_is_instanceof;
+ } else if (arg_ce && arg_ce == tmp_ce) {
+ if (tmp_is_instanceof != arg_is_instanceof) {
+ tmp_is_instanceof = 1;
+ }
+ } else {
+ tmp_ce = NULL;
+ tmp_is_instanceof = 0;
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT(opline->op1);
+
+ if (Z_TYPE_P(zv) == IS_NULL) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 1;
+ tmp_range.max = 1;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 1);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 1);
+ }
+ }
+ } else if (Z_TYPE_P(zv) == IS_LONG) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = Z_LVAL_P(zv);
+ tmp_range.max = Z_LVAL_P(zv);
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(zv));
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(zv));
+ }
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ } else if (ssa_op && info->ssa.var_info && ssa_op->op1_use >= 0) {
+ if (info->ssa.var_info[ssa_op->op1_use].has_range) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range = info->ssa.var_info[ssa_op->op1_use].range;
+ } else if (tmp_has_range) {
+ /* union */
+ if (info->ssa.var_info[ssa_op->op1_use].range.underflow) {
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ } else {
+ tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[ssa_op->op1_use].range.min);
+ }
+ if (info->ssa.var_info[ssa_op->op1_use].range.overflow) {
+ tmp_range.overflow = 1;
+ tmp_range.max = ZEND_LONG_MAX;
+ } else {
+ tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[ssa_op->op1_use].range.max);
+ }
+ }
+ } else if (!widening) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ tmp_range.max = ZEND_LONG_MAX;
+ tmp_range.overflow = 1;
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ }
+ }
+ }
+
+ if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+ if (tmp_is_instanceof < 0) {
+ tmp_is_instanceof = 0;
+ tmp_ce = NULL;
+ }
+ if (tmp_has_range < 0) {
+ tmp_has_range = 0;
+ }
+ ret->type = tmp;
+ ret->ce = tmp_ce;
+ ret->is_instanceof = tmp_is_instanceof;
+ }
+ ret->range = tmp_range;
+ ret->has_range = tmp_has_range;
+}
+
+static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level)
+{
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ int j;
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ /* Type Inference */
+ for (j = op_array->last_var; j < ssa_vars_count; j++) {
+ zend_bitset_incl(worklist, j);
+ ssa_var_info[j].type = 0;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) {
+ free_alloca(worklist, use_heap);
+ return FAILURE;
+ }
+
+ /* Narrowing integer initialization to doubles */
+ zend_type_narrowing(op_array, script, ssa, optimization_level);
+
+ if (ZEND_FUNC_INFO(op_array)) {
+ zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+ }
+
+ free_alloca(worklist, use_heap);
+ return SUCCESS;
+}
+
+ZEND_API int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) /* {{{ */
+{
+ zend_ssa_var_info *ssa_var_info;
+ int i;
+
+ if (!ssa->var_info) {
+ ssa->var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var_info));
+ }
+ ssa_var_info = ssa->var_info;
+
+ if (!op_array->function_name) {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ ssa_var_info[i].has_range = 0;
+ }
+ } else {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF;
+ ssa_var_info[i].has_range = 0;
+ if (ssa->vars[i].alias) {
+ ssa_var_info[i].type |= get_ssa_alias_types(ssa->vars[i].alias);
+ }
+ }
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_var_info[i].type = 0;
+ ssa_var_info[i].has_range = 0;
+ }
+
+ if (zend_infer_ranges(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_infer_types(op_array, script, ssa, optimization_level) != SUCCESS) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+ zend_bitset worklist;
+ int worklist_len, i;
+ ALLOCA_FLAG(use_heap);
+
+ if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) {
+ return;
+ }
+ worklist_len = zend_bitset_len(info->ssa.vars_count);
+ worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ call_info = info->callee_info;
+ while (call_info) {
+ if (call_info->recursive && call_info->caller_call_opline &&
+ info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) {
+ zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def);
+ }
+ call_info = call_info->next_callee;
+ }
+ WHILE_WORKLIST(worklist, worklist_len, i) {
+ if (!info->ssa.var_info[i].recursive) {
+ info->ssa.var_info[i].recursive = 1;
+ add_usages(op_array, &info->ssa, worklist, i);
+ }
+ } WHILE_WORKLIST_END();
+ free_alloca(worklist, use_heap);
+}
+
+ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2)
+{
+ if (opline->op1_type == IS_CV) {
+ if (t1 & MAY_BE_UNDEF) {
+ switch (opline->opcode) {
+ case ZEND_UNSET_VAR:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ return 1;
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ASSIGN:
+ 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:
+ case ZEND_UNSET_CV:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_MAKE_REF:
+ case ZEND_FETCH_DIM_W:
+ break;
+ default:
+ /* undefined variable warning */
+ return 1;
+ }
+ }
+ } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ if ((t1 & MAY_BE_RC1)
+ && (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:
+ case ZEND_CASE_STRICT:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_FETCH_LIST_R:
+ case ZEND_QM_ASSIGN:
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEPARATE:
+ case ZEND_END_SILENCE:
+ case ZEND_MAKE_REF:
+ break;
+ default:
+ /* destructor may be called */
+ return 1;
+ }
+ }
+ }
+
+ if (opline->op2_type == IS_CV) {
+ if (t2 & MAY_BE_UNDEF) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_REF:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ break;
+ default:
+ /* undefined variable warning */
+ return 1;
+ }
+ }
+ } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ if ((t2 & MAY_BE_RC1)
+ && (t2 & (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_ASSIGN:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ break;
+ default:
+ /* destructor may be called */
+ return 1;
+ }
+ }
+ }
+
+ switch (opline->opcode) {
+ case ZEND_NOP:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP:
+ case ZEND_CHECK_VAR:
+ case ZEND_MAKE_REF:
+ case ZEND_BEGIN_SILENCE:
+ case ZEND_END_SILENCE:
+ case ZEND_FREE:
+ case ZEND_FE_FREE:
+ case ZEND_SEPARATE:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_ISSET_ISEMPTY_THIS:
+ case ZEND_COALESCE:
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_FUNC_NUM_ARGS:
+ case ZEND_FUNC_GET_ARGS:
+ case ZEND_COPY_TMP:
+ case ZEND_CASE_STRICT:
+ case ZEND_JMP_NULL:
+ return 0;
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_CHECK_FUNC_ARG:
+ /* May throw for named params. */
+ return opline->op2_type == IS_CONST;
+ case ZEND_INIT_FCALL:
+ /* can't throw, because call is resolved at compile time */
+ return 0;
+ case ZEND_BIND_GLOBAL:
+ if ((opline+1)->opcode == ZEND_BIND_GLOBAL) {
+ return zend_may_throw(opline + 1, ssa_op + 1, op_array, ssa);
+ }
+ return 0;
+ case 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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_DIV:
+ case ZEND_MOD:
+ if (!OP2_HAS_RANGE() ||
+ (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) {
+ /* Division by zero */
+ return 1;
+ }
+ /* break missing intentionally */
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_POW:
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_SL:
+ case ZEND_SR:
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ !OP2_HAS_RANGE() ||
+ OP2_MIN_RANGE() < 0;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case 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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_BW_NOT:
+ return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_PRE_INC:
+ case ZEND_POST_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_DEC:
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_BOOL_NOT:
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_BOOL:
+ case ZEND_JMP_SET:
+ return (t1 & MAY_BE_OBJECT);
+ case ZEND_BOOL_XOR:
+ return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT);
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_SPACESHIP:
+ if ((t1 & MAY_BE_ANY) == MAY_BE_NULL
+ || (t2 & MAY_BE_ANY) == MAY_BE_NULL) {
+ 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_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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ } 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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ } 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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ } else if (opline->extended_value == ZEND_SL ||
+ opline->extended_value == ZEND_SR) {
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ !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|MAY_BE_RESOURCE)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ }
+ return 1;
+ case ZEND_ASSIGN:
+ if (t1 & MAY_BE_REF) {
+ return 1;
+ }
+ 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:
+ if ((opline+1)->op1_type == IS_CV) {
+ if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) {
+ return 1;
+ }
+ }
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
+ (t2 & (MAY_BE_UNDEF|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_ASSIGN_OBJ:
+ if (t1 & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_OBJECT))) {
+ return 1;
+ }
+ if (ssa_op->op1_use) {
+ zend_ssa_var_info *var_info = ssa->var_info + ssa_op->op1_use;
+ zend_class_entry *ce = var_info->ce;
+
+ if (var_info->is_instanceof ||
+ !ce || ce->create_object || ce->__get || ce->__set || ce->parent) {
+ return 1;
+ }
+
+ if (op_array->scope != ce && ce->default_properties_count) {
+ zend_property_info *prop_info;
+
+ if (opline->op2_type == IS_CONST) {
+ prop_info = zend_hash_find_ptr(&ce->properties_info,
+ Z_STR_P(CRT_CONSTANT(opline->op2)));
+ if (prop_info && !(prop_info->flags & ZEND_ACC_PUBLIC)) {
+ return 1;
+ }
+ } else {
+ if (t2 & (MAY_BE_ANY-MAY_BE_STRING)) {
+ return 1;
+ }
+ ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
+ if (!(prop_info->flags & ZEND_ACC_PUBLIC)) {
+ return 1;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ }
+ return 0;
+ }
+ return 1;
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ return t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT);
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ return (opline->op2_type != IS_UNUSED) && (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_STRLEN:
+ return (t1 & MAY_BE_ANY) != MAY_BE_STRING;
+ case ZEND_COUNT:
+ return (t1 & MAY_BE_ANY) != MAY_BE_ARRAY;
+ case ZEND_RECV_INIT:
+ if (Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_CONSTANT_AST) {
+ return 1;
+ }
+ if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
+ uint32_t arg_num = opline->op1.num;
+ zend_arg_info *cur_arg_info;
+
+ if (EXPECTED(arg_num <= op_array->num_args)) {
+ cur_arg_info = &op_array->arg_info[arg_num-1];
+ } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
+ cur_arg_info = &op_array->arg_info[op_array->num_args];
+ } else {
+ return 0;
+ }
+ return ZEND_TYPE_IS_SET(cur_arg_info->type);
+ } else {
+ return 0;
+ }
+ case ZEND_FETCH_IS:
+ return (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_FETCH_DIM_IS:
+ return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_CAST:
+ switch (opline->extended_value) {
+ case IS_LONG:
+ case IS_DOUBLE:
+ return (t1 & MAY_BE_OBJECT);
+ case IS_STRING:
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case IS_ARRAY:
+ return (t1 & MAY_BE_OBJECT);
+ case IS_OBJECT:
+ return 0;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ case ZEND_ARRAY_KEY_EXISTS:
+ if ((t2 & MAY_BE_ANY) != MAY_BE_ARRAY) {
+ return 1;
+ }
+ if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+ return 1;
+ }
+ return 0;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
+ return 1;
+ }
+ return 0;
+ case ZEND_FE_FETCH_R:
+ if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
+ return 1;
+ }
+ if (opline->op2_type == IS_CV
+ && (t2 & MAY_BE_RC1)
+ && (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
+ return 1;
+ }
+ return 0;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_LIST_W:
+ if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+ return 1;
+ }
+ if (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+ return 1;
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ return 1;
+ }
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
+{
+ return zend_may_throw_ex(opline, ssa_op, op_array, ssa, OP1_INFO(), OP2_INFO());
+}
diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h
new file mode 100644
index 0000000000..8390946012
--- /dev/null
+++ b/Zend/Optimizer/zend_inference.h
@@ -0,0 +1,288 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_INFERENCE_H
+#define ZEND_INFERENCE_H
+
+#include "zend_optimizer.h"
+#include "zend_ssa.h"
+#include "zend_bitset.h"
+
+/* Bitmask for type inference (zend_ssa_var_info.type) */
+#include "zend_type_info.h"
+
+#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
+#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
+#define MAY_BE_GUARD (1<<28) /* needs type guard */
+//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
+
+//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
+#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
+#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
+
+#define MAY_HAVE_DTOR \
+ (MAY_BE_OBJECT|MAY_BE_RESOURCE \
+ |MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
+
+#define DEFINE_SSA_OP_HAS_RANGE(opN) \
+ static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT(opline->opN); \
+ return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
+ } else { \
+ return (opline->opN##_type != IS_UNUSED && \
+ ssa->var_info && \
+ ssa_op->opN##_use >= 0 && \
+ ssa->var_info[ssa_op->opN##_use].has_range); \
+ } \
+ return 0; \
+ } \
+
+#define DEFINE_SSA_OP_MIN_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT(opline->opN); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->var_info && \
+ ssa_op->opN##_use >= 0 && \
+ ssa->var_info[ssa_op->opN##_use].has_range) { \
+ return ssa->var_info[ssa_op->opN##_use].range.min; \
+ } \
+ return ZEND_LONG_MIN; \
+ } \
+
+#define DEFINE_SSA_OP_MAX_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT(opline->opN); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->var_info && \
+ ssa_op->opN##_use >= 0 && \
+ ssa->var_info[ssa_op->opN##_use].has_range) { \
+ return ssa->var_info[ssa_op->opN##_use].range.max; \
+ } \
+ return ZEND_LONG_MAX; \
+ } \
+
+#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT(opline->opN); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->var_info && \
+ ssa_op->opN##_use >= 0 && \
+ ssa->var_info[ssa_op->opN##_use].has_range) { \
+ return ssa->var_info[ssa_op->opN##_use].range.underflow; \
+ } \
+ return 1; \
+ } \
+
+#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT(opline->opN); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->var_info && \
+ ssa_op->opN##_use >= 0 && \
+ ssa->var_info[ssa_op->opN##_use].has_range) { \
+ return ssa->var_info[ssa_op->opN##_use].range.overflow; \
+ } \
+ return 1; \
+ } \
+
+DEFINE_SSA_OP_HAS_RANGE(op1)
+DEFINE_SSA_OP_MIN_RANGE(op1)
+DEFINE_SSA_OP_MAX_RANGE(op1)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
+DEFINE_SSA_OP_HAS_RANGE(op2)
+DEFINE_SSA_OP_MIN_RANGE(op2)
+DEFINE_SSA_OP_MAX_RANGE(op2)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
+
+#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline, ssa_op))
+#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline, ssa_op))
+#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline, ssa_op))
+#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op))
+#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op))
+#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline, ssa_op))
+#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline, ssa_op))
+#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline, ssa_op))
+#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op))
+#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op))
+
+static zend_always_inline uint32_t _const_op_type(const zval *zv) {
+ if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
+ return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ } else if (Z_TYPE_P(zv) == IS_ARRAY) {
+ HashTable *ht = Z_ARRVAL_P(zv);
+ uint32_t tmp = MAY_BE_ARRAY;
+ zend_string *str;
+ zval *val;
+
+ if (Z_REFCOUNTED_P(zv)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RCN;
+ }
+
+ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
+ if (str) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ } else {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
+ } ZEND_HASH_FOREACH_END();
+ if (HT_IS_PACKED(ht)) {
+ tmp &= ~MAY_BE_ARRAY_HASH;
+ }
+ return tmp;
+ } else {
+ uint32_t tmp = (1 << Z_TYPE_P(zv));
+
+ if (Z_REFCOUNTED_P(zv)) {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ tmp |= MAY_BE_RCN;
+ }
+ return tmp;
+ }
+}
+
+static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
+{
+ if (ssa->var_info && ssa_var_num >= 0) {
+ return ssa->var_info[ssa_var_num].type;
+ } else {
+ return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+}
+
+#define DEFINE_SSA_OP_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ return _const_op_type(CRT_CONSTANT(opline->opN)); \
+ } else { \
+ return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \
+ } \
+ } \
+
+#define DEFINE_SSA_OP_DEF_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
+ { \
+ return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \
+ } \
+
+
+DEFINE_SSA_OP_INFO(op1)
+DEFINE_SSA_OP_INFO(op2)
+DEFINE_SSA_OP_INFO(result)
+DEFINE_SSA_OP_DEF_INFO(op1)
+DEFINE_SSA_OP_DEF_INFO(op2)
+DEFINE_SSA_OP_DEF_INFO(result)
+
+#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op))
+#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op))
+#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
+#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
+#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op))
+#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
+#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
+#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
+#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
+#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
+
+static zend_always_inline 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 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()
+
+ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
+ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
+ZEND_API int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
+
+ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert);
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
+ZEND_API int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow);
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level);
+
+ZEND_API uint32_t zend_fetch_arg_info_type(
+ const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce);
+ZEND_API void zend_init_func_return_info(
+ const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret);
+
+ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2);
+ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
+
+ZEND_API int zend_update_type_info(
+ const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script,
+ zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
+ zend_long optimization_level);
+
+END_EXTERN_C()
+
+#endif /* ZEND_INFERENCE_H */
diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c
new file mode 100644
index 0000000000..cc9971a9f8
--- /dev/null
+++ b/Zend/Optimizer/zend_optimizer.c
@@ -0,0 +1,1576 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
+{
+ zval_ptr_dtor_nogc(zvalue);
+}
+
+void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
+{
+ zval val;
+
+ if (!ctx->constants) {
+ ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
+ zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
+ }
+ ZVAL_COPY(&val, value);
+ zend_hash_add(ctx->constants, Z_STR_P(name), &val);
+}
+
+int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */
+{
+ binary_op_type binary_op = get_binary_op(opcode);
+ int er, ret;
+
+ if (zend_binary_op_produces_error(opcode, op1, op2)) {
+ return FAILURE;
+ }
+
+ er = EG(error_reporting);
+ EG(error_reporting) = 0;
+ ret = binary_op(result, op1, op2);
+ EG(error_reporting) = er;
+
+ return ret;
+}
+/* }}} */
+
+int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */
+{
+ unary_op_type unary_op = get_unary_op(opcode);
+
+ if (unary_op) {
+ if (opcode == ZEND_BW_NOT
+ && Z_TYPE_P(op1) != IS_LONG
+ && Z_TYPE_P(op1) != IS_DOUBLE
+ && Z_TYPE_P(op1) != IS_STRING) {
+ /* produces "Unsupported operand types" exception */
+ return FAILURE;
+ }
+ return unary_op(result, op1);
+ } else { /* ZEND_BOOL */
+ ZVAL_BOOL(result, zend_is_true(op1));
+ return SUCCESS;
+ }
+}
+/* }}} */
+
+int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
+{
+ switch (type) {
+ case IS_NULL:
+ ZVAL_NULL(result);
+ return SUCCESS;
+ case _IS_BOOL:
+ ZVAL_BOOL(result, zval_is_true(op1));
+ return SUCCESS;
+ case IS_LONG:
+ ZVAL_LONG(result, zval_get_long(op1));
+ return SUCCESS;
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(result, zval_get_double(op1));
+ return SUCCESS;
+ case IS_STRING:
+ /* Conversion from double to string takes into account run-time
+ 'precision' setting and cannot be evaluated at compile-time */
+ if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
+ ZVAL_STR(result, zval_get_string(op1));
+ return SUCCESS;
+ }
+ break;
+ case IS_ARRAY:
+ ZVAL_COPY(result, op1);
+ convert_to_array(result);
+ return SUCCESS;
+ }
+ return FAILURE;
+}
+/* }}} */
+
+int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */
+{
+ if (Z_TYPE_P(op1) != IS_STRING) {
+ return FAILURE;
+ }
+ ZVAL_LONG(result, Z_STRLEN_P(op1));
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
+{
+ zval *val;
+
+ if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
+ ZVAL_COPY(value, val);
+ return 1;
+ }
+ return 0;
+}
+
+int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
+{
+ int i = op_array->last_literal;
+ op_array->last_literal++;
+ op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
+ ZVAL_COPY_VALUE(&op_array->literals[i], zv);
+ Z_EXTRA(op_array->literals[i]) = 0;
+ return i;
+}
+
+static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
+ zval zv;
+ ZVAL_STR(&zv, str);
+ zend_string_hash_val(str);
+ return zend_optimizer_add_literal(op_array, &zv);
+}
+
+static inline void drop_leading_backslash(zval *val) {
+ if (Z_STRVAL_P(val)[0] == '\\') {
+ zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
+ zval_ptr_dtor_nogc(val);
+ ZVAL_STR(val, str);
+ }
+}
+
+static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) {
+ uint32_t ret = op_array->cache_size;
+ op_array->cache_size += num * sizeof(void *);
+ return ret;
+}
+
+#define REQUIRES_STRING(val) do { \
+ if (Z_TYPE_P(val) != IS_STRING) { \
+ return 0; \
+ } \
+} while (0)
+
+#define TO_STRING_NOWARN(val) do { \
+ if (Z_TYPE_P(val) >= IS_ARRAY) { \
+ return 0; \
+ } \
+ convert_to_string(val); \
+} while (0)
+
+int zend_optimizer_update_op1_const(zend_op_array *op_array,
+ zend_op *opline,
+ 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);
+ zval_ptr_dtor_nogc(val);
+ return 1;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_INSTANCEOF:
+ case ZEND_MAKE_REF:
+ return 0;
+ case ZEND_CATCH:
+ REQUIRES_STRING(val);
+ drop_leading_backslash(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH);
+ zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
+ break;
+ case ZEND_DEFINED:
+ REQUIRES_STRING(val);
+ drop_leading_backslash(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ opline->extended_value = alloc_cache_slots(op_array, 1);
+ zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
+ break;
+ case ZEND_NEW:
+ REQUIRES_STRING(val);
+ drop_leading_backslash(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ opline->op2.num = alloc_cache_slots(op_array, 1);
+ zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ REQUIRES_STRING(val);
+ drop_leading_backslash(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ if (opline->op2_type != IS_CONST) {
+ opline->result.num = alloc_cache_slots(op_array, 1);
+ }
+ zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
+ break;
+ case ZEND_FETCH_CLASS_CONSTANT:
+ REQUIRES_STRING(val);
+ drop_leading_backslash(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ if (opline->op2_type != IS_CONST) {
+ opline->extended_value = alloc_cache_slots(op_array, 1);
+ }
+ 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:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ 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:
+ TO_STRING_NOWARN(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ 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, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
+ }
+ break;
+ case ZEND_SEND_VAR:
+ opline->opcode = ZEND_SEND_VAL;
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ break;
+ case ZEND_SEPARATE:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ return 0;
+ case ZEND_VERIFY_RETURN_TYPE:
+ /* This would require a non-local change.
+ * zend_optimizer_replace_by_const() supports this. */
+ return 0;
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_FETCH_LIST_R:
+ case ZEND_COPY_TMP:
+ case ZEND_FETCH_CLASS_NAME:
+ return 0;
+ case ZEND_ECHO:
+ {
+ zval zv;
+ if (Z_TYPE_P(val) != IS_STRING && zend_optimizer_eval_cast(&zv, IS_STRING, val) == SUCCESS) {
+ zval_ptr_dtor_nogc(val);
+ val = &zv;
+ }
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) {
+ MAKE_NOP(opline);
+ }
+ /* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */
+ /* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */
+ break;
+ }
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ case ZEND_FETCH_R:
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_RW:
+ case ZEND_FETCH_IS:
+ case ZEND_FETCH_UNSET:
+ case ZEND_FETCH_FUNC_ARG:
+ TO_STRING_NOWARN(val);
+ if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) {
+ opline->opcode = ZEND_FAST_CONCAT;
+ }
+ /* break missing intentionally */
+ default:
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ break;
+ }
+
+ opline->op1_type = IS_CONST;
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
+ zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
+ }
+ return 1;
+}
+
+int zend_optimizer_update_op2_const(zend_op_array *op_array,
+ zend_op *opline,
+ zval *val)
+{
+ zval tmp;
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_REF:
+ case ZEND_FAST_CALL:
+ return 0;
+ case ZEND_FETCH_CLASS:
+ if ((opline + 1)->opcode == ZEND_INSTANCEOF &&
+ (opline + 1)->op2.var == opline->result.var) {
+ return 0;
+ }
+ /* break missing intentionally */
+ case ZEND_INSTANCEOF:
+ 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)));
+ opline->extended_value = alloc_cache_slots(op_array, 1);
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ 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)));
+ 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:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ 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:
+ 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_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS));
+ }
+ break;
+ case ZEND_INIT_FCALL:
+ REQUIRES_STRING(val);
+ if (Z_REFCOUNT_P(val) == 1) {
+ zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
+ } else {
+ ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
+ zval_ptr_dtor_nogc(val);
+ val = &tmp;
+ }
+ opline->op2.constant = zend_optimizer_add_literal(op_array, val);
+ opline->result.num = alloc_cache_slots(op_array, 1);
+ break;
+ case ZEND_INIT_DYNAMIC_CALL:
+ if (Z_TYPE_P(val) == IS_STRING) {
+ if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
+ return 0;
+ }
+
+ if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
+ /* Dynamic call to various special functions must stay dynamic,
+ * otherwise would drop a warning */
+ return 0;
+ }
+
+ opline->opcode = ZEND_INIT_FCALL_BY_NAME;
+ 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)));
+ opline->result.num = alloc_cache_slots(op_array, 1);
+ } else {
+ opline->op2.constant = zend_optimizer_add_literal(op_array, val);
+ }
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ REQUIRES_STRING(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)));
+ opline->result.num = alloc_cache_slots(op_array, 2);
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ REQUIRES_STRING(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->result.num = alloc_cache_slots(op_array, 2);
+ }
+ 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:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_UNSET_OBJ:
+ case ZEND_PRE_INC_OBJ:
+ 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, 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, 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:
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_R:
+ case ZEND_FETCH_LIST_W:
+ 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);
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_INIT_ARRAY:
+ if (Z_TYPE_P(val) == IS_STRING) {
+ zend_ulong index;
+ if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
+ zval_ptr_dtor_nogc(val);
+ ZVAL_LONG(val, index);
+ }
+ }
+ opline->op2.constant = zend_optimizer_add_literal(op_array, val);
+ break;
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ TO_STRING_NOWARN(val);
+ if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) {
+ opline->opcode = ZEND_FAST_CONCAT;
+ }
+ /* break missing intentionally */
+ default:
+ opline->op2.constant = zend_optimizer_add_literal(op_array, val);
+ break;
+ }
+
+ opline->op2_type = IS_CONST;
+ if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
+ zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
+ }
+ return 1;
+}
+
+int zend_optimizer_replace_by_const(zend_op_array *op_array,
+ zend_op *opline,
+ zend_uchar type,
+ uint32_t var,
+ zval *val)
+{
+ zend_op *end = op_array->opcodes + op_array->last;
+
+ while (opline < end) {
+ if (opline->op1_type == type &&
+ opline->op1.var == var) {
+ switch (opline->opcode) {
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_SEPARATE:
+ case ZEND_RETURN_BY_REF:
+ return 0;
+ case ZEND_SEND_VAR:
+ opline->extended_value = 0;
+ opline->opcode = ZEND_SEND_VAL;
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ opline->extended_value = 0;
+ opline->opcode = ZEND_SEND_VAL_EX;
+ break;
+ case ZEND_SEND_VAR_NO_REF:
+ return 0;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ opline->opcode = ZEND_SEND_VAL;
+ break;
+ case ZEND_SEND_USER:
+ opline->opcode = ZEND_SEND_VAL_EX;
+ break;
+ /* In most cases IS_TMP_VAR operand may be used only once.
+ * The operands are usually destroyed by the opcode handler.
+ * ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand
+ * unchanged, and allows its reuse. these instructions
+ * usually terminated by ZEND_FREE that finally kills the value.
+ */
+ case ZEND_FETCH_LIST_R: {
+ zend_op *m = opline;
+
+ do {
+ if (m->opcode == ZEND_FETCH_LIST_R &&
+ m->op1_type == type &&
+ m->op1.var == var) {
+ zval v;
+ 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;
+ }
+ m++;
+ } while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
+
+ ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
+ MAKE_NOP(m);
+ zval_ptr_dtor_nogc(val);
+ return 1;
+ }
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT: {
+ 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_CASE_STRICT
+ || opline->opcode == ZEND_SWITCH_LONG
+ || opline->opcode == ZEND_SWITCH_STRING
+ || opline->opcode == ZEND_MATCH
+ ) {
+ zval v;
+
+ if (opline->opcode == ZEND_CASE) {
+ opline->opcode = ZEND_IS_EQUAL;
+ } else if (opline->opcode == ZEND_CASE_STRICT) {
+ opline->opcode = ZEND_IS_IDENTICAL;
+ }
+ ZVAL_COPY(&v, val);
+ if (Z_TYPE(v) == IS_STRING) {
+ zend_string_hash_val(Z_STR(v));
+ }
+ 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_UNREACHABLE();
+ }
+ }
+ opline++;
+ }
+ zval_ptr_dtor_nogc(val);
+ return 1;
+ }
+ case ZEND_VERIFY_RETURN_TYPE: {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))
+ || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+ return 0;
+ }
+ MAKE_NOP(opline);
+
+ /* zend_handle_loops_and_finally may inserts other oplines */
+ do {
+ ++opline;
+ } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
+ ZEND_ASSERT(opline->op1.var == var);
+
+ break;
+ }
+ default:
+ break;
+ }
+ return zend_optimizer_update_op1_const(op_array, opline, val);
+ }
+
+ if (opline->op2_type == type &&
+ opline->op2.var == var) {
+ return zend_optimizer_update_op2_const(op_array, opline, val);
+ }
+ opline++;
+ }
+
+ return 1;
+}
+
+/* Update jump offsets after a jump was migrated to another opline */
+void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
+ switch (new_opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+ break;
+ case ZEND_JMPZNZ:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ break;
+ 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));
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ }
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ zval *zv;
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
+ } ZEND_HASH_FOREACH_END();
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ break;
+ }
+ }
+}
+
+/* Shift jump offsets based on shiftlist */
+void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
+ break;
+ case ZEND_JMPZNZ:
+ 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)]);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ 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_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ 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_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)]);
+ break;
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ {
+ HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ zval *zv;
+ ZEND_HASH_FOREACH_VAL(jumptable, zv) {
+ Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
+ } ZEND_HASH_FOREACH_END();
+ 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)]);
+ break;
+ }
+ }
+}
+
+static zend_class_entry *get_class_entry_from_op1(
+ zend_script *script, zend_op_array *op_array, zend_op *opline) {
+ if (opline->op1_type == IS_CONST) {
+ zval *op1 = CRT_CONSTANT(opline->op1);
+ if (Z_TYPE_P(op1) == IS_STRING) {
+ zend_string *class_name = Z_STR_P(op1 + 1);
+ zend_class_entry *ce;
+ if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
+ return ce;
+ } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ } else if (ce->type == ZEND_USER_CLASS &&
+ ce->info.user.filename &&
+ ce->info.user.filename == op_array->filename) {
+ return ce;
+ }
+ }
+ }
+ } else if (opline->op1_type == IS_UNUSED && op_array->scope
+ && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ return op_array->scope;
+ }
+ return NULL;
+}
+
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype)
+{
+ *is_prototype = 0;
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ {
+ zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
+ zend_function *func;
+ if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
+ return func;
+ } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ return func;
+ } else if (func->type == ZEND_USER_FUNCTION &&
+ func->op_array.filename &&
+ func->op_array.filename == op_array->filename) {
+ return func;
+ }
+ }
+ break;
+ }
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
+ zval *function_name = CRT_CONSTANT(opline->op2) + 1;
+ zend_function *func;
+ if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
+ return func;
+ } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ return func;
+ } else if (func->type == ZEND_USER_FUNCTION &&
+ func->op_array.filename &&
+ func->op_array.filename == op_array->filename) {
+ return func;
+ }
+ }
+ }
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
+ zend_class_entry *ce = get_class_entry_from_op1(
+ script, op_array, opline);
+ if (ce) {
+ zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
+ zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name);
+ if (fbc) {
+ bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0;
+ bool same_scope = fbc->common.scope == op_array->scope;
+ if (is_public || same_scope) {
+ return fbc;
+ }
+ }
+ }
+ }
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ if (opline->op1_type == IS_UNUSED
+ && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING
+ && op_array->scope
+ && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
+ && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
+ zend_string *method_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
+ zend_function *fbc = zend_hash_find_ptr(
+ &op_array->scope->function_table, method_name);
+ if (fbc) {
+ bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
+ bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
+ bool same_scope = fbc->common.scope == op_array->scope;
+ if (is_private) {
+ /* Only use private method if in the same scope. We can't even use it
+ * as a prototype, as it may be overridden with changed signature. */
+ return same_scope ? fbc : NULL;
+ }
+ /* If the method is non-final, it may be overridden,
+ * but only with a compatible method signature. */
+ *is_prototype = !is_final;
+ return fbc;
+ }
+ }
+ break;
+ case ZEND_NEW:
+ {
+ zend_class_entry *ce = get_class_entry_from_op1(
+ script, op_array, opline);
+ if (ce && ce->type == ZEND_USER_CLASS) {
+ return ce->constructor;
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
+uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
+ if (zend_string_equals_literal(name, "extract")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "compact")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "db2_execute")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
+ return ZEND_FUNC_VARARG;
+ } else {
+ return 0;
+ }
+}
+
+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)
+{
+ if (op_array->type == ZEND_EVAL_CODE) {
+ return;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
+ zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
+ }
+
+ /* pass 1 (Simple local optimizations)
+ * - persistent constant substitution (true, false, null, etc)
+ * - constant casting (ADD expects numbers, CONCAT strings, etc)
+ * - constant expression evaluation
+ * - optimize constant conditional JMPs
+ * - pre-evaluate constant function calls
+ * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
+ */
+ if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
+ zend_optimizer_pass1(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
+ zend_dump_op_array(op_array, 0, "after pass 1", NULL);
+ }
+ }
+
+ /* pass 3: (Jump optimization)
+ * - optimize series of JMPs
+ */
+ if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
+ zend_optimizer_pass3(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
+ zend_dump_op_array(op_array, 0, "after pass 3", NULL);
+ }
+ }
+
+ /* pass 4:
+ * - INIT_FCALL_BY_NAME -> DO_FCALL
+ */
+ if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
+ zend_optimize_func_calls(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
+ zend_dump_op_array(op_array, 0, "after pass 4", NULL);
+ }
+ }
+
+ /* pass 5:
+ * - CFG optimization
+ */
+ if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
+ zend_optimize_cfg(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
+ zend_dump_op_array(op_array, 0, "after pass 5", NULL);
+ }
+ }
+
+ /* pass 6:
+ * - DFA optimization
+ */
+ if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
+ !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
+ zend_optimize_dfa(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
+ zend_dump_op_array(op_array, 0, "after pass 6", NULL);
+ }
+ }
+
+ /* pass 9:
+ * - Optimize temp variables usage
+ */
+ if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
+ zend_optimize_temporary_variables(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
+ zend_dump_op_array(op_array, 0, "after pass 9", NULL);
+ }
+ }
+
+ /* pass 10:
+ * - remove NOPs
+ */
+ if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
+ zend_optimizer_nop_removal(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
+ zend_dump_op_array(op_array, 0, "after pass 10", NULL);
+ }
+ }
+
+ /* pass 11:
+ * - Compact literals table
+ */
+ if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
+ (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
+ !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
+ zend_optimizer_compact_literals(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
+ zend_dump_op_array(op_array, 0, "after pass 11", NULL);
+ }
+ }
+
+ if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
+ (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
+ !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
+ zend_optimizer_compact_vars(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
+ zend_dump_op_array(op_array, 0, "after pass 13", NULL);
+ }
+ }
+
+ if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
+ return;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
+ zend_dump_op_array(op_array, 0, "after optimizer", NULL);
+ }
+}
+
+static void zend_revert_pass_two(zend_op_array *op_array)
+{
+ zend_op *opline, *end;
+
+ ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0);
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ if (opline->op1_type == IS_CONST) {
+ ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2);
+ }
+ /* reset smart branch flags IS_SMART_BRANCH_JMP[N]Z */
+ opline->result_type &= (IS_TMP_VAR|IS_VAR|IS_CV|IS_CONST);
+ opline++;
+ }
+#if !ZEND_USE_ABS_CONST_ADDR
+ if (op_array->literals) {
+ zval *literals = emalloc(sizeof(zval) * op_array->last_literal);
+ memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal);
+ op_array->literals = literals;
+ }
+#endif
+
+ op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
+}
+
+static void zend_redo_pass_two(zend_op_array *op_array)
+{
+ zend_op *opline, *end;
+#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
+ zend_op *old_opcodes = op_array->opcodes;
+#endif
+
+ ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
+
+#if !ZEND_USE_ABS_CONST_ADDR
+ if (op_array->last_literal) {
+ op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
+ ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
+ sizeof(zval) * op_array->last_literal);
+ memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
+ op_array->literals, sizeof(zval) * op_array->last_literal);
+ efree(op_array->literals);
+ op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
+ } else {
+ if (op_array->literals) {
+ efree(op_array->literals);
+ }
+ op_array->literals = NULL;
+ }
+#endif
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ if (opline->op1_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
+ }
+ /* fix jumps to point to new array */
+ switch (opline->opcode) {
+#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
+ break;
+ case ZEND_JMPZNZ:
+ /* relative extended_value don't have to be changed */
+ /* break omitted intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ /* relative extended_value don't have to be changed */
+ break;
+#endif
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_IN_ARRAY:
+ case ZEND_ARRAY_KEY_EXISTS:
+ if (opline->result_type & IS_TMP_VAR) {
+ /* reinitialize result_type of smart branch instructions */
+ if (opline + 1 < end) {
+ if ((opline+1)->opcode == ZEND_JMPZ
+ && (opline+1)->op1_type == IS_TMP_VAR
+ && (opline+1)->op1.var == opline->result.var) {
+ opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
+ } else if ((opline+1)->opcode == ZEND_JMPNZ
+ && (opline+1)->op1_type == IS_TMP_VAR
+ && (opline+1)->op1.var == opline->result.var) {
+ opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
+ }
+ }
+ }
+ break;
+ }
+ ZEND_VM_SET_OPCODE_HANDLER(opline);
+ opline++;
+ }
+
+ op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
+}
+
+static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_op *opline, *end;
+#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
+ zend_op *old_opcodes = op_array->opcodes;
+#endif
+
+ ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
+
+#if !ZEND_USE_ABS_CONST_ADDR
+ if (op_array->last_literal) {
+ op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
+ ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
+ sizeof(zval) * op_array->last_literal);
+ memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
+ op_array->literals, sizeof(zval) * op_array->last_literal);
+ efree(op_array->literals);
+ op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
+ } else {
+ if (op_array->literals) {
+ efree(op_array->literals);
+ }
+ op_array->literals = NULL;
+ }
+#endif
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
+ uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
+ uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
+ uint32_t res_info =
+ (opline->opcode == ZEND_PRE_INC ||
+ opline->opcode == ZEND_PRE_DEC ||
+ opline->opcode == ZEND_POST_INC ||
+ opline->opcode == ZEND_POST_DEC) ?
+ ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
+ (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)));
+
+ if (opline->op1_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
+ }
+
+ /* fix jumps to point to new array */
+ switch (opline->opcode) {
+#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
+ break;
+ case ZEND_JMPZNZ:
+ /* relative extended_value don't have to be changed */
+ /* break omitted intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_ASSERT_CHECK:
+ case ZEND_JMP_NULL:
+ opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
+ break;
+ case ZEND_CATCH:
+ if (!(opline->extended_value & ZEND_LAST_CATCH)) {
+ opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_MATCH:
+ /* relative extended_value don't have to be changed */
+ break;
+#endif
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ case ZEND_ISSET_ISEMPTY_CV:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_IN_ARRAY:
+ case ZEND_ARRAY_KEY_EXISTS:
+ if (opline->result_type & IS_TMP_VAR) {
+ /* reinitialize result_type of smart branch instructions */
+ if (opline + 1 < end) {
+ if ((opline+1)->opcode == ZEND_JMPZ
+ && (opline+1)->op1_type == IS_TMP_VAR
+ && (opline+1)->op1.var == opline->result.var) {
+ opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
+ } else if ((opline+1)->opcode == ZEND_JMPNZ
+ && (opline+1)->op1_type == IS_TMP_VAR
+ && (opline+1)->op1.var == opline->result.var) {
+ opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
+ }
+ }
+ }
+ break;
+ }
+ zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
+ opline++;
+ }
+
+ op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
+}
+
+static void zend_optimize_op_array(zend_op_array *op_array,
+ zend_optimizer_ctx *ctx)
+{
+ /* Revert pass_two() */
+ zend_revert_pass_two(op_array);
+
+ /* Do actual optimizations */
+ zend_optimize(op_array, ctx);
+
+ /* Redo pass_two() */
+ zend_redo_pass_two(op_array);
+
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, NULL);
+ }
+}
+
+static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ zend_function *func;
+ zend_op *opline, *end;
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ if (opline->opcode == ZEND_INIT_FCALL) {
+ func = zend_hash_find_ptr(
+ &ctx->script->function_table,
+ Z_STR_P(RT_CONSTANT(opline, opline->op2)));
+ if (func) {
+ opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
+ }
+ }
+ opline++;
+ }
+}
+
+static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
+{
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+ if (func_info) {
+ zend_call_info *call_info =func_info->callee_info;
+
+ while (call_info) {
+ zend_op *opline = call_info->caller_init_opline;
+
+ if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
+ opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
+ }
+ call_info = call_info->next_callee;
+ }
+ }
+}
+
+static 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];
+ int ssa_var = ssa_op->result_def;
+ if (ssa_var < 0) {
+ /* Be conservative. */
+ return 1;
+ }
+
+ /* If the variable is used by a PHI, this may be the assignment of the final branch of a
+ * ternary/etc structure. While this is where the live range starts, the value from the other
+ * branch may also be used. As such, use the type of the PHI node for the following check. */
+ if (func_info->ssa.vars[ssa_var].phi_use_chain) {
+ ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var;
+ }
+
+ uint32_t type = func_info->ssa.var_info[ssa_var].type;
+ return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
+}
+
+static void zend_foreach_op_array_helper(
+ zend_op_array *op_array, zend_op_array_func_t func, void *context) {
+ func(op_array, context);
+ for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
+ func(op_array->dynamic_func_defs[i], context);
+ }
+}
+
+void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
+{
+ zend_class_entry *ce;
+ zend_string *key;
+ zend_op_array *op_array;
+
+ zend_foreach_op_array_helper(&script->main_op_array, func, context);
+
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ zend_foreach_op_array_helper(op_array, func, context);
+ } 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_PTR(&ce->function_table, op_array) {
+ if (op_array->scope == ce
+ && op_array->type == ZEND_USER_FUNCTION
+ && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
+ zend_foreach_op_array_helper(op_array, func, context);
+ }
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+}
+
+static void step_optimize_op_array(zend_op_array *op_array, void *context) {
+ zend_optimize_op_array(op_array, (zend_optimizer_ctx *) context);
+}
+
+static void step_adjust_fcall_stack_size(zend_op_array *op_array, void *context) {
+ zend_adjust_fcall_stack_size(op_array, (zend_optimizer_ctx *) context);
+}
+
+static void step_dump_after_optimizer(zend_op_array *op_array, void *context) {
+ zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
+}
+
+ZEND_API 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;
+ zend_call_graph call_graph;
+
+ ctx.arena = zend_arena_create(64 * 1024);
+ ctx.script = script;
+ ctx.constants = NULL;
+ ctx.optimization_level = optimization_level;
+ ctx.debug_level = debug_level;
+
+ if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
+ (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
+ zend_build_call_graph(&ctx.arena, script, &call_graph) == SUCCESS) {
+ /* Optimize using call-graph */
+ int i;
+ zend_func_info *func_info;
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_revert_pass_two(call_graph.op_arrays[i]);
+ zend_optimize(call_graph.op_arrays[i], &ctx);
+ }
+
+ zend_analyze_call_graph(&ctx.arena, script, &call_graph);
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
+ if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
+ }
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
+ func_info->flags = func_info->ssa.cfg.flags;
+ } else {
+ ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
+ }
+ }
+ }
+
+ //TODO: perform inner-script inference???
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
+ }
+ }
+
+ if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
+ }
+ }
+
+ if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
+ if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
+ zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
+ }
+ }
+ }
+
+ if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_optimizer_compact_vars(call_graph.op_arrays[i]);
+ if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
+ zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
+ }
+ }
+ }
+
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; 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(op_array, &func_info->ssa);
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, needs_live_range);
+ }
+ } else {
+ zend_redo_pass_two(op_array);
+ if (op_array->live_range) {
+ zend_recalc_live_ranges(op_array, NULL);
+ }
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
+ }
+ } else {
+ zend_foreach_op_array(script, step_optimize_op_array, &ctx);
+
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ zend_foreach_op_array(script, step_adjust_fcall_stack_size, &ctx);
+ }
+ }
+
+ 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_foreach_op_array(script, step_dump_after_optimizer, NULL);
+ }
+
+ if (ctx.constants) {
+ zend_hash_destroy(ctx.constants);
+ }
+ zend_arena_destroy(ctx.arena);
+
+ return 1;
+}
+
+int zend_optimizer_startup(void)
+{
+ return zend_func_info_startup();
+}
+
+int zend_optimizer_shutdown(void)
+{
+ return zend_func_info_shutdown();
+}
diff --git a/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h
new file mode 100644
index 0000000000..4b43255437
--- /dev/null
+++ b/Zend/Optimizer/zend_optimizer.h
@@ -0,0 +1,98 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_OPTIMIZER_H
+#define ZEND_OPTIMIZER_H
+
+#include "zend.h"
+#include "zend_compile.h"
+
+#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */
+#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */
+#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */
+#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
+#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
+#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
+#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
+#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
+#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
+#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
+#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */
+#define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */
+#define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */
+#define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */
+#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */
+#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */
+
+#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */
+
+#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
+
+#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF"
+
+
+#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
+#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
+#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
+#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
+#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
+#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
+#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
+#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
+#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
+#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
+#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
+#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
+#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
+#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
+
+#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
+#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
+
+#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
+#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
+#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
+
+#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
+#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
+#define ZEND_DUMP_DFA_CFG (1<<23)
+#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
+#define ZEND_DUMP_DFA_LIVENESS (1<<25)
+#define ZEND_DUMP_DFA_PHI (1<<26)
+#define ZEND_DUMP_DFA_SSA (1<<27)
+#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
+#define ZEND_DUMP_SCCP (1<<29)
+
+typedef struct _zend_script {
+ zend_string *filename;
+ zend_op_array main_op_array;
+ HashTable function_table;
+ HashTable class_table;
+ uint32_t first_early_binding_opline; /* the linked list of delayed declarations */
+} zend_script;
+
+BEGIN_EXTERN_C()
+ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
+int zend_optimizer_startup(void);
+int zend_optimizer_shutdown(void);
+END_EXTERN_C()
+
+#endif
diff --git a/Zend/Optimizer/zend_optimizer_internal.h b/Zend/Optimizer/zend_optimizer_internal.h
new file mode 100644
index 0000000000..911eb79e64
--- /dev/null
+++ b/Zend/Optimizer/zend_optimizer_internal.h
@@ -0,0 +1,123 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@php.net> |
+ | Zeev Suraski <zeev@php.net> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_OPTIMIZER_INTERNAL_H
+#define ZEND_OPTIMIZER_INTERNAL_H
+
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+
+#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
+#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
+#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
+#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
+
+#define VAR_NUM(v) EX_VAR_TO_NUM(v)
+#define NUM_VAR(v) EX_NUM_TO_VAR(v)
+
+#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ)
+#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ)
+#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
+#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
+
+#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
+#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
+
+typedef struct _zend_optimizer_ctx {
+ zend_arena *arena;
+ zend_script *script;
+ HashTable *constants;
+ zend_long optimization_level;
+ zend_long debug_level;
+} zend_optimizer_ctx;
+
+#define LITERAL_LONG(op, val) do { \
+ zval _c; \
+ ZVAL_LONG(&_c, val); \
+ op.constant = zend_optimizer_add_literal(op_array, &_c); \
+ } while (0)
+
+#define LITERAL_BOOL(op, val) do { \
+ zval _c; \
+ ZVAL_BOOL(&_c, val); \
+ op.constant = zend_optimizer_add_literal(op_array, &_c); \
+ } while (0)
+
+#define literal_dtor(zv) do { \
+ zval_ptr_dtor_nogc(zv); \
+ ZVAL_NULL(zv); \
+ } while (0)
+
+#define COPY_NODE(target, src) do { \
+ target ## _type = src ## _type; \
+ target = src; \
+ } while (0)
+
+static inline 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);
+int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value);
+int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2);
+int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1);
+int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1);
+int zend_optimizer_eval_strlen(zval *result, zval *op1);
+int zend_optimizer_update_op1_const(zend_op_array *op_array,
+ zend_op *opline,
+ zval *val);
+int zend_optimizer_update_op2_const(zend_op_array *op_array,
+ zend_op *opline,
+ zval *val);
+int zend_optimizer_replace_by_const(zend_op_array *op_array,
+ zend_op *opline,
+ 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_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map);
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimizer_compact_vars(zend_op_array *op_array);
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype);
+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);
+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, bool reorder_dtor_effects);
+int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
+
+typedef void (*zend_op_array_func_t)(zend_op_array *, void *context);
+void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context);
+
+#endif
diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c
new file mode 100644
index 0000000000..6fe97cbc1e
--- /dev/null
+++ b/Zend/Optimizer/zend_ssa.c
@@ -0,0 +1,1628 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ | Nikita Popov <nikic@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+#include "zend_ssa.h"
+#include "zend_dump.h"
+#include "zend_inference.h"
+#include "Optimizer/zend_optimizer_internal.h"
+
+static bool dominates(const zend_basic_block *blocks, int a, int b) {
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+
+static bool will_rejoin(
+ const zend_cfg *cfg, const zend_dfg *dfg, const zend_basic_block *block,
+ int other_successor, int exclude, int var) {
+ int i;
+ for (i = 0; i < block->predecessors_count; i++) {
+ int predecessor = cfg->predecessors[block->predecessor_offset + i];
+ if (predecessor == exclude) {
+ continue;
+ }
+
+ /* The variable is changed in this predecessor,
+ * so we will not rejoin with the original value. */
+ // TODO: This should not be limited to the direct predecessor block.
+ if (DFG_ISSET(dfg->def, dfg->size, predecessor, var)) {
+ continue;
+ }
+
+ /* The other successor dominates this predecessor,
+ * so we will get the original value from it. */
+ if (dominates(cfg->blocks, other_successor, predecessor)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
+{
+ zend_basic_block *from_block, *to_block;
+ int other_successor;
+
+ if (!DFG_ISSET(dfg->in, dfg->size, to, var)) {
+ /* Variable is not live, certainly won't benefit from pi */
+ return 0;
+ }
+
+ /* Make sure that both successors of the from block aren't the same. Pi nodes are associated
+ * with predecessor blocks, so we can't distinguish which edge the pi belongs to. */
+ from_block = &ssa->cfg.blocks[from];
+ ZEND_ASSERT(from_block->successors_count == 2);
+ if (from_block->successors[0] == from_block->successors[1]) {
+ return 0;
+ }
+
+ to_block = &ssa->cfg.blocks[to];
+ if (to_block->predecessors_count == 1) {
+ /* Always place pi if one predecessor (an if branch) */
+ return 1;
+ }
+
+ /* Check whether we will rejoin with the original value coming from the other successor,
+ * in which case the pi node will not have an effect. */
+ other_successor = from_block->successors[0] == to
+ ? from_block->successors[1] : from_block->successors[0];
+ return !will_rejoin(&ssa->cfg, dfg, to_block, other_successor, from, var);
+}
+/* }}} */
+
+static zend_ssa_phi *add_pi(
+ zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa,
+ int from, int to, int var) /* {{{ */
+{
+ zend_ssa_phi *phi;
+ if (!needs_pi(op_array, dfg, ssa, from, to, var)) {
+ return NULL;
+ }
+
+ phi = zend_arena_calloc(arena, 1,
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count) +
+ sizeof(void*) * ssa->cfg.blocks[to].predecessors_count);
+ phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
+ memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count));
+
+ phi->pi = from;
+ phi->var = var;
+ phi->ssa_var = -1;
+ phi->next = ssa->blocks[to].phis;
+ ssa->blocks[to].phis = phi;
+
+ /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that
+ * this is not entirely accurate, because the pi is actually placed along the edge from->to.
+ * If there is a back-edge to "to" this may result in non-minimal SSA form. */
+ DFG_SET(dfg->def, dfg->size, to, var);
+
+ /* If there are multiple predecessors in the target block, we need to place a phi there.
+ * However this can (generally) not be expressed in terms of dominance frontiers, so place it
+ * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */
+ if (ssa->cfg.blocks[to].predecessors_count > 1) {
+ DFG_SET(dfg->use, dfg->size, to, var);
+ }
+
+ return phi;
+}
+/* }}} */
+
+static void pi_range(
+ zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max,
+ char underflow, char overflow, char negative) /* {{{ */
+{
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ constraint->min_var = min_var;
+ constraint->max_var = max_var;
+ constraint->min_ssa_var = -1;
+ constraint->max_ssa_var = -1;
+ constraint->range.min = min;
+ constraint->range.max = max;
+ constraint->range.underflow = underflow;
+ constraint->range.overflow = overflow;
+ constraint->negative = negative ? NEG_INIT : NEG_NONE;
+ phi->has_range_constraint = 1;
+}
+/* }}} */
+
+static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 0);
+}
+static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 1);
+}
+static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0);
+}
+static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0);
+}
+
+static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ phi->has_range_constraint = 0;
+ phi->constraint.type.ce = NULL;
+ phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+ phi->constraint.type.type_mask |= type_mask;
+ if (type_mask & MAY_BE_NULL) {
+ phi->constraint.type.type_mask |= MAY_BE_UNDEF;
+ }
+}
+static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ pi_type_mask(phi, ~type_mask & relevant);
+}
+static inline uint32_t mask_for_type_check(uint32_t type) {
+ if (type & MAY_BE_ARRAY) {
+ return type | (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ } else {
+ return type;
+ }
+}
+
+/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
+ * This negated adjustment is what is written into the "adjustment" parameter. */
+static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */
+{
+ zend_op *op = opline;
+ zval *zv;
+
+ while (op != op_array->opcodes) {
+ op--;
+ if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
+ continue;
+ }
+
+ if (op->opcode == ZEND_POST_DEC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = -1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_POST_INC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = 1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_ADD) {
+ if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
+ zv = CRT_CONSTANT_EX(op_array, op, op->op2);
+ if (Z_TYPE_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->op2_type == IS_CV && op->op1_type == IS_CONST) {
+ zv = CRT_CONSTANT_EX(op_array, op, op->op1);
+ if (Z_TYPE_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op2.var);
+ }
+ }
+ } else if (op->opcode == ZEND_SUB) {
+ if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
+ zv = CRT_CONSTANT_EX(op_array, op, op->op2);
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ *adjustment = Z_LVAL_P(zv);
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ }
+ }
+ break;
+ }
+ return -1;
+}
+/* }}} */
+
+/* e-SSA construction: Pi placement (Pi is actually a Phi with single
+ * source and constraint).
+ * Order of Phis is important, Pis must be placed before Phis
+ */
+static void place_essa_pis(
+ zend_arena **arena, const zend_script *script, const zend_op_array *op_array,
+ uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ {
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ int j, blocks_count = ssa->cfg.blocks_count;
+ for (j = 0; j < blocks_count; j++) {
+ zend_ssa_phi *pi;
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ int bt; /* successor block number if a condition is true */
+ int bf; /* successor block number if a condition is false */
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) {
+ continue;
+ }
+ /* the last instruction of basic block is conditional branch,
+ * based on comparison of CV(s)
+ */
+ switch (opline->opcode) {
+ case ZEND_JMPZ:
+ case ZEND_JMPZNZ:
+ bf = blocks[j].successors[0];
+ bt = blocks[j].successors[1];
+ break;
+ case ZEND_JMPNZ:
+ bt = blocks[j].successors[0];
+ bf = blocks[j].successors[1];
+ break;
+ case ZEND_COALESCE:
+ if (opline->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM(opline->op1.var);
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[0], var))) {
+ pi_not_type_mask(pi, MAY_BE_NULL);
+ }
+ }
+ continue;
+ case ZEND_JMP_NULL:
+ if (opline->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM(opline->op1.var);
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[1], var))) {
+ pi_not_type_mask(pi, MAY_BE_NULL);
+ }
+ }
+ continue;
+ default:
+ continue;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_SMALLER ||
+ (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var1 = -1;
+ int var2 = -1;
+ zend_long val1 = 0;
+ zend_long val2 = 0;
+// long val = 0;
+
+ if ((opline-1)->op1_type == IS_CV) {
+ var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
+ } else if ((opline-1)->op1_type == IS_TMP_VAR) {
+ var1 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op1.var, &val2);
+ }
+
+ if ((opline-1)->op2_type == IS_CV) {
+ var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
+ } else if ((opline-1)->op2_type == IS_TMP_VAR) {
+ var2 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op2.var, &val1);
+ }
+
+ if (var1 >= 0 && var2 >= 0) {
+ if (!zend_sub_will_overflow(val1, val2) && !zend_sub_will_overflow(val2, val1)) {
+ zend_long tmp = val1;
+ val1 -= val2;
+ val2 -= tmp;
+ } else {
+ var1 = -1;
+ var2 = -1;
+ }
+ } else if (var1 >= 0 && var2 < 0) {
+ zend_long add_val2 = 0;
+ if ((opline-1)->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2);
+
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ add_val2 = Z_LVAL_P(zv);
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ add_val2 = 0;
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ add_val2 = 1;
+ } else {
+ var1 = -1;
+ }
+ } else {
+ var1 = -1;
+ }
+ if (!zend_add_will_overflow(val2, add_val2)) {
+ val2 += add_val2;
+ } else {
+ var1 = -1;
+ }
+ } else if (var1 < 0 && var2 >= 0) {
+ zend_long add_val1 = 0;
+ if ((opline-1)->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1);
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ add_val1 = Z_LVAL_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1));
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ add_val1 = 0;
+ } else if (Z_TYPE_P(zv) == IS_TRUE) {
+ add_val1 = 1;
+ } else {
+ var2 = -1;
+ }
+ } else {
+ var2 = -1;
+ }
+ if (!zend_add_will_overflow(val1, add_val1)) {
+ val1 += add_val1;
+ } else {
+ var2 = -1;
+ }
+ }
+
+ if (var1 >= 0) {
+ if ((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val2 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2-1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2);
+ }
+ if (val2 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2+1);
+ }
+ }
+ }
+ }
+ if (var2 >= 0) {
+ if((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val1 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1+1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1);
+ }
+ if (val1 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1-1);
+ }
+ }
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_POST_INC ||
+ (opline-1)->opcode == ZEND_POST_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((opline-1)->opcode == ZEND_POST_DEC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, -1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, -1);
+ }
+ } else if ((opline-1)->opcode == ZEND_POST_INC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 1);
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_PRE_INC ||
+ (opline-1)->opcode == ZEND_PRE_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 0);
+ }
+ /* speculative */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 0);
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ uint32_t type = (opline-1)->extended_value;
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, mask_for_type_check(type));
+ }
+ if (type != MAY_BE_RESOURCE) {
+ /* is_resource() may return false for closed resources */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, mask_for_type_check(type));
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_IDENTICAL
+ || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var;
+ zval *val;
+ uint32_t type_mask;
+ if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
+ var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2);
+ } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
+ var = EX_VAR_TO_NUM((opline-1)->op2.var);
+ val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1);
+ } else {
+ continue;
+ }
+
+ /* We're interested in === null/true/false comparisons here, because they eliminate
+ * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
+ if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
+ continue;
+ }
+
+ type_mask = _const_op_type(val);
+ if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ } else {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV &&
+ (opline-1)->op2_type == IS_CONST) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2) + 1);
+ zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
+ if (!ce) {
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (!ce || ce->type != ZEND_INTERNAL_CLASS) {
+ continue;
+ }
+ }
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, MAY_BE_OBJECT);
+ pi->constraint.type.ce = ce;
+ }
+ }
+ }
+}
+/* }}} */
+
+static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
+{
+ const zend_op *next;
+
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op1.var)
+ }
+ if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op2.var)
+ }
+ if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
+ && opline->result_type == IS_CV
+ && opline->opcode != ZEND_RECV) {
+ ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->result.var)
+ }
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ if (opline->op1_type == IS_CV) {
+add_op1_def:
+ 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;
+ case ZEND_ASSIGN_REF:
+ if (opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ if (build_flags & ZEND_SSA_RC_INFERENCE && 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)
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_OBJ_REF:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ 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)
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_STATIC_PROP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && 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_ASSIGN_STATIC_PROP_REF:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ 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_ASSIGN_STATIC_PROP_OP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ }
+ break;
+ case ZEND_ASSIGN_DIM_OP:
+ case ZEND_ASSIGN_OBJ_OP:
+ next = opline + 1;
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ }
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ASSIGN_OP:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ case ZEND_FE_RESET_RW:
+ case ZEND_MAKE_REF:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_LIST_W:
+ if (opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ case ZEND_CAST:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_FE_RESET_R:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_ADD_ARRAY_UNPACK:
+ ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
+ /* break missing intentionally */
+ case ZEND_INIT_ARRAY:
+ if (((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
+ && opline->op1_type == IS_CV) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_YIELD:
+ if (opline->op1_type == IS_CV
+ && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ || (build_flags & ZEND_SSA_RC_INFERENCE))) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_UNSET_CV:
+ goto add_op1_def;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ goto add_op1_def;
+ }
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ if (opline->op2_type != IS_CV) {
+ ssa_ops[k].op2_use = -1; /* not used */
+ }
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ break;
+ case ZEND_BIND_LEXICAL:
+ if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].result_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(op_array->last_var + opline->result.var)
+ }
+
+ return ssa_vars_count;
+}
+/* }}} */
+
+ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
+{
+ return _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
+}
+/* }}} */
+
+static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ int i, j;
+ zend_op *opline, *end;
+ int *tmp = NULL;
+ ALLOCA_FLAG(use_heap = 0);
+
+ // FIXME: Can we optimize this copying out in some cases?
+ if (blocks[n].next_child >= 0) {
+ tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap);
+ memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T));
+ var = tmp;
+ }
+
+ if (ssa_blocks[n].phis) {
+ zend_ssa_phi *phi = ssa_blocks[n].phis;
+ do {
+ if (phi->ssa_var < 0) {
+ phi->ssa_var = ssa_vars_count;
+ var[phi->var] = ssa_vars_count;
+ ssa_vars_count++;
+ } else {
+ var[phi->var] = phi->ssa_var;
+ }
+ phi = phi->next;
+ } while (phi);
+ }
+
+ opline = op_array->opcodes + blocks[n].start;
+ end = opline + blocks[n].len;
+ for (; opline < end; opline++) {
+ uint32_t k = opline - op_array->opcodes;
+ if (opline->opcode != ZEND_OP_DATA) {
+ ssa_vars_count = _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
+ }
+ }
+
+ zend_ssa_op *fe_fetch_ssa_op = blocks[n].len != 0
+ && ((end-1)->opcode == ZEND_FE_FETCH_R || (end-1)->opcode == ZEND_FE_FETCH_RW)
+ && (end-1)->op2_type == IS_CV
+ ? &ssa_ops[blocks[n].start + blocks[n].len - 1] : NULL;
+ for (i = 0; i < blocks[n].successors_count; i++) {
+ int succ = blocks[n].successors[i];
+ zend_ssa_phi *p;
+ for (p = ssa_blocks[succ].phis; p; p = p->next) {
+ if (p->pi == n) {
+ /* e-SSA Pi */
+ if (p->has_range_constraint) {
+ if (p->constraint.range.min_var >= 0) {
+ p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
+ }
+ if (p->constraint.range.max_var >= 0) {
+ p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
+ }
+ }
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ p->sources[j] = var[p->var];
+ }
+ if (p->ssa_var < 0) {
+ p->ssa_var = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ } else if (p->pi < 0) {
+ /* Normal Phi */
+ for (j = 0; j < blocks[succ].predecessors_count; j++)
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ p->sources[j] = var[p->var];
+ if (fe_fetch_ssa_op && i == 0 && p->sources[j] == fe_fetch_ssa_op->op2_def) {
+ /* On the exit edge of an FE_FETCH, use the pre-modification value instead. */
+ p->sources[j] = fe_fetch_ssa_op->op2_use;
+ }
+ }
+ }
+ for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
+ if (p->pi == n) {
+ zend_ssa_phi *q = p->next;
+ while (q) {
+ if (q->pi < 0 && q->var == p->var) {
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ q->sources[j] = p->ssa_var;
+ }
+ q = q->next;
+ }
+ }
+ }
+ }
+
+ ssa->vars_count = ssa_vars_count;
+
+ j = blocks[n].children;
+ while (j >= 0) {
+ // FIXME: Tail call optimization?
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS)
+ return FAILURE;
+ j = blocks[j].next_child;
+ }
+
+ if (tmp) {
+ free_alloca(tmp, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+ uint32_t set_size;
+ zend_bitset def, in, phi;
+ int *var = NULL;
+ int i, j, k, changed;
+ zend_dfg dfg;
+ ALLOCA_FLAG(dfg_use_heap)
+ ALLOCA_FLAG(var_use_heap)
+
+ if ((blocks_count * (op_array->last_var + op_array->T)) > 4 * 1024 * 1024) {
+ /* Don't build SSA for very big functions */
+ return FAILURE;
+ }
+
+ ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
+ ssa->blocks = ssa_blocks;
+
+ /* Compute Variable Liveness */
+ dfg.vars = op_array->last_var + op_array->T;
+ dfg.size = set_size = zend_bitset_len(dfg.vars);
+ dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1), dfg_use_heap);
+ memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1));
+ dfg.def = dfg.tmp + set_size;
+ dfg.use = dfg.def + set_size * blocks_count;
+ dfg.in = dfg.use + set_size * blocks_count;
+ dfg.out = dfg.in + set_size * blocks_count;
+
+ if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
+ zend_dump_dfg(op_array, &ssa->cfg, &dfg);
+ }
+
+ def = dfg.def;
+ in = dfg.in;
+
+ /* Reuse the "use" set, as we no longer need it */
+ phi = dfg.use;
+ zend_bitset_clear(phi, set_size * blocks_count);
+
+ /* Place e-SSA pis. This will add additional "def" points, so it must
+ * happen before def propagation. */
+ place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg);
+
+ /* SSA construction, Step 1: Propagate "def" sets in merge points */
+ do {
+ changed = 0;
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].predecessors_count > 1) {
+ if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ /* Prevent any values from flowing into irreducible loops by
+ replacing all incoming values with explicit phis. The
+ register allocator depends on this property. */
+ zend_bitset_union(phi_j, in + (j * set_size), set_size);
+ } else {
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
+ while (i != -1 && i != blocks[j].idom) {
+ zend_bitset_union_with_intersection(
+ phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size);
+ i = blocks[i].idom;
+ }
+ }
+ }
+ if (!zend_bitset_subset(phi_j, def_j, set_size)) {
+ zend_bitset_union(def_j, phi_j, set_size);
+ changed = 1;
+ }
+ }
+ }
+ } while (changed);
+
+ /* SSA construction, Step 2: Phi placement based on Dominance Frontiers */
+ var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap);
+ if (!var) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (!zend_bitset_empty(phi + j * set_size, set_size)) {
+ ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) {
+ zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(int) * blocks[j].predecessors_count) +
+ sizeof(void*) * blocks[j].predecessors_count);
+
+ phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
+ memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[j].predecessors_count));
+
+ phi->pi = -1;
+ phi->var = i;
+ phi->ssa_var = -1;
+
+ /* Place phis after pis */
+ {
+ zend_ssa_phi **pp = &ssa_blocks[j].phis;
+ while (*pp) {
+ if ((*pp)->pi < 0) {
+ break;
+ }
+ pp = &(*pp)->next;
+ }
+ phi->next = *pp;
+ *pp = phi;
+ }
+ } ZEND_BITSET_FOREACH_END();
+ }
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) {
+ zend_dump_phi_placement(op_array, ssa);
+ }
+
+ /* SSA construction, Step 3: Renaming */
+ ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op));
+ memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op));
+ memset(var + op_array->last_var, 0xff, op_array->T * sizeof(int));
+ /* Create uninitialized SSA variables for each CV */
+ for (j = 0; j < op_array->last_var; j++) {
+ var[j] = j;
+ }
+ ssa->vars_count = op_array->last_var;
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) {
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars;
+ int i;
+
+ if (!ssa->vars) {
+ ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var));
+ }
+ ssa_vars = ssa->vars;
+
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_vars[i].var = i;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_vars[i].var = -1;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+
+ for (i = op_array->last - 1; i >= 0; i--) {
+ zend_ssa_op *op = ssa->ops + i;
+
+ if (op->op1_use >= 0) {
+ op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
+ ssa_vars[op->op1_use].use_chain = i;
+ }
+ if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
+ op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
+ ssa_vars[op->op2_use].use_chain = i;
+ }
+ if (op->result_use >= 0 && op->result_use != op->op1_use && op->result_use != op->op2_use) {
+ op->res_use_chain = ssa_vars[op->result_use].use_chain;
+ ssa_vars[op->result_use].use_chain = i;
+ }
+ if (op->op1_def >= 0) {
+ ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var);
+ ssa_vars[op->op1_def].definition = i;
+ }
+ if (op->op2_def >= 0) {
+ ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var);
+ ssa_vars[op->op2_def].definition = i;
+ }
+ if (op->result_def >= 0) {
+ ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var);
+ ssa_vars[op->result_def].definition = i;
+ }
+ }
+
+ for (i = 0; i < ssa->cfg.blocks_count; i++) {
+ zend_ssa_phi *phi = ssa->blocks[i].phis;
+ while (phi) {
+ phi->block = i;
+ ssa_vars[phi->ssa_var].var = phi->var;
+ ssa_vars[phi->ssa_var].definition_phi = phi;
+ if (phi->pi >= 0) {
+ zend_ssa_phi *p;
+
+ ZEND_ASSERT(phi->sources[0] >= 0);
+ p = ssa_vars[phi->sources[0]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[0], p);
+ }
+ if (!p) {
+ phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain;
+ ssa_vars[phi->sources[0]].phi_use_chain = phi;
+ }
+ if (phi->has_range_constraint) {
+ /* min and max variables can't be used together */
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ if (constraint->min_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain;
+ ssa_vars[constraint->min_ssa_var].sym_use_chain = phi;
+ } else if (constraint->max_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain;
+ ssa_vars[constraint->max_ssa_var].sym_use_chain = phi;
+ }
+ }
+ } else {
+ int j;
+
+ for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
+ zend_ssa_phi *p;
+
+ ZEND_ASSERT(phi->sources[j] >= 0);
+ p = ssa_vars[phi->sources[j]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[j], p);
+ }
+ if (!p) {
+ phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain;
+ ssa_vars[phi->sources[j]].phi_use_chain = phi;
+ }
+ }
+ }
+ phi = phi->next;
+ }
+ }
+
+ /* Mark indirectly accessed variables */
+ for (i = 0; i < op_array->last_var; i++) {
+ if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
+ ssa_vars[i].alias = SYMTABLE_ALIAS;
+ } else if (zend_string_equals_literal(op_array->vars[i], "http_response_header")) {
+ ssa_vars[i].alias = HTTP_RESPONSE_HEADER_ALIAS;
+ }
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ if (ssa_vars[i].var < op_array->last_var) {
+ ssa_vars[i].alias = ssa_vars[ssa_vars[i].var].alias;
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
+{
+ if (ssa->vars[var].use_chain == op) {
+ ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ int use = ssa->vars[var].use_chain;
+
+ while (use >= 0) {
+ if (ssa->ops[use].result_use == var) {
+ if (ssa->ops[use].res_use_chain == op) {
+ ssa->ops[use].res_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].res_use_chain;
+ }
+ } else if (ssa->ops[use].op1_use == var) {
+ if (ssa->ops[use].op1_use_chain == op) {
+ ssa->ops[use].op1_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op1_use_chain;
+ }
+ } else if (ssa->ops[use].op2_use == var) {
+ if (ssa->ops[use].op2_use_chain == op) {
+ ssa->ops[use].op2_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op2_use_chain;
+ }
+ } else {
+ break;
+ }
+ }
+ /* something wrong */
+ ZEND_UNREACHABLE();
+ return 0;
+ }
+}
+/* }}} */
+
+void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) /* {{{ */
+{
+ if (ssa_op->result_use >= 0) {
+ zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->result_use);
+ ssa_op->result_use = -1;
+ ssa_op->res_use_chain = -1;
+ }
+ if (ssa_op->op1_use >= 0) {
+ if (ssa_op->op1_use != ssa_op->op2_use) {
+ zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op1_use);
+ } else {
+ ssa_op->op2_use_chain = ssa_op->op1_use_chain;
+ }
+ ssa_op->op1_use = -1;
+ ssa_op->op1_use_chain = -1;
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op2_use);
+ ssa_op->op2_use = -1;
+ ssa_op->op2_use_chain = -1;
+ }
+
+ /* We let the caller make sure that all defs are gone */
+ ZEND_ASSERT(ssa_op->result_def == -1);
+ ZEND_ASSERT(ssa_op->op1_def == -1);
+ ZEND_ASSERT(ssa_op->op2_def == -1);
+
+ MAKE_NOP(opline);
+}
+/* }}} */
+
+static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(zend_ssa *ssa, int var, zend_ssa_phi *p) /* {{{ */
+{
+ if (p->pi >= 0) {
+ return &p->use_chains[0];
+ } else {
+ int j;
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] == var) {
+ return &p->use_chains[j];
+ }
+ }
+ }
+ ZEND_UNREACHABLE();
+ return NULL;
+}
+/* }}} */
+
+/* May be called even if source is not used in the phi (useful when removing uses in a phi
+ * with multiple identical operands) */
+static inline void zend_ssa_remove_use_of_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int source, zend_ssa_phi *next_use_phi) /* {{{ */
+{
+ zend_ssa_phi **cur = &ssa->vars[source].phi_use_chain;
+ while (*cur && *cur != phi) {
+ cur = zend_ssa_next_use_phi_ptr(ssa, source, *cur);
+ }
+ if (*cur) {
+ *cur = next_use_phi;
+ }
+}
+/* }}} */
+
+static void zend_ssa_remove_uses_of_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+{
+ int source;
+ FOREACH_PHI_SOURCE(phi, source) {
+ zend_ssa_remove_use_of_phi_source(ssa, phi, source, zend_ssa_next_use_phi(ssa, source, phi));
+ } FOREACH_PHI_SOURCE_END();
+}
+/* }}} */
+
+static void zend_ssa_remove_phi_from_block(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+{
+ zend_ssa_block *block = &ssa->blocks[phi->block];
+ zend_ssa_phi **cur = &block->phis;
+ while (*cur != phi) {
+ ZEND_ASSERT(*cur != NULL);
+ cur = &(*cur)->next;
+ }
+ *cur = (*cur)->next;
+}
+/* }}} */
+
+static inline void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */
+{
+ if (ssa_op->op1_def >= 0) {
+ zend_ssa_remove_uses_of_var(ssa, ssa_op->op1_def);
+ zend_ssa_remove_op1_def(ssa, ssa_op);
+ }
+ if (ssa_op->op2_def >= 0) {
+ zend_ssa_remove_uses_of_var(ssa, ssa_op->op2_def);
+ zend_ssa_remove_op2_def(ssa, ssa_op);
+ }
+ if (ssa_op->result_def >= 0) {
+ zend_ssa_remove_uses_of_var(ssa, ssa_op->result_def);
+ zend_ssa_remove_result_def(ssa, ssa_op);
+ }
+}
+/* }}} */
+
+static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int pred_offset, int predecessors_count) /* {{{ */
+{
+ int j, var_num = phi->sources[pred_offset];
+ zend_ssa_phi *next_phi = phi->use_chains[pred_offset];
+
+ predecessors_count--;
+ if (pred_offset < predecessors_count) {
+ memmove(phi->sources + pred_offset, phi->sources + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(uint32_t));
+ memmove(phi->use_chains + pred_offset, phi->use_chains + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(zend_ssa_phi*));
+ }
+
+ /* Check if they same var is used in a different phi operand as well, in this case we don't
+ * need to adjust the use chain (but may have to move the next pointer). */
+ for (j = 0; j < predecessors_count; j++) {
+ if (phi->sources[j] == var_num) {
+ if (j < pred_offset) {
+ ZEND_ASSERT(next_phi == NULL);
+ } else if (j >= pred_offset) {
+ phi->use_chains[j] = next_phi;
+ }
+ return;
+ }
+ }
+
+ /* Variable only used in one operand, remove the phi from the use chain. */
+ zend_ssa_remove_use_of_phi_source(ssa, phi, var_num, next_phi);
+}
+/* }}} */
+
+void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+{
+ ZEND_ASSERT(phi->ssa_var >= 0);
+ ZEND_ASSERT(ssa->vars[phi->ssa_var].use_chain < 0
+ && ssa->vars[phi->ssa_var].phi_use_chain == NULL);
+ zend_ssa_remove_uses_of_phi_sources(ssa, phi);
+ zend_ssa_remove_phi_from_block(ssa, phi);
+ ssa->vars[phi->ssa_var].definition_phi = NULL;
+ phi->ssa_var = -1;
+}
+/* }}} */
+
+void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */
+{
+ zend_ssa_var *var = &ssa->vars[var_num];
+ zend_ssa_phi *phi;
+ int use;
+ FOREACH_PHI_USE(var, phi) {
+ int i, end = NUM_PHI_SOURCES(phi);
+ for (i = 0; i < end; i++) {
+ if (phi->sources[i] == var_num) {
+ phi->use_chains[i] = NULL;
+ }
+ }
+ } FOREACH_PHI_USE_END();
+ var->phi_use_chain = NULL;
+ FOREACH_USE(var, use) {
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+ if (ssa_op->op1_use == var_num) {
+ ssa_op->op1_use = -1;
+ ssa_op->op1_use_chain = -1;
+ }
+ if (ssa_op->op2_use == var_num) {
+ ssa_op->op2_use = -1;
+ ssa_op->op2_use_chain = -1;
+ }
+ if (ssa_op->result_use == var_num) {
+ ssa_op->result_use = -1;
+ ssa_op->res_use_chain = -1;
+ }
+ } FOREACH_USE_END();
+ var->use_chain = -1;
+}
+/* }}} */
+
+void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
+{
+ zend_basic_block *next_block = &ssa->cfg.blocks[to];
+ zend_ssa_block *next_ssa_block = &ssa->blocks[to];
+ zend_ssa_phi *phi;
+ int j;
+
+ /* Find at which predecessor offset this block is referenced */
+ int pred_offset = -1;
+ int *predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset];
+
+ for (j = 0; j < next_block->predecessors_count; j++) {
+ if (predecessors[j] == from) {
+ pred_offset = j;
+ break;
+ }
+ }
+
+ /* If there are duplicate successors, the predecessors may have been removed in
+ * a previous iteration already. */
+ if (pred_offset == -1) {
+ return;
+ }
+
+ /* For phis in successor blocks, remove the operands associated with this block */
+ for (phi = next_ssa_block->phis; phi; phi = phi->next) {
+ if (phi->pi >= 0) {
+ if (phi->pi == from) {
+ zend_ssa_rename_var_uses(ssa, phi->ssa_var, phi->sources[0], /* update_types */ 0);
+ zend_ssa_remove_phi(ssa, phi);
+ }
+ } else {
+ ZEND_ASSERT(phi->sources[pred_offset] >= 0);
+ zend_ssa_remove_phi_source(ssa, phi, pred_offset, next_block->predecessors_count);
+ }
+ }
+
+ /* Remove this predecessor */
+ next_block->predecessors_count--;
+ if (pred_offset < next_block->predecessors_count) {
+ predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset + pred_offset];
+ memmove(predecessors, predecessors + 1, (next_block->predecessors_count - pred_offset) * sizeof(uint32_t));
+ }
+}
+/* }}} */
+
+void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ */
+{
+ zend_basic_block *block = &ssa->cfg.blocks[i];
+ zend_ssa_block *ssa_block = &ssa->blocks[i];
+ int *predecessors;
+ zend_ssa_phi *phi;
+ int j, s;
+
+ block->flags &= ~ZEND_BB_REACHABLE;
+
+ /* Removes phis in this block */
+ for (phi = ssa_block->phis; phi; phi = phi->next) {
+ zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
+ zend_ssa_remove_phi(ssa, phi);
+ }
+
+ /* Remove instructions in this block */
+ for (j = block->start; j < block->start + block->len; j++) {
+ if (op_array->opcodes[j].opcode == ZEND_NOP) {
+ continue;
+ }
+
+ zend_ssa_remove_defs_of_instr(ssa, &ssa->ops[j]);
+ zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]);
+ }
+
+ for (s = 0; s < block->successors_count; s++) {
+ zend_ssa_remove_predecessor(ssa, i, block->successors[s]);
+ }
+
+ /* Remove successors of predecessors */
+ predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
+ for (j = 0; j < block->predecessors_count; j++) {
+ if (predecessors[j] >= 0) {
+ zend_basic_block *prev_block = &ssa->cfg.blocks[predecessors[j]];
+
+ for (s = 0; s < prev_block->successors_count; s++) {
+ if (prev_block->successors[s] == i) {
+ memmove(prev_block->successors + s,
+ prev_block->successors + s + 1,
+ sizeof(int) * (prev_block->successors_count - s - 1));
+ prev_block->successors_count--;
+ s--;
+ }
+ }
+ }
+ }
+
+ block->successors_count = 0;
+ block->predecessors_count = 0;
+
+ /* Remove from dominators tree */
+ if (block->idom >= 0) {
+ j = ssa->cfg.blocks[block->idom].children;
+ if (j == i) {
+ ssa->cfg.blocks[block->idom].children = block->next_child;
+ } else if (j >= 0) {
+ while (ssa->cfg.blocks[j].next_child >= 0) {
+ if (ssa->cfg.blocks[j].next_child == i) {
+ ssa->cfg.blocks[j].next_child = block->next_child;
+ break;
+ }
+ j = ssa->cfg.blocks[j].next_child;
+ }
+ }
+ }
+ block->idom = -1;
+ block->level = -1;
+ block->children = -1;
+ block->next_child = -1;
+}
+/* }}} */
+
+static void propagate_phi_type_widening(zend_ssa *ssa, int var) /* {{{ */
+{
+ zend_ssa_phi *phi;
+ FOREACH_PHI_USE(&ssa->vars[var], phi) {
+ if (ssa->var_info[var].type & ~ssa->var_info[phi->ssa_var].type) {
+ ssa->var_info[phi->ssa_var].type |= ssa->var_info[var].type;
+ propagate_phi_type_widening(ssa, phi->ssa_var);
+ }
+ } FOREACH_PHI_USE_END();
+}
+/* }}} */
+
+void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types) /* {{{ */
+{
+ zend_ssa_var *old_var = &ssa->vars[old];
+ zend_ssa_var *new_var = &ssa->vars[new];
+ int use;
+ zend_ssa_phi *phi;
+
+ ZEND_ASSERT(old >= 0 && new >= 0);
+ ZEND_ASSERT(old != new);
+
+ /* Only a no_val is both variables are */
+ new_var->no_val &= old_var->no_val;
+
+ /* Update ssa_op use chains */
+ FOREACH_USE(old_var, use) {
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+
+ /* If the op already uses the new var, don't add the op to the use
+ * list again. Instead move the use_chain to the correct operand. */
+ bool add_to_use_chain = 1;
+ if (ssa_op->result_use == new) {
+ add_to_use_chain = 0;
+ } else if (ssa_op->op1_use == new) {
+ if (ssa_op->result_use == old) {
+ ssa_op->res_use_chain = ssa_op->op1_use_chain;
+ ssa_op->op1_use_chain = -1;
+ }
+ add_to_use_chain = 0;
+ } else if (ssa_op->op2_use == new) {
+ if (ssa_op->result_use == old) {
+ ssa_op->res_use_chain = ssa_op->op2_use_chain;
+ ssa_op->op2_use_chain = -1;
+ } else if (ssa_op->op1_use == old) {
+ ssa_op->op1_use_chain = ssa_op->op2_use_chain;
+ ssa_op->op2_use_chain = -1;
+ }
+ add_to_use_chain = 0;
+ }
+
+ /* Perform the actual renaming */
+ if (ssa_op->result_use == old) {
+ ssa_op->result_use = new;
+ }
+ if (ssa_op->op1_use == old) {
+ ssa_op->op1_use = new;
+ }
+ if (ssa_op->op2_use == old) {
+ ssa_op->op2_use = new;
+ }
+
+ /* Add op to use chain of new var (if it isn't already). We use the
+ * first use chain of (result, op1, op2) that has the new variable. */
+ if (add_to_use_chain) {
+ if (ssa_op->result_use == new) {
+ ssa_op->res_use_chain = new_var->use_chain;
+ new_var->use_chain = use;
+ } else if (ssa_op->op1_use == new) {
+ ssa_op->op1_use_chain = new_var->use_chain;
+ new_var->use_chain = use;
+ } else {
+ ZEND_ASSERT(ssa_op->op2_use == new);
+ ssa_op->op2_use_chain = new_var->use_chain;
+ new_var->use_chain = use;
+ }
+ }
+ } FOREACH_USE_END();
+ old_var->use_chain = -1;
+
+ /* Update phi use chains */
+ FOREACH_PHI_USE(old_var, phi) {
+ int j;
+ bool after_first_new_source = 0;
+
+ /* If the phi already uses the new var, find its use chain, as we may
+ * need to move it to a different source operand. */
+ zend_ssa_phi **existing_use_chain_ptr = NULL;
+ for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) {
+ if (phi->sources[j] == new) {
+ existing_use_chain_ptr = &phi->use_chains[j];
+ break;
+ }
+ }
+
+ for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) {
+ if (phi->sources[j] == new) {
+ after_first_new_source = 1;
+ } else if (phi->sources[j] == old) {
+ phi->sources[j] = new;
+
+ /* Either move existing use chain to this source, or add the phi
+ * to the phi use chain of the new variables. Do this only once. */
+ if (!after_first_new_source) {
+ if (existing_use_chain_ptr) {
+ phi->use_chains[j] = *existing_use_chain_ptr;
+ *existing_use_chain_ptr = NULL;
+ } else {
+ phi->use_chains[j] = new_var->phi_use_chain;
+ new_var->phi_use_chain = phi;
+ }
+ after_first_new_source = 1;
+ } else {
+ phi->use_chains[j] = NULL;
+ }
+ }
+ }
+
+ /* Make sure phi result types are not incorrectly narrow after renaming.
+ * This should not normally happen, but can occur if we DCE an assignment
+ * or unset and there is an improper phi-indirected use lateron. */
+ // TODO Alternatively we could rerun type-inference after DCE
+ if (update_types && (ssa->var_info[new].type & ~ssa->var_info[phi->ssa_var].type)) {
+ ssa->var_info[phi->ssa_var].type |= ssa->var_info[new].type;
+ propagate_phi_type_widening(ssa, phi->ssa_var);
+ }
+ } FOREACH_PHI_USE_END();
+ old_var->phi_use_chain = NULL;
+}
+/* }}} */
diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h
new file mode 100644
index 0000000000..ddc9f95f43
--- /dev/null
+++ b/Zend/Optimizer/zend_ssa.h
@@ -0,0 +1,326 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_SSA_H
+#define ZEND_SSA_H
+
+#include "zend_optimizer.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_ssa_range {
+ zend_long min;
+ zend_long max;
+ bool underflow;
+ bool overflow;
+} zend_ssa_range;
+
+typedef enum _zend_ssa_negative_lat {
+ NEG_NONE = 0,
+ NEG_INIT = 1,
+ NEG_INVARIANT = 2,
+ NEG_USE_LT = 3,
+ NEG_USE_GT = 4,
+ NEG_UNKNOWN = 5
+} zend_ssa_negative_lat;
+
+/* Special kind of SSA Phi function used in eSSA */
+typedef struct _zend_ssa_range_constraint {
+ zend_ssa_range range; /* simple range constraint */
+ int min_var;
+ int max_var;
+ int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
+ int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
+ zend_ssa_negative_lat negative;
+} zend_ssa_range_constraint;
+
+typedef struct _zend_ssa_type_constraint {
+ uint32_t type_mask; /* Type mask to intersect with */
+ zend_class_entry *ce; /* Class entry for instanceof constraints */
+} zend_ssa_type_constraint;
+
+typedef union _zend_ssa_pi_constraint {
+ zend_ssa_range_constraint range;
+ zend_ssa_type_constraint type;
+} zend_ssa_pi_constraint;
+
+/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
+typedef struct _zend_ssa_phi zend_ssa_phi;
+struct _zend_ssa_phi {
+ zend_ssa_phi *next; /* next Phi in the same BB */
+ int pi; /* if >= 0 this is actually a e-SSA Pi */
+ zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
+ int var; /* Original CV, VAR or TMP variable index */
+ int ssa_var; /* SSA variable index */
+ int block; /* current BB index */
+ int visited : 1; /* flag to avoid recursive processing */
+ int has_range_constraint : 1;
+ zend_ssa_phi **use_chains;
+ zend_ssa_phi *sym_use_chain;
+ int *sources; /* Array of SSA IDs that produce this var.
+ As many as this block has
+ predecessors. */
+};
+
+typedef struct _zend_ssa_block {
+ zend_ssa_phi *phis;
+} zend_ssa_block;
+
+typedef struct _zend_ssa_op {
+ int op1_use;
+ int op2_use;
+ int result_use;
+ int op1_def;
+ int op2_def;
+ int result_def;
+ int op1_use_chain;
+ int op2_use_chain;
+ int res_use_chain;
+} zend_ssa_op;
+
+typedef enum _zend_ssa_alias_kind {
+ NO_ALIAS,
+ SYMTABLE_ALIAS,
+ HTTP_RESPONSE_HEADER_ALIAS
+} zend_ssa_alias_kind;
+
+typedef enum _zend_ssa_escape_state {
+ ESCAPE_STATE_UNKNOWN,
+ ESCAPE_STATE_NO_ESCAPE,
+ ESCAPE_STATE_FUNCTION_ESCAPE,
+ ESCAPE_STATE_GLOBAL_ESCAPE
+} zend_ssa_escape_state;
+
+typedef struct _zend_ssa_var {
+ int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
+ int scc; /* strongly connected component */
+ int definition; /* opcode that defines this value */
+ zend_ssa_phi *definition_phi; /* phi that defines this value */
+ int use_chain; /* uses of this value, linked through opN_use_chain */
+ zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
+ zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */
+ unsigned int no_val : 1; /* value doesn't matter (used as op1 in ZEND_ASSIGN) */
+ unsigned int scc_entry : 1;
+ unsigned int alias : 2; /* value may be changed indirectly */
+ unsigned int escape_state : 2;
+} zend_ssa_var;
+
+typedef struct _zend_ssa_var_info {
+ uint32_t type; /* inferred type (see zend_inference.h) */
+ zend_ssa_range range;
+ zend_class_entry *ce;
+ unsigned int has_range : 1;
+ unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
+ unsigned int recursive : 1;
+ unsigned int use_as_double : 1;
+ unsigned int delayed_fetch_this : 1;
+ unsigned int avoid_refcounting : 1;
+ unsigned int guarded_reference : 1;
+ unsigned int indirect_reference : 1; /* IS_INDIRECT returned by FETCH_DIM_W/FETCH_OBJ_W */
+} zend_ssa_var_info;
+
+typedef struct _zend_ssa {
+ zend_cfg cfg; /* control flow graph */
+ int vars_count; /* number of SSA variables */
+ int sccs; /* number of SCCs */
+ zend_ssa_block *blocks; /* array of SSA blocks */
+ zend_ssa_op *ops; /* array of SSA instructions */
+ zend_ssa_var *vars; /* use/def chain of SSA variables */
+ zend_ssa_var_info *var_info;
+} zend_ssa;
+
+BEGIN_EXTERN_C()
+
+ZEND_API int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
+ZEND_API int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
+ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
+
+void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
+void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
+void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
+void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
+void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
+void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types);
+
+static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var)
+{
+ ZEND_ASSERT(var->definition >= 0);
+ ZEND_ASSERT(var->use_chain < 0);
+ ZEND_ASSERT(!var->phi_use_chain);
+ var->definition = -1;
+}
+
+static zend_always_inline void zend_ssa_remove_result_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
+{
+ zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
+ _zend_ssa_remove_def(var);
+ ssa_op->result_def = -1;
+}
+
+static zend_always_inline void zend_ssa_remove_op1_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
+{
+ zend_ssa_var *var = &ssa->vars[ssa_op->op1_def];
+ _zend_ssa_remove_def(var);
+ ssa_op->op1_def = -1;
+}
+
+static zend_always_inline void zend_ssa_remove_op2_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
+{
+ zend_ssa_var *var = &ssa->vars[ssa_op->op2_def];
+ _zend_ssa_remove_def(var);
+ ssa_op->op2_def = -1;
+}
+
+END_EXTERN_C()
+
+static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
+{
+ ssa_op += use;
+ if (ssa_op->op1_use == var) {
+ return ssa_op->op1_use_chain;
+ } else if (ssa_op->op2_use == var) {
+ return ssa_op->op2_use_chain;
+ } else {
+ return ssa_op->res_use_chain;
+ }
+}
+
+static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p)
+{
+ if (p->pi >= 0) {
+ return p->use_chains[0];
+ } else {
+ int j;
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] == var) {
+ return p->use_chains[j];
+ }
+ }
+ }
+ return NULL;
+}
+
+static zend_always_inline bool zend_ssa_is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
+{
+ if (opline->opcode == ZEND_ASSIGN
+ || opline->opcode == ZEND_UNSET_CV
+ || opline->opcode == ZEND_BIND_GLOBAL
+ || opline->opcode == ZEND_BIND_STATIC) {
+ return ssa_op->op1_use == var && ssa_op->op2_use != var;
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ return ssa_op->op2_use == var && ssa_op->op1_use != var;
+ }
+ 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;
+}
+
+static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) {
+ /* Rename def to use if possible. Mark variable as not defined otherwise. */
+ if (ssa_op->op1_def >= 0) {
+ if (ssa_op->op1_use >= 0) {
+ zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
+ }
+ ssa->vars[ssa_op->op1_def].definition = -1;
+ ssa_op->op1_def = -1;
+ }
+ if (ssa_op->op2_def >= 0) {
+ if (ssa_op->op2_use >= 0) {
+ zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1);
+ }
+ ssa->vars[ssa_op->op2_def].definition = -1;
+ ssa_op->op2_def = -1;
+ }
+ if (ssa_op->result_def >= 0) {
+ if (ssa_op->result_use >= 0) {
+ zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1);
+ }
+ ssa->vars[ssa_op->result_def].definition = -1;
+ ssa_op->result_def = -1;
+ }
+}
+
+#define NUM_PHI_SOURCES(phi) \
+ ((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count))
+
+/* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue"
+ * and changing the use chain of the current element */
+#define FOREACH_USE(var, use) do { \
+ int _var_num = (var) - ssa->vars, next; \
+ for (use = (var)->use_chain; use >= 0; use = next) { \
+ next = zend_ssa_next_use(ssa->ops, _var_num, use);
+#define FOREACH_USE_END() \
+ } \
+} while (0)
+
+#define FOREACH_PHI_USE(var, phi) do { \
+ int _var_num = (var) - ssa->vars; \
+ zend_ssa_phi *next_phi; \
+ for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \
+ next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi);
+#define FOREACH_PHI_USE_END() \
+ } \
+} while (0)
+
+#define FOREACH_PHI_SOURCE(phi, source) do { \
+ zend_ssa_phi *_phi = (phi); \
+ int _i, _end = NUM_PHI_SOURCES(phi); \
+ for (_i = 0; _i < _end; _i++) { \
+ ZEND_ASSERT(_phi->sources[_i] >= 0); \
+ source = _phi->sources[_i];
+#define FOREACH_PHI_SOURCE_END() \
+ } \
+} while (0)
+
+#define FOREACH_PHI(phi) do { \
+ int _i; \
+ for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
+ phi = ssa->blocks[_i].phis; \
+ for (; phi; phi = phi->next) {
+#define FOREACH_PHI_END() \
+ } \
+ } \
+} while (0)
+
+#define FOREACH_BLOCK(block) do { \
+ int _i; \
+ for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
+ (block) = &ssa->cfg.blocks[_i]; \
+ if (!((block)->flags & ZEND_BB_REACHABLE)) { \
+ continue; \
+ }
+#define FOREACH_BLOCK_END() \
+ } \
+} while (0)
+
+/* Does not support "break" */
+#define FOREACH_INSTR_NUM(i) do { \
+ zend_basic_block *_block; \
+ FOREACH_BLOCK(_block) { \
+ uint32_t _end = _block->start + _block->len; \
+ for ((i) = _block->start; (i) < _end; (i)++) {
+#define FOREACH_INSTR_NUM_END() \
+ } \
+ } FOREACH_BLOCK_END(); \
+} while (0)
+
+#endif /* ZEND_SSA_H */
diff --git a/Zend/Optimizer/zend_worklist.h b/Zend/Optimizer/zend_worklist.h
new file mode 100644
index 0000000000..2f3e3dd979
--- /dev/null
+++ b/Zend/Optimizer/zend_worklist.h
@@ -0,0 +1,121 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andy Wingo <wingo@igalia.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef _ZEND_WORKLIST_H_
+#define _ZEND_WORKLIST_H_
+
+#include "zend_arena.h"
+#include "zend_bitset.h"
+
+typedef struct _zend_worklist_stack {
+ int *buf;
+ int len;
+ int capacity;
+} zend_worklist_stack;
+
+#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
+ (s)->len = 0; \
+ (s)->capacity = _len; \
+ } while (0)
+
+#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
+ free_alloca((s)->buf, use_heap)
+
+static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
+{
+ ZEND_ASSERT(len >= 0);
+
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
+ stack->len = 0;
+ stack->capacity = len;
+
+ return SUCCESS;
+}
+
+static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
+{
+ ZEND_ASSERT(stack->len < stack->capacity);
+ stack->buf[stack->len++] = i;
+}
+
+static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[stack->len - 1];
+}
+
+static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[--stack->len];
+}
+
+typedef struct _zend_worklist {
+ zend_bitset visited;
+ zend_worklist_stack stack;
+} zend_worklist;
+
+#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
+ (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
+ (w)->stack.len = 0; \
+ (w)->stack.capacity = _len; \
+ (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
+ } while (0)
+
+#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
+ free_alloca((w)->stack.buf, use_heap)
+
+static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
+{
+ ZEND_ASSERT(len >= 0);
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
+}
+
+static inline int zend_worklist_len(zend_worklist *worklist)
+{
+ return worklist->stack.len;
+}
+
+static inline int zend_worklist_push(zend_worklist *worklist, int i)
+{
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
+
+ if (zend_bitset_in(worklist->visited, i)) {
+ return 0;
+ }
+
+ zend_bitset_incl(worklist->visited, i);
+ zend_worklist_stack_push(&worklist->stack, i);
+ return 1;
+}
+
+static inline int zend_worklist_peek(zend_worklist *worklist)
+{
+ return zend_worklist_stack_peek(&worklist->stack);
+}
+
+static inline int zend_worklist_pop(zend_worklist *worklist)
+{
+ /* Does not clear visited flag */
+ return zend_worklist_stack_pop(&worklist->stack);
+}
+
+#endif /* _ZEND_WORKLIST_H_ */
diff --git a/Zend/Zend.m4 b/Zend/Zend.m4
index 781e51d3e4..081a237012 100644
--- a/Zend/Zend.m4
+++ b/Zend/Zend.m4
@@ -209,6 +209,10 @@ fi
test -n "$GCC" && CFLAGS="-Wall -Wextra -Wno-strict-aliasing -Wno-implicit-fallthrough -Wno-unused-parameter -Wno-sign-compare $CFLAGS"
dnl Check if compiler supports -Wno-clobbered (only GCC)
AX_CHECK_COMPILE_FLAG([-Wno-clobbered], CFLAGS="-Wno-clobbered $CFLAGS", , [-Werror])
+AX_CHECK_COMPILE_FLAG([-Wduplicated-cond], CFLAGS="-Wduplicated-cond $CFLAGS", , [-Werror])
+AX_CHECK_COMPILE_FLAG([-Wlogical-op], CFLAGS="-Wlogical-op $CFLAGS", , [-Werror])
+AX_CHECK_COMPILE_FLAG([-Wformat-truncation], CFLAGS="-Wformat-truncation $CFLAGS", , [-Werror])
+AX_CHECK_COMPILE_FLAG([-fno-common], CFLAGS="-fno-common $CFLAGS", , [-Werror])
test -n "$DEBUG_CFLAGS" && CFLAGS="$CFLAGS $DEBUG_CFLAGS"
diff --git a/Zend/tests/anon/015.phpt b/Zend/tests/anon/015.phpt
new file mode 100644
index 0000000000..f55c4b2605
--- /dev/null
+++ b/Zend/tests/anon/015.phpt
@@ -0,0 +1,30 @@
+--TEST--
+static variables in methods inherited from parent class
+--FILE--
+<?php
+class C {
+ function foo ($y = null) {
+ static $x = null;
+ if (!is_null($y)) {
+ $x = [$y];
+ }
+ return $x;
+ }
+}
+$c = new C();
+$c->foo(42);
+$d = new class extends C {};
+var_dump($d->foo());
+var_dump($d->foo(24));
+var_dump($c->foo());
+?>
+--EXPECT--
+NULL
+array(1) {
+ [0]=>
+ int(24)
+}
+array(1) {
+ [0]=>
+ int(42)
+}
diff --git a/Zend/tests/anon/016.phpt b/Zend/tests/anon/016.phpt
new file mode 100644
index 0000000000..a5607cda74
--- /dev/null
+++ b/Zend/tests/anon/016.phpt
@@ -0,0 +1,31 @@
+--TEST--
+static variables in methods inherited from parent class (can't cache objects)
+--FILE--
+<?php
+class C {
+ function foo ($y = null) {
+ static $x = null;
+ if (!is_null($y)) {
+ $x = [$y];
+ }
+ return $x;
+ }
+}
+$c = new C();
+$c->foo(new stdClass);
+$d = new class extends C {};
+var_dump($d->foo());
+var_dump($d->foo(24));
+var_dump($c->foo());
+?>
+--EXPECT--
+NULL
+array(1) {
+ [0]=>
+ int(24)
+}
+array(1) {
+ [0]=>
+ object(stdClass)#2 (0) {
+ }
+}
diff --git a/Zend/tests/array_self_add_globals.phpt b/Zend/tests/array_self_add_globals.phpt
index ebad7c3fdf..78dd3bf9a3 100644
--- a/Zend/tests/array_self_add_globals.phpt
+++ b/Zend/tests/array_self_add_globals.phpt
@@ -2,7 +2,6 @@
Add $GLOBALS to itself
--FILE--
<?php
-$GLOBALS += $GLOBALS;
$x = $GLOBALS + $GLOBALS;
?>
===DONE===
diff --git a/Zend/tests/array_unpack/non_integer_keys.phpt b/Zend/tests/array_unpack/non_integer_keys.phpt
index a5e407743c..ab7a20ac86 100644
--- a/Zend/tests/array_unpack/non_integer_keys.phpt
+++ b/Zend/tests/array_unpack/non_integer_keys.phpt
@@ -1,5 +1,5 @@
--TEST--
-Array unpacking does not work with non-integer keys
+Array unpacking does not work with non-integer/string keys
--FILE--
<?php
function gen() {
@@ -15,4 +15,4 @@ try {
?>
--EXPECT--
-Exception: Cannot unpack Traversable with non-integer keys
+Exception: Keys must be of type int|string during array unpacking
diff --git a/Zend/tests/array_unpack/string_keys.phpt b/Zend/tests/array_unpack/string_keys.phpt
index e4cfd77f58..d446e69cab 100644
--- a/Zend/tests/array_unpack/string_keys.phpt
+++ b/Zend/tests/array_unpack/string_keys.phpt
@@ -1,22 +1,58 @@
--TEST--
-array unpacking with string keys (not supported)
+Array unpacking with string keys
--FILE--
<?php
-try {
- $array = [1, 2, "foo" => 3, 4];
- var_dump([...$array]);
-} catch (Error $ex) {
- var_dump($ex->getMessage());
-}
-try {
- $iterator = new ArrayIterator([1, 2, "foo" => 3, 4]);
- var_dump([...$iterator]);
-} catch (Error $ex) {
- var_dump($ex->getMessage());
+// Works with both arrays and Traversables.
+$array = [1, 2, "foo" => 3, 4];
+var_dump([...$array]);
+
+$iterator = new ArrayIterator([1, 2, "foo" => 3, 4]);
+var_dump([...$iterator]);
+
+// Test overwriting behavior.
+$array1 = ["foo" => 1];
+$array2 = ["foo" => 2];
+var_dump(["foo" => 0, ...$array1, ...$array2]);
+var_dump(["foo" => 0, ...$array1, ...$array2, "foo" => 3]);
+
+// Test numeric string key from iterator.
+function gen() {
+ yield "42" => 42;
}
+var_dump([...gen()]);
?>
--EXPECT--
-string(36) "Cannot unpack array with string keys"
-string(42) "Cannot unpack Traversable with string keys"
+array(4) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+ ["foo"]=>
+ int(3)
+ [2]=>
+ int(4)
+}
+array(4) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+ ["foo"]=>
+ int(3)
+ [2]=>
+ int(4)
+}
+array(1) {
+ ["foo"]=>
+ int(2)
+}
+array(1) {
+ ["foo"]=>
+ int(3)
+}
+array(1) {
+ [0]=>
+ int(42)
+}
diff --git a/Zend/tests/array_unpack/unpack_string_keys_compile_time.phpt b/Zend/tests/array_unpack/unpack_string_keys_compile_time.phpt
index 1401fb9bd5..df58d78a6a 100644
--- a/Zend/tests/array_unpack/unpack_string_keys_compile_time.phpt
+++ b/Zend/tests/array_unpack/unpack_string_keys_compile_time.phpt
@@ -1,10 +1,23 @@
--TEST--
-Unpacking of string keys detected at compile-time
+Unpacking of string keys is supported at compile-time
--FILE--
<?php
var_dump([...['a' => 'b']]);
+var_dump(['a' => 'X', ...['a' => 'b']]);
+var_dump([...['a' => 'b'], 'a' => 'X']);
?>
---EXPECTF--
-Fatal error: Cannot unpack array with string keys in %s on line %d
+--EXPECT--
+array(1) {
+ ["a"]=>
+ string(1) "b"
+}
+array(1) {
+ ["a"]=>
+ string(1) "b"
+}
+array(1) {
+ ["a"]=>
+ string(1) "X"
+}
diff --git a/Zend/tests/attributes/016_custom_attribute_validation.phpt b/Zend/tests/attributes/016_custom_attribute_validation.phpt
index 0f3167f986..11ffeaa774 100644
--- a/Zend/tests/attributes/016_custom_attribute_validation.phpt
+++ b/Zend/tests/attributes/016_custom_attribute_validation.phpt
@@ -2,8 +2,8 @@
Attribute validation callback of internal attributes.
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) {
- echo "skip requires zend-test extension\n";
+if (!extension_loaded('zend_test')) {
+ echo "skip requires zend_test extension\n";
}
--FILE--
<?php
diff --git a/Zend/tests/bug27798.phpt b/Zend/tests/bug27798.phpt
index 310fd97991..c16c5c825d 100644
--- a/Zend/tests/bug27798.phpt
+++ b/Zend/tests/bug27798.phpt
@@ -57,12 +57,12 @@ array(3) {
}
Child::__construct
array(3) {
- ["Baz"]=>
- int(4)
["Foo"]=>
int(1)
["Bar"]=>
int(2)
+ ["Baz"]=>
+ int(4)
}
array(1) {
["Foo"]=>
diff --git a/Zend/tests/bug43201.phpt b/Zend/tests/bug43201.phpt
index 49816ea1c1..c93b118e16 100644
--- a/Zend/tests/bug43201.phpt
+++ b/Zend/tests/bug43201.phpt
@@ -30,25 +30,37 @@ Warning: Undefined variable $ref in %s on line %d
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
Warning: Undefined variable $undef in %s on line %d
+Deprecated: chop(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+
Notice: Indirect modification of overloaded property Foo::$arr has no effect in %sbug43201.php on line 17
ok
diff --git a/Zend/tests/bug53826.phpt b/Zend/tests/bug53826.phpt
new file mode 100644
index 0000000000..3f0a069536
--- /dev/null
+++ b/Zend/tests/bug53826.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Bug #53826: __callStatic fired in base class through a parent call if the method is private
+--FILE--
+<?php
+
+class A1 {
+ public function __call($method, $args) { echo "__call\n"; }
+ public static function __callStatic($method, $args) { echo "__callStatic\n"; }
+}
+
+class A2 { // A1 with private function test
+ public function __call($method, $args) { echo "__call\n"; }
+ public static function __callStatic($method, $args) { echo "__callStatic\n"; }
+ private function test() {}
+}
+
+class B1 extends A1 {
+ public function test(){ parent::test(); }
+}
+
+class B2 extends A2 {
+ public function test(){ parent::test(); }
+}
+
+$test1 = new B1;
+$test2 = new B2;
+$test1->test();
+$test2->test();
+
+?>
+--EXPECT--
+__call
+__call
diff --git a/Zend/tests/bug60536_003.phpt b/Zend/tests/bug60536_003.phpt
index 3ba23b9288..8696591bb2 100644
--- a/Zend/tests/bug60536_003.phpt
+++ b/Zend/tests/bug60536_003.phpt
@@ -32,14 +32,14 @@ var_dump($b);
?>
--EXPECTF--
object(SubclassA)#%d (2) {
- ["hello":"SubclassA":private]=>
- int(0)
["hello":"BaseWithPropA":private]=>
int(0)
+ ["hello":"SubclassA":private]=>
+ int(0)
}
object(SubclassB)#%d (2) {
- ["hello":"SubclassB":private]=>
- int(0)
["hello":"BaseWithTPropB":private]=>
int(0)
+ ["hello":"SubclassB":private]=>
+ int(0)
}
diff --git a/Zend/tests/bug64677.phpt b/Zend/tests/bug64677.phpt
index 2dcd00ce0a..c3b168bd83 100644
--- a/Zend/tests/bug64677.phpt
+++ b/Zend/tests/bug64677.phpt
@@ -7,7 +7,7 @@ class cat {
}
}
$cat = new cat();
-$cat->show_output('Files: ', trim(`cd .`)); // this gives invalid args to shell_exec
+$cat->show_output('Files: ', trim((string) `cd .`)); // this gives invalid args to shell_exec
$cat->show_output('Files: ', `cd .`); // this causes a segmentation fault
$cat->show_output(`cd .`); // this causes a segmentation fault
diff --git a/Zend/tests/bug70895.phpt b/Zend/tests/bug70895.phpt
index 1a28d9ef5c..afbea1c91d 100644
--- a/Zend/tests/bug70895.phpt
+++ b/Zend/tests/bug70895.phpt
@@ -20,6 +20,6 @@ try {
}
?>
--EXPECT--
-array_map(): Argument #1 ($callback) must be a valid callback, function "%n" not found or invalid function name
-array_map(): Argument #1 ($callback) must be a valid callback, function "%n %i" not found or invalid function name
-array_map(): Argument #1 ($callback) must be a valid callback, function "%n %i aoeu %f aoeu %p" not found or invalid function name
+array_map(): Argument #1 ($callback) must be a valid callback or null, function "%n" not found or invalid function name
+array_map(): Argument #1 ($callback) must be a valid callback or null, function "%n %i" not found or invalid function name
+array_map(): Argument #1 ($callback) must be a valid callback or null, function "%n %i aoeu %f aoeu %p" not found or invalid function name
diff --git a/Zend/tests/bug70898.phpt b/Zend/tests/bug70898.phpt
index 2aff5109d8..d3d2cf79a9 100644
--- a/Zend/tests/bug70898.phpt
+++ b/Zend/tests/bug70898.phpt
@@ -13,4 +13,4 @@ try {
}
?>
--EXPECT--
-array_map(): Argument #1 ($callback) must be a valid callback, function "0000000000000000000000000000000000" not found or invalid function name
+array_map(): Argument #1 ($callback) must be a valid callback or null, function "0000000000000000000000000000000000" not found or invalid function name
diff --git a/Zend/tests/bug71539_6.phpt b/Zend/tests/bug71539_6.phpt
deleted file mode 100644
index d690538595..0000000000
--- a/Zend/tests/bug71539_6.phpt
+++ /dev/null
@@ -1,15 +0,0 @@
---TEST--
-Bug #71539.5 (Memory error on $arr[$a] =& $arr[$b] if RHS rehashes)
---FILE--
-<?php
-$name = 'a';
-for ($i = 0; $i < 100000; $i++) {
- if ($name != 'i') {
- $$name =& $GLOBALS;
- }
- $name++;
-}
-?>
-OK
---EXPECT--
-OK
diff --git a/Zend/tests/bug71695.phpt b/Zend/tests/bug71695.phpt
deleted file mode 100644
index 31ae73a99d..0000000000
--- a/Zend/tests/bug71695.phpt
+++ /dev/null
@@ -1,17 +0,0 @@
---TEST--
-Bug #71695 (Global variables are reserved before execution)
---FILE--
-<?php
-function provideGlobals() {
- var_dump(array_key_exists("foo", $GLOBALS));
- var_dump(isset($GLOBALS["foo"]));
- $GLOBALS += array("foo" => "foo");
-}
-
-provideGlobals();
-echo $foo;
-?>
---EXPECT--
-bool(false)
-bool(false)
-foo
diff --git a/Zend/tests/bug75474.phpt b/Zend/tests/bug75474.phpt
new file mode 100644
index 0000000000..f71e3c6b2e
--- /dev/null
+++ b/Zend/tests/bug75474.phpt
@@ -0,0 +1,75 @@
+--TEST--
+Bug #75474: function scope static variables are not bound to a unique function
+--FILE--
+<?php
+
+function bar($k, $v) {
+ static $foo = [];
+ $foo[$k] = $v;
+ return $foo;
+}
+
+var_dump(bar(0, 0));
+var_dump(Closure::fromCallable("bar")(1, 1));
+var_dump(bar(2, 2));
+var_dump(Closure::fromCallable("bar")(3, 3));
+$RF = new ReflectionFunction("bar");
+var_dump($RF->getClosure()(4, 4));
+var_dump(bar(5, 5));
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ int(0)
+}
+array(2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(1)
+}
+array(3) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(1)
+ [2]=>
+ int(2)
+}
+array(4) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(1)
+ [2]=>
+ int(2)
+ [3]=>
+ int(3)
+}
+array(5) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(1)
+ [2]=>
+ int(2)
+ [3]=>
+ int(3)
+ [4]=>
+ int(4)
+}
+array(6) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(1)
+ [2]=>
+ int(2)
+ [3]=>
+ int(3)
+ [4]=>
+ int(4)
+ [5]=>
+ int(5)
+}
diff --git a/Zend/tests/bug78239.phpt b/Zend/tests/bug78239.phpt
index 1ecad67460..50dd46c971 100644
--- a/Zend/tests/bug78239.phpt
+++ b/Zend/tests/bug78239.phpt
@@ -1,7 +1,7 @@
--TEST--
Bug #78239: Deprecation notice during string conversion converted to exception hangs
--SKIPIF--
-<?php if (!extension_loaded("zend-test")) die("skip requires zend-test extension"); ?>
+<?php if (!extension_loaded("zend_test")) die("skip requires zend_test extension"); ?>
--FILE--
<?php
function handleError($level, $message, $file = '', $line = 0, $context = [])
diff --git a/Zend/tests/bug78335_2.phpt b/Zend/tests/bug78335_2.phpt
index 5fb5fcb788..5f2c9ed9df 100644
--- a/Zend/tests/bug78335_2.phpt
+++ b/Zend/tests/bug78335_2.phpt
@@ -1,7 +1,7 @@
--TEST--
Bug #78335: Static properties containing cycles report as leak (internal class variant)
--SKIPIF--
-<?php if (!extension_loaded("zend-test")) die("skip requires zend-test"); ?>
+<?php if (!extension_loaded("zend_test")) die("skip requires zend_test"); ?>
--FILE--
<?php
diff --git a/Zend/tests/bug79862.phpt b/Zend/tests/bug79862.phpt
index b923da78b4..a04dc5c9ac 100644
--- a/Zend/tests/bug79862.phpt
+++ b/Zend/tests/bug79862.phpt
@@ -45,14 +45,14 @@ NULL
NULL
NULL
object(c)#1 (6) {
- ["prop1"]=>
- int(1)
- ["prop2":protected]=>
- int(2)
["prop3":"a":private]=>
int(3)
["prop4":"a":private]=>
int(4)
+ ["prop1"]=>
+ int(1)
+ ["prop2":protected]=>
+ int(2)
["prop5"]=>
int(5)
["prop6"]=>
diff --git a/Zend/tests/call_to_deprecated_function_args.phpt b/Zend/tests/call_to_deprecated_function_args.phpt
index c7781e30e0..7bb9039b46 100644
--- a/Zend/tests/call_to_deprecated_function_args.phpt
+++ b/Zend/tests/call_to_deprecated_function_args.phpt
@@ -2,7 +2,7 @@
Check that arguments are freed when calling a deprecated function
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
--FILE--
<?php
diff --git a/Zend/tests/class_exists_002.phpt b/Zend/tests/class_exists_002.phpt
index 5e5df1d371..e020c1d623 100644
--- a/Zend/tests/class_exists_002.phpt
+++ b/Zend/tests/class_exists_002.phpt
@@ -8,7 +8,6 @@ class foo {
}
var_dump(class_exists(''));
-var_dump(class_exists(NULL));
var_dump(class_exists('FOO'));
var_dump(class_exists('bar'));
var_dump(class_exists(1));
@@ -16,7 +15,6 @@ var_dump(class_exists(1));
?>
--EXPECT--
bool(false)
-bool(false)
bool(true)
bool(false)
bool(false)
diff --git a/Zend/tests/closure_bindTo_preserves_used_variables.phpt b/Zend/tests/closure_bindTo_preserves_used_variables.phpt
new file mode 100644
index 0000000000..cec68ea70a
--- /dev/null
+++ b/Zend/tests/closure_bindTo_preserves_used_variables.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Closure::bindTo() should preserve used variables
+--FILE--
+<?php
+
+$var = 0;
+$fn = function() use($var) {
+ var_dump($var);
+};
+$fn();
+$fn = $fn->bindTo(null, null);
+$fn();
+
+?>
+--EXPECT--
+int(0)
+int(0)
diff --git a/Zend/tests/closures/closure_from_callable_gc.phpt b/Zend/tests/closures/closure_from_callable_gc.phpt
new file mode 100644
index 0000000000..e326fc3b09
--- /dev/null
+++ b/Zend/tests/closures/closure_from_callable_gc.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Closure::fromCallable() and GC
+--FILE--
+<?php
+
+class Test {
+ public function method() {}
+
+ public function method2($y) {
+ static $x;
+ $x = $y;
+ }
+}
+
+$fn = Closure::fromCallable([new Test, 'method2']);
+$fn($fn);
+unset($fn); // Still referenced from static var.
+gc_collect_cycles();
+
+$fn = Closure::fromCallable([new Test, 'method']);
+$fn2 = $fn; unset($fn2); // Add to root buffer.
+gc_collect_cycles();
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/const_deprecation.phpt b/Zend/tests/const_deprecation.phpt
index b7cf99d52e..08de4229b9 100644
--- a/Zend/tests/const_deprecation.phpt
+++ b/Zend/tests/const_deprecation.phpt
@@ -2,7 +2,7 @@
Internal constant deprecation
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip requires zend-test');
+if (!extension_loaded('zend_test')) die('skip requires zend_test');
?>
--FILE--
<?php
diff --git a/Zend/tests/exception_001.phpt b/Zend/tests/exception_001.phpt
index 232ef012ed..aba29d4aa3 100644
--- a/Zend/tests/exception_001.phpt
+++ b/Zend/tests/exception_001.phpt
@@ -7,7 +7,7 @@ try {
try {
try {
try {
- throw new Exception(NULL);
+ throw new Exception();
} catch (Exception $e) {
var_dump($e->getMessage());
throw $e;
diff --git a/Zend/tests/gc_010.phpt b/Zend/tests/gc_010.phpt
deleted file mode 100644
index 756c8ebc2a..0000000000
--- a/Zend/tests/gc_010.phpt
+++ /dev/null
@@ -1,21 +0,0 @@
---TEST--
-GC 010: Cycle with reference to $GLOBALS
---INI--
-zend.enable_gc=1
---FILE--
-<?php
-$a = array();
-$a[] =& $a;
-var_dump($a);
-$a[] =& $GLOBALS;
-unset($a);
-var_dump(gc_collect_cycles());
-echo "ok\n"
-?>
---EXPECT--
-array(1) {
- [0]=>
- *RECURSION*
-}
-int(1)
-ok
diff --git a/Zend/tests/get_mangled_object_vars.phpt b/Zend/tests/get_mangled_object_vars.phpt
index 735548579e..f9ad008a33 100644
--- a/Zend/tests/get_mangled_object_vars.phpt
+++ b/Zend/tests/get_mangled_object_vars.phpt
@@ -33,10 +33,10 @@ echo "\n";
?>
--EXPECT--
array (
- '' . "\0" . 'B' . "\0" . 'priv' => 4,
'pub' => 1,
'' . "\0" . '*' . "\0" . 'prot' => 2,
'' . "\0" . 'A' . "\0" . 'priv' => 3,
+ '' . "\0" . 'B' . "\0" . 'priv' => 4,
'dyn' => 5,
6 => 6,
)
diff --git a/Zend/tests/globals_001.phpt b/Zend/tests/globals_001.phpt
index 069e88730c..7c54046212 100644
--- a/Zend/tests/globals_001.phpt
+++ b/Zend/tests/globals_001.phpt
@@ -29,6 +29,6 @@ string(%d) "%s"
Warning: Undefined array key "PHP_SELF" in %s on line %d
NULL
-Warning: Undefined variable $_SERVER in %s on line %d
+Warning: Undefined global variable $_SERVER in %s on line %d
NULL
Done
diff --git a/Zend/tests/globals_002.phpt b/Zend/tests/globals_002.phpt
index d8f9ad4aa2..faa9b94ade 100644
--- a/Zend/tests/globals_002.phpt
+++ b/Zend/tests/globals_002.phpt
@@ -32,6 +32,6 @@ string(%d) "%s"
Warning: Undefined array key "PHP_SELF" in %s on line %d
NULL
-Warning: Undefined variable $_SERVER in %s on line %d
+Warning: Undefined global variable $_SERVER in %s on line %d
NULL
Done
diff --git a/Zend/tests/globals_003.phpt b/Zend/tests/globals_003.phpt
index 6ac9d9f779..36e74bde64 100644
--- a/Zend/tests/globals_003.phpt
+++ b/Zend/tests/globals_003.phpt
@@ -38,6 +38,6 @@ string(%d) "%s"
Warning: Undefined array key "PHP_SELF" in %s on line %d
NULL
-Warning: Undefined variable $_SERVER in %s on line %d
+Warning: Undefined global variable $_SERVER in %s on line %d
NULL
Done
diff --git a/Zend/tests/globals_004.phpt b/Zend/tests/globals_004.phpt
index 27520a220c..4c81e5e57a 100644
--- a/Zend/tests/globals_004.phpt
+++ b/Zend/tests/globals_004.phpt
@@ -23,6 +23,6 @@ string(%d) "%s"
Warning: Undefined array key "PHP_SELF" in %s on line %d
NULL
-Warning: Undefined variable $_SERVER in %s on line %d
+Warning: Undefined global variable $_SERVER in %s on line %d
NULL
Done
diff --git a/Zend/tests/inherit_internal_static.phpt b/Zend/tests/inherit_internal_static.phpt
index 4716717f15..6e29301b74 100644
--- a/Zend/tests/inherit_internal_static.phpt
+++ b/Zend/tests/inherit_internal_static.phpt
@@ -1,7 +1,7 @@
--TEST--
Inherit internal static property into userland class
--SKIPIF--
-<?php if (!extension_loaded('zend-test')) die('skip requires zend-test'); ?>
+<?php if (!extension_loaded('zend_test')) die('skip requires zend_test'); ?>
--FILE--
<?php
diff --git a/Zend/tests/interface_exists_001.phpt b/Zend/tests/interface_exists_001.phpt
index 84e9c6df1e..4bc01221a8 100644
--- a/Zend/tests/interface_exists_001.phpt
+++ b/Zend/tests/interface_exists_001.phpt
@@ -8,10 +8,8 @@ interface foo {
var_dump(interface_exists('foo'));
var_dump(interface_exists(1));
-var_dump(interface_exists(NULL));
?>
--EXPECT--
bool(true)
bool(false)
-bool(false)
diff --git a/Zend/tests/invalid_const_class_name.phpt b/Zend/tests/invalid_const_class_name.phpt
new file mode 100644
index 0000000000..bedd74e1df
--- /dev/null
+++ b/Zend/tests/invalid_const_class_name.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Invalid constant class name in nested class constant access
+--FILE--
+<?php
+[]::X::X;
+?>
+--EXPECTF--
+Fatal error: Illegal class name in %s on line %d
diff --git a/Zend/tests/iterable_or_null.phpt b/Zend/tests/iterable_or_null.phpt
index 9f798af06d..19cb854437 100644
--- a/Zend/tests/iterable_or_null.phpt
+++ b/Zend/tests/iterable_or_null.phpt
@@ -2,7 +2,7 @@
Test Z_PARAM_ITERABLE() and Z_PARAM_ITERABLE_OR_NULL
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
diff --git a/Zend/tests/list_keyed_leading_comma.phpt b/Zend/tests/list_keyed_leading_comma.phpt
new file mode 100644
index 0000000000..dcadcbf393
--- /dev/null
+++ b/Zend/tests/list_keyed_leading_comma.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Leading comma in keyed list assignment
+--FILE--
+<?php
+
+[, "a" => $b] = [1, "a" => 2];
+
+?>
+--EXPECTF--
+Fatal error: Cannot use empty array entries in keyed array assignment in %s on line %d
diff --git a/Zend/tests/lsb_023.phpt b/Zend/tests/lsb_023.phpt
new file mode 100644
index 0000000000..a8051aa85f
--- /dev/null
+++ b/Zend/tests/lsb_023.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Late Static Binding static:: calls protected / public method of child class even then
+the method is private in parent class
+--FILE--
+<?php
+class A {
+ public static function out() {
+ echo static::value(), PHP_EOL;
+ }
+
+ private static function value() { return 'A'; }
+}
+class B extends A {
+ protected static function value() { return 'B'; }
+}
+class C extends A {
+ public static function value() { return 'C'; }
+}
+A::out();
+B::out();
+C::out();
+echo PHP_EOL;
+--EXPECT--
+A
+B
+C
diff --git a/Zend/tests/lsb_024.phpt b/Zend/tests/lsb_024.phpt
new file mode 100644
index 0000000000..2c71c678d3
--- /dev/null
+++ b/Zend/tests/lsb_024.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Late Static Binding static:: accesses protected / public static variables of child
+class when the variable is private in parent class
+--FILE--
+<?php
+class A {
+ private static $value = 'A';
+
+ public static function out() {
+ echo static::$value, PHP_EOL;
+ }
+}
+class B extends A {
+ protected static $value = 'B';
+}
+class C extends A {
+ public static $value = 'C';
+}
+A::out();
+B::out();
+C::out();
+--EXPECT--
+A
+B
+C
diff --git a/Zend/tests/method_static_var.phpt b/Zend/tests/method_static_var.phpt
index 06574732d7..f92e6d3a5d 100644
--- a/Zend/tests/method_static_var.phpt
+++ b/Zend/tests/method_static_var.phpt
@@ -3,8 +3,6 @@ Initial value of static var in method depends on the include time of the class d
--FILE--
<?php
-/* The current behavior is probably a bug, but we should still test how it currently works. */
-
class Foo {
public static function test() {
static $i = 0;
@@ -22,5 +20,5 @@ Bar::test();
--EXPECT--
int(1)
int(2)
+int(1)
int(2)
-int(3)
diff --git a/Zend/tests/multibyte/multibyte_encoding_001.phpt b/Zend/tests/multibyte/multibyte_encoding_001.phpt
index 3b26dcb989..f95b4311a9 100644
--- a/Zend/tests/multibyte/multibyte_encoding_001.phpt
+++ b/Zend/tests/multibyte/multibyte_encoding_001.phpt
@@ -13,7 +13,7 @@ internal_encoding=SJIS
<?php
declare(encoding='Shift_JIS');
$s = "•\"; // 0x95+0x5c in script, not somewhere else "
-printf("%x:%x\n", ord($s[0]), ord($s[1]));
+printf("%x:%x", ord($s[0]), ord($s[1]));
?>
--EXPECT--
95:5c
diff --git a/Zend/tests/multibyte/multibyte_encoding_003.phpt b/Zend/tests/multibyte/multibyte_encoding_003.phpt
index a0983329f4..f0fb60f6cd 100644
--- a/Zend/tests/multibyte/multibyte_encoding_003.phpt
+++ b/Zend/tests/multibyte/multibyte_encoding_003.phpt
Binary files differ
diff --git a/Zend/tests/named_params/undef_var.phpt b/Zend/tests/named_params/undef_var.phpt
index b1d5682434..5ef41e26dd 100644
--- a/Zend/tests/named_params/undef_var.phpt
+++ b/Zend/tests/named_params/undef_var.phpt
@@ -1,5 +1,5 @@
--TEST--
-Passing undefined variabled to named arg
+Passing undefined variable to named arg
--FILE--
<?php
diff --git a/Zend/tests/null_to_non_nullable_special_func.phpt b/Zend/tests/null_to_non_nullable_special_func.phpt
new file mode 100644
index 0000000000..9dc1c96f72
--- /dev/null
+++ b/Zend/tests/null_to_non_nullable_special_func.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test null arg behavior for special functions
+--FILE--
+<?php
+
+$null = null;
+var_dump(strlen($null));
+
+?>
+--EXPECTF--
+Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
+int(0)
diff --git a/Zend/tests/nullsafe_operator/013.phpt b/Zend/tests/nullsafe_operator/013.phpt
index fd1fbc9006..595f292f6f 100644
--- a/Zend/tests/nullsafe_operator/013.phpt
+++ b/Zend/tests/nullsafe_operator/013.phpt
@@ -38,14 +38,21 @@ dump_error(fn() => array_key_exists('foo', $foo?->foo()));
?>
--EXPECTF--
+Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d
int(0)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
+
+Deprecated: defined(): Passing null to parameter #1 ($constant_name) of type string is deprecated in %s on line %d
bool(false)
+
+Deprecated: chr(): Passing null to parameter #1 ($codepoint) of type int is deprecated in %s on line %d
string(1) "%s"
+
+Deprecated: ord(): Passing null to parameter #1 ($character) of type string is deprecated in %s on line %d
int(0)
string(98) "call_user_func_array(): Argument #1 ($function) must be a valid callback, no array or string given"
string(77) "call_user_func_array(): Argument #2 ($args) must be of type array, null given"
@@ -55,6 +62,8 @@ string(4) "NULL"
string(52) "func_num_args() expects exactly 0 arguments, 1 given"
string(52) "func_get_args() expects exactly 0 arguments, 1 given"
string(69) "array_slice(): Argument #1 ($array) must be of type array, null given"
+
+Deprecated: array_slice(): Passing null to parameter #2 ($offset) of type int is deprecated in %s on line %d
array(1) {
[0]=>
string(3) "foo"
diff --git a/Zend/tests/objects_033.phpt b/Zend/tests/objects_033.phpt
index 3c19864490..edb722b040 100644
--- a/Zend/tests/objects_033.phpt
+++ b/Zend/tests/objects_033.phpt
@@ -24,5 +24,5 @@ print_r($a, true);
var_dump($a < $b);
?>
--EXPECT--
-bool(false)
-bool(false)
+bool(true)
+bool(true)
diff --git a/Zend/tests/oct_whitespace.phpt b/Zend/tests/oct_whitespace.phpt
new file mode 100644
index 0000000000..cb0bf4ad7a
--- /dev/null
+++ b/Zend/tests/oct_whitespace.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Octal literal followed by whitespace and another number
+--FILE--
+<?php
+var_dump(0o0 2);
+?>
+--EXPECTF--
+Parse error: syntax error, unexpected integer "2", expecting ")" in %s on line %d
diff --git a/Zend/tests/overloaded_func_001.phpt b/Zend/tests/overloaded_func_001.phpt
index 7fc435f920..d78ef1971c 100644
--- a/Zend/tests/overloaded_func_001.phpt
+++ b/Zend/tests/overloaded_func_001.phpt
@@ -2,7 +2,7 @@
Overloaded function 001
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
diff --git a/Zend/tests/overloaded_func_002.phpt b/Zend/tests/overloaded_func_002.phpt
index a02f1e8e37..0a3107182a 100644
--- a/Zend/tests/overloaded_func_002.phpt
+++ b/Zend/tests/overloaded_func_002.phpt
@@ -2,7 +2,7 @@
Overloaded function 002
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
diff --git a/Zend/tests/resource_key.phpt b/Zend/tests/resource_key.phpt
new file mode 100644
index 0000000000..fddd41bcad
--- /dev/null
+++ b/Zend/tests/resource_key.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Behavior of resources as array keys
+--FILE--
+<?php
+
+$r = fopen(__FILE__, 'r');
+$a = [];
+echo "Assign:";
+$a[$r] = 1;
+echo "Add assign:";
+$a[$r] += 1;
+echo "Inc:";
+$a[$r]++;
+echo "Get:";
+var_dump($a[$r]);
+echo "Isset:";
+var_dump(isset($a[$r]));
+echo "Unset:";
+unset($a[$r]);
+
+?>
+--EXPECTF--
+Assign:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+Add assign:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+Inc:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+Get:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+int(3)
+Isset:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+bool(true)
+Unset:
+Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
diff --git a/Zend/tests/restrict_globals/globals_in_globals.phpt b/Zend/tests/restrict_globals/globals_in_globals.phpt
new file mode 100644
index 0000000000..fbb811708c
--- /dev/null
+++ b/Zend/tests/restrict_globals/globals_in_globals.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$GLOBALS no longer contains 'GLOBALS'
+--FILE--
+<?php
+
+$g = $GLOBALS;
+var_dump(isset($g['GLOBALS']));
+
+?>
+--EXPECT--
+bool(false)
diff --git a/Zend/tests/restrict_globals/invalid_append.phpt b/Zend/tests/restrict_globals/invalid_append.phpt
new file mode 100644
index 0000000000..8e8b99fb9b
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_append.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot append to $GLOBALS
+--FILE--
+<?php
+
+$GLOBALS[] = 1;
+
+?>
+--EXPECTF--
+Fatal error: Cannot append to $GLOBALS in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_append_isset.phpt b/Zend/tests/restrict_globals/invalid_append_isset.phpt
new file mode 100644
index 0000000000..6cb57351e7
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_append_isset.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Cannot append to $GLOBALS in isset()
+--FILE--
+<?php
+isset($GLOBALS[]);
+?>
+--EXPECTF--
+Fatal error: Cannot use [] for reading in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_append_unset.phpt b/Zend/tests/restrict_globals/invalid_append_unset.phpt
new file mode 100644
index 0000000000..b7c06179c7
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_append_unset.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Cannot append to $GLOBALS in unset()
+--FILE--
+<?php
+unset($GLOBALS[]);
+?>
+--EXPECTF--
+Fatal error: Cannot use [] for unsetting in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign.phpt b/Zend/tests/restrict_globals/invalid_assign.phpt
new file mode 100644
index 0000000000..c42cc9e1f9
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot assign to $GLOBALS
+--FILE--
+<?php
+
+$GLOBALS = [];
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign_list.phpt b/Zend/tests/restrict_globals/invalid_assign_list.phpt
new file mode 100644
index 0000000000..8fc3e524a9
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign_list.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot list-assign to $GLOBALS
+--FILE--
+<?php
+
+list($GLOBALS) = [1];
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt b/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt
new file mode 100644
index 0000000000..594a308ea1
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot list-assign to $GLOBALS (by-ref)
+--FILE--
+<?php
+
+list(&$GLOBALS) = [1];
+
+?>
+--EXPECTF--
+Fatal error: Cannot assign reference to non referencable value in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign_op.phpt b/Zend/tests/restrict_globals/invalid_assign_op.phpt
new file mode 100644
index 0000000000..a9b36853ea
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign_op.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot compound assign to $GLOBALS
+--FILE--
+<?php
+
+$GLOBALS += [];
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt b/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt
new file mode 100644
index 0000000000..b14c167646
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Cannot by-ref assign to $GLOBALS (LHS)
+--FILE--
+<?php
+
+$var = [];
+$GLOBALS =& $var;
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt b/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt
new file mode 100644
index 0000000000..3cdab0f9c9
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Cannot by-ref assign to $GLOBALS (RHS)
+--FILE--
+<?php
+
+$var = [];
+$var =& $GLOBALS;
+
+?>
+--EXPECTF--
+Fatal error: Cannot acquire reference to $GLOBALS in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_foreach.phpt b/Zend/tests/restrict_globals/invalid_foreach.phpt
new file mode 100644
index 0000000000..0e7c74881a
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_foreach.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot use $GLOBALS as foreach result variable
+--FILE--
+<?php
+
+foreach ([1] as $GLOBALS) {}
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_foreach_ref.phpt b/Zend/tests/restrict_globals/invalid_foreach_ref.phpt
new file mode 100644
index 0000000000..2c355c09ae
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_foreach_ref.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot use $GLOBALS as foreach result variable (by-ref)
+--FILE--
+<?php
+
+foreach ([1] as &$GLOBALS) {}
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt b/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt
new file mode 100644
index 0000000000..a0145a0624
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt
@@ -0,0 +1,23 @@
+--TEST--
+$GLOBALS cannot be passed by reference (runtime error)
+--FILE--
+<?php
+
+function by_ref(&$ref) {}
+try {
+ by_ref($GLOBALS);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ by_ref2($GLOBALS);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+function by_ref2(&$ref) {}
+
+?>
+--EXPECT--
+by_ref(): Argument #1 ($ref) cannot be passed by reference
+by_ref2(): Argument #1 ($ref) cannot be passed by reference
diff --git a/Zend/tests/restrict_globals/invalid_unset.phpt b/Zend/tests/restrict_globals/invalid_unset.phpt
new file mode 100644
index 0000000000..781388b9b2
--- /dev/null
+++ b/Zend/tests/restrict_globals/invalid_unset.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot unset $GLOBALS
+--FILE--
+<?php
+
+unset($GLOBALS);
+
+?>
+--EXPECTF--
+Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d
diff --git a/Zend/tests/restrict_globals/key_canonicalization.phpt b/Zend/tests/restrict_globals/key_canonicalization.phpt
new file mode 100644
index 0000000000..66d9a29a92
--- /dev/null
+++ b/Zend/tests/restrict_globals/key_canonicalization.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$GLOBALS should have canonicalized keys
+--FILE--
+<?php
+
+${1} = 42;
+var_dump($GLOBALS[1]);
+
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/restrict_globals/valid.phpt b/Zend/tests/restrict_globals/valid.phpt
new file mode 100644
index 0000000000..b99bbe402f
--- /dev/null
+++ b/Zend/tests/restrict_globals/valid.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Supported operations on $GLOBALS
+--FILE--
+<?php
+
+function test() {
+ var_dump($GLOBALS['x']);
+ $GLOBALS['x'] = 1;
+ var_dump($GLOBALS['x']);
+ $GLOBALS['x']++;
+ var_dump($GLOBALS['x']);
+ $GLOBALS['x'] += 2;
+ var_dump($GLOBALS['x']);
+ unset($GLOBALS['y']);
+ var_dump(isset($GLOBALS['x']));
+ var_dump(isset($GLOBALS['y']));
+ $GLOBALS['z'][] = 1;
+}
+
+$y = 1;
+test();
+var_dump($x, $y, $z);
+
+$ref = 1;
+$GLOBALS['z'] =& $ref;
+$ref++;
+var_dump($z);
+
+$x = 1;
+$ref2 =& $GLOBALS['x'];
+$ref2++;
+var_dump($x);
+
+?>
+--EXPECTF--
+Warning: Undefined global variable $x in %s on line %d
+NULL
+int(1)
+int(2)
+int(4)
+bool(true)
+bool(false)
+
+Warning: Undefined variable $y in %s on line %d
+int(4)
+NULL
+array(1) {
+ [0]=>
+ int(1)
+}
+int(2)
+int(2)
diff --git a/Zend/tests/return_types/internal_functions001.phpt b/Zend/tests/return_types/internal_functions001.phpt
index 153e32ca6e..183f394c9b 100644
--- a/Zend/tests/return_types/internal_functions001.phpt
+++ b/Zend/tests/return_types/internal_functions001.phpt
@@ -2,7 +2,7 @@
Return type for internal functions
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
// Internal function return types are only checked in debug builds
if (!PHP_DEBUG) die('skip requires debug build');
?>
diff --git a/Zend/tests/return_types/internal_functions002.phpt b/Zend/tests/return_types/internal_functions002.phpt
index 531b6c452d..f6287b72f0 100644
--- a/Zend/tests/return_types/internal_functions002.phpt
+++ b/Zend/tests/return_types/internal_functions002.phpt
@@ -2,7 +2,7 @@
Return type for internal functions 2
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
--FILE--
<?php
zend_test_nullable_array_return();
diff --git a/Zend/tests/return_types/internal_functions003.phpt b/Zend/tests/return_types/internal_functions003.phpt
index a7e71204fa..754b14574d 100644
--- a/Zend/tests/return_types/internal_functions003.phpt
+++ b/Zend/tests/return_types/internal_functions003.phpt
@@ -2,7 +2,7 @@
Return type for internal functions 3: Void return type
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
--FILE--
<?php
var_dump(zend_test_void_return());
diff --git a/Zend/tests/static_variable_in_dynamic_function.phpt b/Zend/tests/static_variable_in_dynamic_function.phpt
new file mode 100644
index 0000000000..77b71a4883
--- /dev/null
+++ b/Zend/tests/static_variable_in_dynamic_function.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Static variables in dynamically declared function (first use before dynamic def dtor)
+--FILE--
+<?php
+
+$code = <<<'CODE'
+if (1) {
+ function test() {
+ static $x = 0;
+ var_dump(++$x);
+ }
+ test();
+}
+CODE;
+eval($code);
+test();
+
+?>
+--EXPECT--
+int(1)
+int(2)
diff --git a/Zend/tests/static_variable_in_dynamic_function_2.phpt b/Zend/tests/static_variable_in_dynamic_function_2.phpt
new file mode 100644
index 0000000000..f7c160325f
--- /dev/null
+++ b/Zend/tests/static_variable_in_dynamic_function_2.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Static variables in dynamically declared function (first use after dynamic def dtor)
+--FILE--
+<?php
+
+$code = <<<'CODE'
+if (1) {
+ function test() {
+ static $x = 0;
+ var_dump(++$x);
+ }
+}
+CODE;
+eval($code);
+test();
+test();
+
+?>
+--EXPECT--
+int(1)
+int(2)
diff --git a/Zend/tests/str_or_obj_of_class_zpp.phpt b/Zend/tests/str_or_obj_of_class_zpp.phpt
index b8f5d8492f..04a321a692 100644
--- a/Zend/tests/str_or_obj_of_class_zpp.phpt
+++ b/Zend/tests/str_or_obj_of_class_zpp.phpt
@@ -2,7 +2,7 @@
Test Z_PARAM_OBJ_OF_CLASS_OR_STR() and Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
@@ -51,9 +51,11 @@ try {
}
?>
---EXPECT--
+--EXPECTF--
string(6) "string"
string(1) "1"
+
+Deprecated: zend_string_or_stdclass(): Passing null to parameter #1 ($param) of type string is deprecated in %s on line %d
string(0) ""
object(stdClass)#1 (0) {
}
diff --git a/Zend/tests/str_or_obj_zpp.phpt b/Zend/tests/str_or_obj_zpp.phpt
index 301abd8ddc..00eec7a688 100644
--- a/Zend/tests/str_or_obj_zpp.phpt
+++ b/Zend/tests/str_or_obj_zpp.phpt
@@ -2,7 +2,7 @@
Test Z_PARAM_OBJ_OR_STR() and Z_PARAM_OBJ_OR_STR_OR_NULL
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
@@ -34,9 +34,11 @@ try {
}
?>
---EXPECT--
+--EXPECTF--
string(6) "string"
string(1) "1"
+
+Deprecated: zend_string_or_object(): Passing null to parameter #1 ($param) of type object|string is deprecated in %s on line %d
string(0) ""
object(stdClass)#1 (0) {
}
diff --git a/Zend/tests/trait_exists_001.phpt b/Zend/tests/trait_exists_001.phpt
index 8a7c55d586..8699b07cef 100644
--- a/Zend/tests/trait_exists_001.phpt
+++ b/Zend/tests/trait_exists_001.phpt
@@ -8,10 +8,8 @@ trait foo {
var_dump(trait_exists('foo'));
var_dump(trait_exists(1));
-var_dump(trait_exists(NULL));
?>
--EXPECT--
bool(true)
bool(false)
-bool(false)
diff --git a/Zend/tests/traits/bug69579.phpt b/Zend/tests/traits/bug69579.phpt
index 94387ce3cd..c12f54a374 100644
--- a/Zend/tests/traits/bug69579.phpt
+++ b/Zend/tests/traits/bug69579.phpt
@@ -2,7 +2,7 @@
Bug #69579 (Internal trait double-free)
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
?>
--FILE--
<?php
diff --git a/Zend/tests/traits/get_declared_traits_004.phpt b/Zend/tests/traits/get_declared_traits_004.phpt
new file mode 100644
index 0000000000..88149686f7
--- /dev/null
+++ b/Zend/tests/traits/get_declared_traits_004.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Testing get_declared_traits() and class_alias()
+--FILE--
+<?php
+
+trait T { }
+class_alias("T", "A");
+foreach (get_declared_traits() as $name) {
+ if (strlen($name) == 1) {
+ echo $name;
+ }
+}
+echo "\n";
+?>
+--EXPECT--
+Ta
diff --git a/Zend/tests/traits/property008.phpt b/Zend/tests/traits/property008.phpt
index ff265be2a0..d4d57f379a 100644
--- a/Zend/tests/traits/property008.phpt
+++ b/Zend/tests/traits/property008.phpt
@@ -42,20 +42,20 @@ var_dump($b);
?>
--EXPECT--
object(SubclassClassicInheritance)#1 (2) {
- ["hello":"SubclassClassicInheritance":private]=>
- int(0)
["hello":"BaseWithPropA":private]=>
int(0)
+ ["hello":"SubclassClassicInheritance":private]=>
+ int(0)
}
object(SubclassA)#2 (2) {
- ["hello":"SubclassA":private]=>
- int(0)
["hello":"BaseWithPropA":private]=>
int(0)
+ ["hello":"SubclassA":private]=>
+ int(0)
}
object(SubclassB)#3 (2) {
- ["hello":"SubclassB":private]=>
- int(0)
["hello":"BaseWithTPropB":private]=>
int(0)
+ ["hello":"SubclassB":private]=>
+ int(0)
}
diff --git a/Zend/tests/type_declarations/internal_function_strict_mode.phpt b/Zend/tests/type_declarations/internal_function_strict_mode.phpt
index 04c59ae341..455164f327 100644
--- a/Zend/tests/type_declarations/internal_function_strict_mode.phpt
+++ b/Zend/tests/type_declarations/internal_function_strict_mode.phpt
@@ -30,6 +30,6 @@ try {
*** Trying Ord With Integer
*** Caught ord(): Argument #1 ($character) must be of type string, int given
*** Trying Array Map With Invalid Callback
-*** Caught array_map(): Argument #1 ($callback) must be a valid callback, first array member is not a valid class name or object
+*** Caught array_map(): Argument #1 ($callback) must be a valid callback or null, first array member is not a valid class name or object
*** Trying Strlen With Float
*** Caught strlen(): Argument #1 ($str) must be of type string, float given
diff --git a/Zend/tests/type_declarations/typed_properties_095.phpt b/Zend/tests/type_declarations/typed_properties_095.phpt
index 8470d4f437..7e92cec682 100644
--- a/Zend/tests/type_declarations/typed_properties_095.phpt
+++ b/Zend/tests/type_declarations/typed_properties_095.phpt
@@ -1,7 +1,7 @@
--TEST--
Typed properties in internal classes
--SKIPIF--
-<?php if (!extension_loaded('zend-test')) die('skip requires zend-test'); ?>
+<?php if (!extension_loaded('zend_test')) die('skip requires zend_test'); ?>
--FILE--
<?php
diff --git a/Zend/tests/type_declarations/union_types/inheritance_internal.phpt b/Zend/tests/type_declarations/union_types/inheritance_internal.phpt
index bb53411cad..84d0f88211 100644
--- a/Zend/tests/type_declarations/union_types/inheritance_internal.phpt
+++ b/Zend/tests/type_declarations/union_types/inheritance_internal.phpt
@@ -2,7 +2,7 @@
Inheritance of union type from internal class
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip requires zend-test extension');
+if (!extension_loaded('zend_test')) die('skip requires zend_test extension');
?>
--FILE--
<?php
diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt
index c01aaba658..bbe13c0e71 100644
--- a/Zend/tests/undef_index_to_exception.phpt
+++ b/Zend/tests/undef_index_to_exception.phpt
@@ -42,5 +42,5 @@ array(0) {
Undefined array key "key"
array(0) {
}
-Undefined array key "test"
+Undefined global variable $test
Undefined variable $test
diff --git a/Zend/tests/unset_cv09.phpt b/Zend/tests/unset_cv09.phpt
deleted file mode 100644
index 63a7eab0f2..0000000000
--- a/Zend/tests/unset_cv09.phpt
+++ /dev/null
@@ -1,14 +0,0 @@
---TEST--
-unset() CV 9 (unset() of global variable in array_pop($GLOBALS))
---FILE--
-<?php
-$x = "ok\n";
-echo array_pop($GLOBALS);
-echo $x;
-echo "ok\n";
-?>
---EXPECTF--
-ok
-
-Warning: Undefined variable $x in %s on line %d
-ok
diff --git a/Zend/tests/unset_cv10.phpt b/Zend/tests/unset_cv10.phpt
index c62b8ae57a..e8908be0f7 100644
--- a/Zend/tests/unset_cv10.phpt
+++ b/Zend/tests/unset_cv10.phpt
@@ -2,6 +2,7 @@
unset() CV 10 (unset() of global variable in ArrayObject::offsetUnset($GLOBALS))
--FILE--
<?php
+/* This is working on a copy of $GLOBALS, so nothing interesting happens here. */
$a = new ArrayObject($GLOBALS);
$x = "ok\n";
echo $x;
@@ -12,5 +13,6 @@ echo "ok\n";
--EXPECTF--
ok
-Warning: Undefined variable $x in %s on line %d
+Warning: Undefined array key "x" in %s on line %d
+ok
ok
diff --git a/Zend/zend.c b/Zend/zend.c
index cbd5aef42b..9e5c7e2328 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -34,6 +34,7 @@
#include "zend_cpuinfo.h"
#include "zend_attributes.h"
#include "zend_observer.h"
+#include "Optimizer/zend_optimizer.h"
static size_t global_map_ptr_last = 0;
@@ -60,7 +61,7 @@ ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_API zend_utility_values zend_uv;
-ZEND_API zend_bool zend_dtrace_enabled;
+ZEND_API bool zend_dtrace_enabled;
/* version information */
static char *zend_version_info;
@@ -92,7 +93,7 @@ static void (*zend_message_dispatcher_p)(zend_long message, const void *data);
static zval *(*zend_get_configuration_directive_p)(zend_string *name);
#if ZEND_RC_DEBUG
-ZEND_API zend_bool zend_rc_debug = 0;
+ZEND_API bool zend_rc_debug = 0;
#endif
static ZEND_INI_MH(OnUpdateErrorReporting) /* {{{ */
@@ -108,7 +109,7 @@ static ZEND_INI_MH(OnUpdateErrorReporting) /* {{{ */
static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */
{
- zend_bool val;
+ bool val;
val = zend_ini_parse_bool(new_value);
gc_enable(val);
@@ -290,7 +291,7 @@ ZEND_API zend_string *zend_strpprintf_unchecked(size_t max_len, const char *form
static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent);
-static void print_hash(smart_str *buf, HashTable *ht, int indent, zend_bool is_object) /* {{{ */
+static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object) /* {{{ */
{
zval *tmp;
zend_string *string_key;
@@ -524,7 +525,7 @@ static FILE *zend_fopen_wrapper(const char *filename, zend_string **opened_path)
/* }}} */
#ifdef ZTS
-static zend_bool short_tags_default = 1;
+static bool short_tags_default = 1;
static uint32_t compiler_options_default = ZEND_COMPILE_DEFAULT;
#else
# define short_tags_default 1
@@ -659,6 +660,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor);
compiler_globals->script_encoding_list = NULL;
+ compiler_globals->current_linking_class = NULL;
#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
/* Map region is going to be created and resized at run-time. */
@@ -712,7 +714,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
zend_init_exception_op();
zend_init_call_trampoline_op();
memset(&executor_globals->trampoline, 0, sizeof(zend_op_array));
- executor_globals->lambda_count = 0;
+ executor_globals->capture_warnings_during_sccp = 0;
ZVAL_UNDEF(&executor_globals->user_error_handler);
ZVAL_UNDEF(&executor_globals->user_exception_handler);
executor_globals->in_autoload = NULL;
@@ -784,15 +786,10 @@ static void module_destructor_zval(zval *zv) /* {{{ */
}
/* }}} */
-static zend_bool php_auto_globals_create_globals(zend_string *name) /* {{{ */
+static bool php_auto_globals_create_globals(zend_string *name) /* {{{ */
{
- zval globals;
-
- /* IS_ARRAY, but with ref-counter 1 and not IS_TYPE_REFCOUNTED */
- ZVAL_ARR(&globals, &EG(symbol_table));
- Z_TYPE_FLAGS_P(&globals) = 0;
- ZVAL_NEW_REF(&globals, &globals);
- zend_hash_update(&EG(symbol_table), name, &globals);
+ /* While we keep registering $GLOBALS as an auto-global, we do not create an
+ * actual variable for it. Access to it handled specially by the compiler. */
return 0;
}
/* }}} */
@@ -958,6 +955,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
php_win32_cp_setup();
#endif
+ zend_optimizer_startup();
+
#ifdef ZTS
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
@@ -1117,6 +1116,8 @@ void zend_shutdown(void) /* {{{ */
}
#endif
zend_destroy_rsrc_list_dtors();
+
+ zend_optimizer_shutdown();
}
/* }}} */
@@ -1292,12 +1293,20 @@ static ZEND_COLD void zend_error_impl(
zval params[4];
zval retval;
zval orig_user_error_handler;
- zend_bool in_compilation;
+ bool in_compilation;
zend_class_entry *saved_class_entry;
zend_stack loop_var_stack;
zend_stack delayed_oplines_stack;
int type = orig_type & E_ALL;
+ /* If we're executing a function during SCCP, count any warnings that may be emitted,
+ * but don't perform any other error handling. */
+ if (EG(capture_warnings_during_sccp)) {
+ ZEND_ASSERT(!(type & E_FATAL_ERRORS) && "Fatal error during SCCP");
+ EG(capture_warnings_during_sccp)++;
+ return;
+ }
+
/* Report about uncaught exception in case of fatal errors */
if (EG(exception)) {
zend_execute_data *ex;
@@ -1596,7 +1605,7 @@ ZEND_API ZEND_COLD void zend_value_error(const char *format, ...) /* {{{ */
va_end(va);
} /* }}} */
-ZEND_API ZEND_COLD void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) /* {{{ */
+ZEND_API ZEND_COLD void zend_output_debug_string(bool trigger_break, const char *format, ...) /* {{{ */
{
#if ZEND_DEBUG
va_list args;
@@ -1687,6 +1696,7 @@ ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count
ret = zend_exception_error(EG(exception), E_ERROR);
}
}
+ zend_destroy_static_vars(op_array);
destroy_op_array(op_array);
efree_size(op_array, sizeof(zend_op_array));
} else if (type==ZEND_REQUIRE) {
diff --git a/Zend/zend.h b/Zend/zend.h
index a851ded9b9..44f3baac46 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -20,7 +20,7 @@
#ifndef ZEND_H
#define ZEND_H
-#define ZEND_VERSION "4.0.3-dev"
+#define ZEND_VERSION "4.1.0-dev"
#define ZEND_ENGINE_3
@@ -107,6 +107,28 @@ typedef struct _zend_trait_alias {
uint32_t modifiers;
} zend_trait_alias;
+typedef struct _zend_class_mutable_data {
+ zval *default_properties_table;
+ HashTable *constants_table;
+ uint32_t ce_flags;
+} zend_class_mutable_data;
+
+typedef struct _zend_class_dependency {
+ zend_string *name;
+ zend_class_entry *ce;
+} zend_class_dependency;
+
+typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry;
+
+struct _zend_inheritance_cache_entry {
+ zend_inheritance_cache_entry *next;
+ zend_class_entry *ce;
+ zend_class_entry *parent;
+ zend_class_dependency *dependencies;
+ uint32_t dependencies_count;
+ zend_class_entry *traits_and_interfaces[1];
+};
+
struct _zend_class_entry {
char type;
zend_string *name;
@@ -127,6 +149,9 @@ struct _zend_class_entry {
HashTable properties_info;
HashTable constants_table;
+ ZEND_MAP_PTR_DEF(zend_class_mutable_data*, mutable_data);
+ zend_inheritance_cache_entry *inheritance_cache;
+
struct _zend_property_info **properties_info_table;
zend_function *constructor;
@@ -203,7 +228,7 @@ typedef struct _zend_utility_functions {
} zend_utility_functions;
typedef struct _zend_utility_values {
- zend_bool html_errors;
+ bool html_errors;
} zend_utility_values;
typedef size_t (*zend_write_func_t)(const char *str, size_t str_length);
@@ -255,7 +280,7 @@ ZEND_API void zend_print_flat_zval_r(zval *expr);
#define zend_print_variable(var) \
zend_print_zval((var), 0)
-ZEND_API ZEND_COLD void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
+ZEND_API ZEND_COLD void zend_output_debug_string(bool trigger_break, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
ZEND_API void zend_activate(void);
ZEND_API void zend_deactivate(void);
@@ -316,7 +341,7 @@ extern ZEND_API zend_class_entry *zend_standard_class_def;
extern ZEND_API zend_utility_values zend_uv;
/* If DTrace is available and enabled */
-extern ZEND_API zend_bool zend_dtrace_enabled;
+extern ZEND_API bool zend_dtrace_enabled;
END_EXTERN_C()
#define ZEND_UV(name) (zend_uv.name)
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 11760d50f0..e532e9754a 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -215,6 +215,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
case ZPP_ERROR_WRONG_CALLBACK:
zend_wrong_callback_error(num, name);
break;
+ case ZPP_ERROR_WRONG_CALLBACK_OR_NULL:
+ zend_wrong_callback_or_null_error(num, name);
+ break;
case ZPP_ERROR_WRONG_CLASS:
zend_wrong_parameter_class_error(num, name, arg);
break;
@@ -337,6 +340,17 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, ch
}
/* }}} */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_or_null_error(uint32_t num, char *error) /* {{{ */
+{
+ if (EG(exception)) {
+ return;
+ }
+
+ zend_argument_type_error(num, "must be a valid callback or null, %s", error);
+ efree(error);
+}
+/* }}} */
+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void)
{
const char *space;
@@ -426,9 +440,41 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest) /* {{{ */
+static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32_t arg_num) {
+ zend_function *func = EG(current_execute_data)->func;
+ ZEND_ASSERT(arg_num > 0);
+ uint32_t arg_offset = arg_num - 1;
+ if (arg_offset >= func->common.num_args) {
+ ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_VARIADIC);
+ arg_offset = func->common.num_args;
+ }
+
+ zend_arg_info *arg_info = &func->common.arg_info[arg_offset];
+ zend_string *func_name = get_active_function_or_method_name();
+ const char *arg_name = get_active_function_arg_name(arg_num);
+
+ /* If no type is specified in arginfo, use the specified fallback_type determined through
+ * zend_parse_parameters instead. */
+ zend_string *type_str = zend_type_to_string(arg_info->type);
+ const char *type = type_str ? ZSTR_VAL(type_str) : fallback_type;
+ zend_error(E_DEPRECATED,
+ "%s(): Passing null to parameter #%" PRIu32 "%s%s%s of type %s is deprecated",
+ ZSTR_VAL(func_name), arg_num,
+ arg_name ? " ($" : "", arg_name ? arg_name : "", arg_name ? ")" : "",
+ type);
+ zend_string_release(func_name);
+ if (type_str) {
+ zend_string_release(type_str);
+ }
+ return !EG(exception);
+}
+
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, bool *dest, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) {
+ if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("bool", arg_num)) {
+ return 0;
+ }
*dest = zend_is_true(arg);
} else {
return 0;
@@ -437,16 +483,16 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest)
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, bool *dest, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
- return zend_parse_arg_bool_weak(arg, dest);
+ return zend_parse_arg_bool_weak(arg, dest, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) == IS_DOUBLE)) {
if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) {
@@ -479,6 +525,9 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest)
return 0;
}
} else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) {
+ if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("int", arg_num)) {
+ return 0;
+ }
*dest = 0;
} else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) {
*dest = 1;
@@ -489,16 +538,16 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest)
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
- return zend_parse_arg_long_weak(arg, dest);
+ return zend_parse_arg_long_weak(arg, dest, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
*dest = (double)Z_LVAL_P(arg);
@@ -517,6 +566,9 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest)
return 0;
}
} else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) {
+ if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("float", arg_num)) {
+ return 0;
+ }
*dest = 0.0;
} else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) {
*dest = 1.0;
@@ -527,7 +579,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest)
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
/* SSTH Exception: IS_LONG may be accepted instead as IS_DOUBLE */
@@ -535,11 +587,11 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest)
} else if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
- return zend_parse_arg_double_weak(arg, dest);
+ return zend_parse_arg_double_weak(arg, dest, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
@@ -558,6 +610,9 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest) /
}
zend_string_release(str);
} else if (Z_TYPE_P(arg) < IS_TRUE) {
+ if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("int|float", arg_num)) {
+ return 0;
+ }
ZVAL_LONG(arg, 0);
} else if (Z_TYPE_P(arg) == IS_TRUE) {
ZVAL_LONG(arg, 1);
@@ -569,9 +624,12 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest) /
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) {
+ if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string", arg_num)) {
+ return 0;
+ }
convert_to_string(arg);
*dest = Z_STR_P(arg);
} else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
@@ -591,24 +649,24 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **des
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
- return zend_parse_arg_str_weak(arg, dest);
+ return zend_parse_arg_str_weak(arg, dest, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
- if (zend_parse_arg_long_weak(arg, dest_long)) {
+ if (zend_parse_arg_long_weak(arg, dest_long, arg_num)) {
*dest_str = NULL;
return 1;
- } else if (zend_parse_arg_str_weak(arg, dest_str)) {
+ } else if (zend_parse_arg_str_weak(arg, dest_str, arg_num)) {
*dest_long = 0;
return 1;
} else {
@@ -617,7 +675,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_stri
}
/* }}} */
-static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec, char **error) /* {{{ */
+static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec, char **error, uint32_t arg_num) /* {{{ */
{
const char *spec_walk = *spec;
char c = *spec_walk++;
@@ -644,13 +702,13 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
case 'l':
{
zend_long *p = va_arg(*va, zend_long *);
- zend_bool *is_null = NULL;
+ bool *is_null = NULL;
if (check_null) {
- is_null = va_arg(*va, zend_bool *);
+ is_null = va_arg(*va, bool *);
}
- if (!zend_parse_arg_long(arg, p, is_null, check_null)) {
+ if (!zend_parse_arg_long(arg, p, is_null, check_null, arg_num)) {
return check_null ? "?int" : "int";
}
}
@@ -659,13 +717,13 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
case 'd':
{
double *p = va_arg(*va, double *);
- zend_bool *is_null = NULL;
+ bool *is_null = NULL;
if (check_null) {
- is_null = va_arg(*va, zend_bool *);
+ is_null = va_arg(*va, bool *);
}
- if (!zend_parse_arg_double(arg, p, is_null, check_null)) {
+ if (!zend_parse_arg_double(arg, p, is_null, check_null, arg_num)) {
return check_null ? "?float" : "float";
}
}
@@ -675,7 +733,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
{
zval **p = va_arg(*va, zval **);
- if (!zend_parse_arg_number(arg, p, check_null)) {
+ if (!zend_parse_arg_number(arg, p, check_null, arg_num)) {
return check_null ? "int|float|null" : "int|float";
}
}
@@ -685,7 +743,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
{
char **p = va_arg(*va, char **);
size_t *pl = va_arg(*va, size_t *);
- if (!zend_parse_arg_string(arg, p, pl, check_null)) {
+ if (!zend_parse_arg_string(arg, p, pl, check_null, arg_num)) {
return check_null ? "?string" : "string";
}
}
@@ -695,7 +753,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
{
char **p = va_arg(*va, char **);
size_t *pl = va_arg(*va, size_t *);
- if (!zend_parse_arg_path(arg, p, pl, check_null)) {
+ if (!zend_parse_arg_path(arg, p, pl, check_null, arg_num)) {
if (Z_TYPE_P(arg) == IS_STRING) {
zend_spprintf(error, 0, "must not contain any null bytes");
return "";
@@ -709,7 +767,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
case 'P':
{
zend_string **str = va_arg(*va, zend_string **);
- if (!zend_parse_arg_path_str(arg, str, check_null)) {
+ if (!zend_parse_arg_path_str(arg, str, check_null, arg_num)) {
if (Z_TYPE_P(arg) == IS_STRING) {
zend_spprintf(error, 0, "must not contain any null bytes");
return "";
@@ -723,7 +781,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
case 'S':
{
zend_string **str = va_arg(*va, zend_string **);
- if (!zend_parse_arg_str(arg, str, check_null)) {
+ if (!zend_parse_arg_str(arg, str, check_null, arg_num)) {
return check_null ? "?string" : "string";
}
}
@@ -731,14 +789,14 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
case 'b':
{
- zend_bool *p = va_arg(*va, zend_bool *);
- zend_bool *is_null = NULL;
+ bool *p = va_arg(*va, bool *);
+ bool *is_null = NULL;
if (check_null) {
- is_null = va_arg(*va, zend_bool *);
+ is_null = va_arg(*va, bool *);
}
- if (!zend_parse_arg_bool(arg, p, is_null, check_null)) {
+ if (!zend_parse_arg_bool(arg, p, is_null, check_null, arg_num)) {
return check_null ? "?bool" : "bool";
}
}
@@ -899,7 +957,7 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons
const char *expected_type = NULL;
char *error = NULL;
- expected_type = zend_parse_arg_impl(arg, va, spec, &error);
+ expected_type = zend_parse_arg_impl(arg, va, spec, &error, arg_num);
if (expected_type) {
if (EG(exception)) {
return FAILURE;
@@ -956,8 +1014,8 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
uint32_t max_num_args = 0;
uint32_t post_varargs = 0;
zval *arg;
- zend_bool have_varargs = 0;
- zend_bool have_optional_args = 0;
+ bool have_varargs = 0;
+ bool have_optional_args = 0;
zval **varargs = NULL;
int *n_varargs = NULL;
@@ -1130,7 +1188,7 @@ ZEND_API zend_result zend_parse_method_parameters(uint32_t num_args, zval *this_
* Z_OBJ(EG(This)) to NULL when calling an internal function with common.scope == NULL.
* In that case EG(This) would still be the $this from the calling code and we'd take the
* wrong branch here. */
- zend_bool is_method = EG(current_execute_data)->func->common.scope != NULL;
+ bool is_method = EG(current_execute_data)->func->common.scope != NULL;
if (!is_method || !this_ptr || Z_TYPE_P(this_ptr) != IS_OBJECT) {
va_start(va, type_spec);
@@ -1213,39 +1271,147 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
}
/* }}} */
+static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *class_type) /* {{{ */
+{
+ zend_class_mutable_data *mutable_data;
+
+ ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
+ ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
+ ZEND_ASSERT(ZEND_MAP_PTR_GET_IMM(class_type->mutable_data) == NULL);
+
+ mutable_data = zend_arena_alloc(&CG(arena), sizeof(zend_class_mutable_data));
+ memset(mutable_data, 0, sizeof(zend_class_mutable_data));
+ mutable_data->ce_flags = class_type->ce_flags;
+ ZEND_MAP_PTR_SET_IMM(class_type->mutable_data, mutable_data);
+
+ return mutable_data;
+}
+/* }}} */
+
+ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */
+{
+ zend_class_mutable_data *mutable_data;
+ HashTable *constants_table;
+ zend_string *key;
+ zend_class_constant *new_c, *c;
+
+ constants_table = zend_arena_alloc(&CG(arena), sizeof(HashTable));
+ zend_hash_init(constants_table, zend_hash_num_elements(&class_type->constants_table), NULL, NULL, 0);
+ zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0);
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
+ if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
+ new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
+ memcpy(new_c, c, sizeof(zend_class_constant));
+ c = new_c;
+ }
+ _zend_hash_append_ptr(constants_table, key, c);
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
+ ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
+
+ mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
+ if (!mutable_data) {
+ mutable_data = zend_allocate_mutable_data(class_type);
+ }
+
+ mutable_data->constants_table = constants_table;
+
+ return constants_table;
+}
+
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
{
- if (!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
- zend_class_constant *c;
- zval *val;
- zend_property_info *prop_info;
+ zend_class_mutable_data *mutable_data = NULL;
+ zval *default_properties_table = NULL;
+ zval *static_members_table = NULL;
+ zend_class_constant *c;
+ zval *val;
+ zend_property_info *prop_info;
+ uint32_t ce_flags;
- if (class_type->parent) {
- if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) {
- return FAILURE;
+ ce_flags = class_type->ce_flags;
+
+ if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
+ return SUCCESS;
+ }
+
+ if (ce_flags & ZEND_ACC_IMMUTABLE) {
+ mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
+ if (mutable_data) {
+ ce_flags = mutable_data->ce_flags;
+ if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
+ return SUCCESS;
}
+ } else {
+ mutable_data = zend_allocate_mutable_data(class_type);
}
+ }
- ZEND_HASH_FOREACH_PTR(&class_type->constants_table, c) {
- val = &c->value;
- if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
+ if (class_type->parent) {
+ if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) {
+ return FAILURE;
+ }
+ }
+
+ if (ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) {
+ HashTable *constants_table;
+
+ if (ce_flags & ZEND_ACC_IMMUTABLE) {
+ constants_table = mutable_data->constants_table;
+ if (!constants_table) {
+ constants_table = zend_separate_class_constants_table(class_type);
+ }
+ } else {
+ constants_table = &class_type->constants_table;
+ }
+ ZEND_HASH_FOREACH_PTR(constants_table, c) {
+ if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
+ val = &c->value;
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
return FAILURE;
}
}
} ZEND_HASH_FOREACH_END();
+ }
- if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) {
- if (class_type->type == ZEND_INTERNAL_CLASS || (class_type->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
+ if (class_type->default_static_members_count) {
+ static_members_table = CE_STATIC_MEMBERS(class_type);
+ if (!static_members_table) {
+ if (class_type->type == ZEND_INTERNAL_CLASS || (ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
zend_class_init_statics(class_type);
+ static_members_table = CE_STATIC_MEMBERS(class_type);
}
}
+ }
+ default_properties_table = class_type->default_properties_table;
+ if ((ce_flags & ZEND_ACC_IMMUTABLE)
+ && (ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)) {
+ zval *src, *dst, *end;
+
+ default_properties_table = mutable_data->default_properties_table;
+ if (!default_properties_table) {
+ default_properties_table = zend_arena_alloc(&CG(arena), sizeof(zval) * class_type->default_properties_count);
+ src = class_type->default_properties_table;
+ dst = default_properties_table;
+ end = dst + class_type->default_properties_count;
+ do {
+ ZVAL_COPY_VALUE_PROP(dst, src);
+ src++;
+ dst++;
+ } while (dst != end);
+ mutable_data->default_properties_table = default_properties_table;
+ }
+ }
+
+ if (ce_flags & (ZEND_ACC_HAS_AST_PROPERTIES|ZEND_ACC_HAS_AST_STATICS)) {
ZEND_HASH_FOREACH_PTR(&class_type->properties_info, prop_info) {
if (prop_info->flags & ZEND_ACC_STATIC) {
- val = CE_STATIC_MEMBERS(class_type) + prop_info->offset;
+ val = static_members_table + prop_info->offset;
} else {
- val = (zval*)((char*)class_type->default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0));
+ val = (zval*)((char*)default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0));
}
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (ZEND_TYPE_IS_SET(prop_info->type)) {
@@ -1268,8 +1434,21 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
}
}
} ZEND_HASH_FOREACH_END();
+ }
- class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
+ ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
+ ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
+ ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
+ if (class_type->ce_flags & ZEND_ACC_IMMUTABLE) {
+ ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
+ if (mutable_data) {
+ mutable_data->ce_flags = ce_flags;
+ }
+ } else {
+ if (!(ce_flags & ZEND_ACC_PRELOADED)) {
+ ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
+ }
+ class_type->ce_flags = ce_flags;
}
return SUCCESS;
@@ -1279,7 +1458,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
static zend_always_inline void _object_properties_init(zend_object *object, zend_class_entry *class_type) /* {{{ */
{
if (class_type->default_properties_count) {
- zval *src = class_type->default_properties_table;
+ zval *src = CE_DEFAULT_PROPERTIES_TABLE(class_type);
zval *dst = object->properties_table;
zval *end = src + class_type->default_properties_count;
@@ -1531,6 +1710,33 @@ ZEND_API void add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, c
}
/* }}} */
+ZEND_API void add_assoc_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_ARR(&tmp, arr);
+ zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp);
+}
+/* }}} */
+
+ZEND_API void add_assoc_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_OBJ(&tmp, obj);
+ zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp);
+}
+/* }}} */
+
+ZEND_API void add_assoc_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_REF(&tmp, ref);
+ zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp);
+}
+/* }}} */
+
ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval *value) /* {{{ */
{
zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, value);
@@ -1609,6 +1815,33 @@ ZEND_API void add_index_stringl(zval *arg, zend_ulong index, const char *str, si
}
/* }}} */
+ZEND_API void add_index_array(zval *arg, zend_ulong index, zend_array *arr) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_ARR(&tmp, arr);
+ zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp);
+}
+/* }}} */
+
+ZEND_API void add_index_object(zval *arg, zend_ulong index, zend_object *obj) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_OBJ(&tmp, obj);
+ zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp);
+}
+/* }}} */
+
+ZEND_API void add_index_reference(zval *arg, zend_ulong index, zend_reference *ref) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_REF(&tmp, ref);
+ zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp);
+}
+/* }}} */
+
ZEND_API zend_result add_next_index_long(zval *arg, zend_long n) /* {{{ */
{
zval tmp;
@@ -1627,7 +1860,7 @@ ZEND_API zend_result add_next_index_null(zval *arg) /* {{{ */
}
/* }}} */
-ZEND_API zend_result add_next_index_bool(zval *arg, zend_bool b) /* {{{ */
+ZEND_API zend_result add_next_index_bool(zval *arg, bool b) /* {{{ */
{
zval tmp;
@@ -1681,6 +1914,33 @@ ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t l
}
/* }}} */
+ZEND_API zend_result add_next_index_array(zval *arg, zend_array *arr) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_ARR(&tmp, arr);
+ return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+ZEND_API zend_result add_next_index_object(zval *arg, zend_object *obj) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_OBJ(&tmp, obj);
+ return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+ZEND_API zend_result add_next_index_reference(zval *arg, zend_reference *ref) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_REF(&tmp, ref);
+ return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) /* {{{ */
{
zval *result;
@@ -1798,6 +2058,36 @@ ZEND_API void add_property_stringl_ex(zval *arg, const char *key, size_t key_len
}
/* }}} */
+ZEND_API void add_property_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_ARR(&tmp, arr);
+ add_property_zval_ex(arg, key, key_len, &tmp);
+ zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */
+}
+/* }}} */
+
+ZEND_API void add_property_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_OBJ(&tmp, obj);
+ add_property_zval_ex(arg, key, key_len, &tmp);
+ zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */
+}
+/* }}} */
+
+ZEND_API void add_property_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref) /* {{{ */
+{
+ zval tmp;
+
+ ZVAL_REF(&tmp, ref);
+ add_property_zval_ex(arg, key, key_len, &tmp);
+ zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */
+}
+/* }}} */
+
ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, zval *value) /* {{{ */
{
zend_string *str;
@@ -2838,7 +3128,7 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
/* }}} */
// TODO num_symbol_tables as unsigned int?
-ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, zend_bool is_ref, int num_symbol_tables, ...) /* {{{ */
+ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...) /* {{{ */
{
HashTable *symbol_table;
va_list symbol_table_list;
@@ -3337,11 +3627,11 @@ ZEND_API zend_string *zend_get_callable_name(zval *callable) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_is_callable_at_frame(
+ZEND_API bool zend_is_callable_at_frame(
zval *callable, zend_object *object, zend_execute_data *frame,
uint32_t check_flags, zend_fcall_info_cache *fcc, char **error) /* {{{ */
{
- zend_bool ret;
+ bool ret;
zend_fcall_info_cache fcc_local;
bool strict_class = 0;
@@ -3458,7 +3748,7 @@ check_func:
}
/* }}} */
-ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */
+ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */
{
/* Determine callability at the first parent user frame. */
zend_execute_data *frame = EG(current_execute_data);
@@ -3466,20 +3756,20 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint
frame = frame->prev_execute_data;
}
- zend_bool ret = zend_is_callable_at_frame(callable, object, frame, check_flags, fcc, error);
+ bool ret = zend_is_callable_at_frame(callable, object, frame, check_flags, fcc, error);
if (callable_name) {
*callable_name = zend_get_callable_name_ex(callable, object);
}
return ret;
}
-ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */
+ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */
{
return zend_is_callable_ex(callable, NULL, check_flags, callable_name, NULL, NULL);
}
/* }}} */
-ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name) /* {{{ */
+ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name) /* {{{ */
{
zend_fcall_info_cache fcc;
@@ -3679,7 +3969,7 @@ static inline zend_string *zval_make_interned_string(zval *zv) /* {{{ */
return Z_STR_P(zv);
}
-static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) {
+static zend_always_inline bool is_persistent_class(zend_class_entry *ce) {
return (ce->type & ZEND_INTERNAL_CLASS)
&& ce->info.internal.module->type == MODULE_PERSISTENT;
}
@@ -3698,6 +3988,11 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
if (Z_TYPE_P(property) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ if (access_type & ZEND_ACC_STATIC) {
+ ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
+ } else {
+ ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
+ }
}
}
@@ -3787,7 +4082,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
}
/* }}} */
-ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val, zend_bool strict) /* {{{ */
+ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val, bool strict) /* {{{ */
{
if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, val, strict))) {
zval_ptr_dtor(val);
@@ -3815,7 +4110,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_null(zend_reference *ref) /* {{{
}
/* }}} */
-ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val) /* {{{ */
+ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, bool val) /* {{{ */
{
zval tmp;
@@ -3905,7 +4200,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval(zend_reference *ref, zval *z
}
/* }}} */
-ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict) /* {{{ */
+ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, bool strict) /* {{{ */
{
zval tmp;
@@ -4013,6 +4308,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
c->ce = ce;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
}
if (!zend_hash_add_ptr(&ce->constants_table, name, c)) {
@@ -4056,7 +4352,7 @@ ZEND_API void zend_declare_class_constant_long(zend_class_entry *ce, const char
}
/* }}} */
-ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value) /* {{{ */
+ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, bool value) /* {{{ */
{
zval constant;
@@ -4297,7 +4593,7 @@ ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope
}
/* }}} */
-ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zend_bool silent, zval *rv) /* {{{ */
+ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv) /* {{{ */
{
zval *value;
zend_class_entry *old_scope = EG(fake_scope);
@@ -4311,7 +4607,7 @@ ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *objec
}
/* }}} */
-ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */
+ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv) /* {{{ */
{
zval *value;
zend_string *str;
@@ -4323,7 +4619,7 @@ ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object,
}
/* }}} */
-ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, zend_bool silent) /* {{{ */
+ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, bool silent) /* {{{ */
{
zval *property;
zend_class_entry *old_scope = EG(fake_scope);
@@ -4336,7 +4632,7 @@ ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string
}
/* }}} */
-ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent) /* {{{ */
+ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, bool silent) /* {{{ */
{
zend_string *key = zend_string_init(name, name_length, 0);
zval *property = zend_read_static_property_ex(scope, key, silent);
@@ -4382,7 +4678,7 @@ ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce)
}
/* }}} */
-ZEND_API zend_bool zend_is_iterable(zval *iterable) /* {{{ */
+ZEND_API bool zend_is_iterable(zval *iterable) /* {{{ */
{
switch (Z_TYPE_P(iterable)) {
case IS_ARRAY:
@@ -4395,7 +4691,7 @@ ZEND_API zend_bool zend_is_iterable(zval *iterable) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_is_countable(zval *countable) /* {{{ */
+ZEND_API bool zend_is_countable(zval *countable) /* {{{ */
{
switch (Z_TYPE_P(countable)) {
case IS_ARRAY:
@@ -4419,7 +4715,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def
zend_string *code = zend_string_concat3(
"<?php ", sizeof("<?php ") - 1, default_value, strlen(default_value), ";", 1);
- ast = zend_compile_string_to_ast(code, &ast_arena, "");
+ ast = zend_compile_string_to_ast(code, &ast_arena, ZSTR_EMPTY_ALLOC());
zend_string_release(code);
if (!ast) {
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 290db040b2..8072befb03 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -278,6 +278,12 @@ typedef struct _zend_fcall_info_cache {
#define CE_STATIC_MEMBERS(ce) \
((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table))
+#define CE_CONSTANTS_TABLE(ce) \
+ zend_class_constants_table(ce)
+
+#define CE_DEFAULT_PROPERTIES_TABLE(ce) \
+ zend_class_default_properties_table(ce)
+
#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
ZEND_API int zend_next_free_module(void);
@@ -352,12 +358,12 @@ ZEND_API ZEND_COLD void zend_wrong_param_count(void);
ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc);
ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object);
ZEND_API zend_string *zend_get_callable_name(zval *callable);
-ZEND_API zend_bool zend_is_callable_at_frame(
+ZEND_API bool zend_is_callable_at_frame(
zval *callable, zend_object *object, zend_execute_data *frame,
uint32_t check_flags, zend_fcall_info_cache *fcc, char **error);
-ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error);
-ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name);
-ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name);
+ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error);
+ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name);
+ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name);
ZEND_API const char *zend_get_module_version(const char *module_name);
ZEND_API int zend_get_module_started(const char *module_name);
@@ -376,12 +382,39 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value);
ZEND_API void zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length);
ZEND_API void zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value);
-ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value);
+ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, bool value);
ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const char *name, size_t name_length, double value);
ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length);
ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
+ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
+
+static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) {
+ if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS)
+ && (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+ zend_class_mutable_data *mutable_data =
+ (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ if (mutable_data && mutable_data->constants_table) {
+ return mutable_data->constants_table;
+ } else {
+ return zend_separate_class_constants_table(ce);
+ }
+ } else {
+ return &ce->constants_table;
+ }
+}
+
+static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) {
+ if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)
+ && (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+ zend_class_mutable_data *mutable_data =
+ (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ return mutable_data->default_properties_table;
+ } else {
+ return ce->default_properties_table;
+ }
+}
ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zval *value);
ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value);
@@ -403,11 +436,11 @@ ZEND_API zend_result zend_update_static_property_double(zend_class_entry *scope,
ZEND_API zend_result zend_update_static_property_string(zend_class_entry *scope, const char *name, size_t name_length, const char *value);
ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length);
-ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zend_bool silent, zval *rv);
-ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_bool silent, zval *rv);
+ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv);
+ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv);
-ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, zend_bool silent);
-ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent);
+ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, bool silent);
+ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, bool silent);
ZEND_API const char *zend_get_type_by_const(int type);
@@ -445,6 +478,9 @@ ZEND_API void add_assoc_double_ex(zval *arg, const char *key, size_t key_len, do
ZEND_API void add_assoc_str_ex(zval *arg, const char *key, size_t key_len, zend_string *str);
ZEND_API void add_assoc_string_ex(zval *arg, const char *key, size_t key_len, const char *str);
ZEND_API void add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, const char *str, size_t length);
+ZEND_API void add_assoc_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr);
+ZEND_API void add_assoc_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj);
+ZEND_API void add_assoc_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref);
ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval *value);
#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n)
@@ -455,6 +491,9 @@ ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval
#define add_assoc_str(__arg, __key, __str) add_assoc_str_ex(__arg, __key, strlen(__key), __str)
#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str)
#define add_assoc_stringl(__arg, __key, __str, __length) add_assoc_stringl_ex(__arg, __key, strlen(__key), __str, __length)
+#define add_assoc_array(__arg, __key, __arr) add_assoc_array_ex(__arg, __key, strlen(__key), __arr)
+#define add_assoc_object(__arg, __key, __obj) add_assoc_object_ex(__arg, __key, strlen(__key), __obj)
+#define add_assoc_reference(__arg, __key, __ref) add_assoc_object_ex(__arg, __key, strlen(__key), __ref)
#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value)
ZEND_API void add_index_long(zval *arg, zend_ulong index, zend_long n);
@@ -465,6 +504,9 @@ ZEND_API void add_index_double(zval *arg, zend_ulong index, double d);
ZEND_API void add_index_str(zval *arg, zend_ulong index, zend_string *str);
ZEND_API void add_index_string(zval *arg, zend_ulong index, const char *str);
ZEND_API void add_index_stringl(zval *arg, zend_ulong index, const char *str, size_t length);
+ZEND_API void add_index_array(zval *arg, zend_ulong index, zend_array *arr);
+ZEND_API void add_index_object(zval *arg, zend_ulong index, zend_object *obj);
+ZEND_API void add_index_reference(zval *arg, zend_ulong index, zend_reference *ref);
static zend_always_inline zend_result add_index_zval(zval *arg, zend_ulong index, zval *value)
{
@@ -473,12 +515,15 @@ static zend_always_inline zend_result add_index_zval(zval *arg, zend_ulong index
ZEND_API zend_result add_next_index_long(zval *arg, zend_long n);
ZEND_API zend_result add_next_index_null(zval *arg);
-ZEND_API zend_result add_next_index_bool(zval *arg, zend_bool b);
+ZEND_API zend_result add_next_index_bool(zval *arg, bool b);
ZEND_API zend_result add_next_index_resource(zval *arg, zend_resource *r);
ZEND_API zend_result add_next_index_double(zval *arg, double d);
ZEND_API zend_result add_next_index_str(zval *arg, zend_string *str);
ZEND_API zend_result add_next_index_string(zval *arg, const char *str);
ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t length);
+ZEND_API zend_result add_next_index_array(zval *arg, zend_array *arr);
+ZEND_API zend_result add_next_index_object(zval *arg, zend_object *obj);
+ZEND_API zend_result add_next_index_reference(zval *arg, zend_reference *ref);
static zend_always_inline zend_result add_next_index_zval(zval *arg, zval *value)
{
@@ -495,6 +540,9 @@ ZEND_API void add_property_double_ex(zval *arg, const char *key, size_t key_len,
ZEND_API void add_property_str_ex(zval *arg, const char *key, size_t key_len, zend_string *str);
ZEND_API void add_property_string_ex(zval *arg, const char *key, size_t key_len, const char *str);
ZEND_API void add_property_stringl_ex(zval *arg, const char *key, size_t key_len, const char *str, size_t length);
+ZEND_API void add_property_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr);
+ZEND_API void add_property_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj);
+ZEND_API void add_property_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref);
ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, zval *value);
#define add_property_long(__arg, __key, __n) add_property_long_ex(__arg, __key, strlen(__key), __n)
@@ -505,6 +553,9 @@ ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, z
#define add_property_str(__arg, __key, __str) add_property_str_ex(__arg, __key, strlen(__key), __str)
#define add_property_string(__arg, __key, __str) add_property_string_ex(__arg, __key, strlen(__key), __str)
#define add_property_stringl(__arg, __key, __str, __length) add_property_stringl_ex(__arg, __key, strlen(__key), __str, __length)
+#define add_property_array(__arg, __key, __arr) add_property_array_ex(__arg, __key, strlen(__key), __arr)
+#define add_property_object(__arg, __key, __obj) add_property_object_ex(__arg, __key, strlen(__key), __obj)
+#define add_property_reference(__arg, __key, __ref) add_property_reference_ex(__arg, __key, strlen(__key), __ref)
#define add_property_zval(__arg, __key, __value) add_property_zval_ex(__arg, __key, strlen(__key), __value)
@@ -605,7 +656,7 @@ static zend_always_inline void zend_call_known_instance_method_with_1_params(
ZEND_API void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
-ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, zend_bool is_ref, int num_symbol_tables, ...);
+ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...);
ZEND_API zend_result zend_delete_global_variable(zend_string *name);
@@ -630,9 +681,9 @@ static zend_always_inline zend_result zend_forbid_dynamic_call(const char *func_
ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce);
-ZEND_API zend_bool zend_is_iterable(zval *iterable);
+ZEND_API bool zend_is_iterable(zval *iterable);
-ZEND_API zend_bool zend_is_countable(zval *countable);
+ZEND_API bool zend_is_countable(zval *countable);
ZEND_API zend_result zend_get_default_from_internal_arg_info(
zval *default_value_zval, zend_internal_arg_info *arg_info);
@@ -779,11 +830,11 @@ END_EXTERN_C()
/* May modify arg in-place. Will free arg in failure case (and take ownership in success case).
* Prefer using the ZEND_TRY_ASSIGN_* macros over these APIs. */
-ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *zv, zend_bool strict);
+ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *zv, bool strict);
ZEND_API zend_result zend_try_assign_typed_ref(zend_reference *ref, zval *zv);
ZEND_API zend_result zend_try_assign_typed_ref_null(zend_reference *ref);
-ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val);
+ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, bool val);
ZEND_API zend_result zend_try_assign_typed_ref_long(zend_reference *ref, zend_long lval);
ZEND_API zend_result zend_try_assign_typed_ref_double(zend_reference *ref, double dval);
ZEND_API zend_result zend_try_assign_typed_ref_empty_string(zend_reference *ref);
@@ -793,7 +844,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_stringl(zend_reference *ref, cons
ZEND_API zend_result zend_try_assign_typed_ref_arr(zend_reference *ref, zend_array *arr);
ZEND_API zend_result zend_try_assign_typed_ref_res(zend_reference *ref, zend_resource *res);
ZEND_API zend_result zend_try_assign_typed_ref_zval(zend_reference *ref, zval *zv);
-ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict);
+ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, bool strict);
#define _ZEND_TRY_ASSIGN_NULL(zv, is_ref) do { \
zval *_zv = zv; \
@@ -1255,6 +1306,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_or_null_error(uint32_t num, char *error);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void);
ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...);
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...);
@@ -1272,6 +1324,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define ZPP_ERROR_WRONG_ARG 9
#define ZPP_ERROR_WRONG_COUNT 10
#define ZPP_ERROR_UNEXPECTED_EXTRA_NAMED 11
+#define ZPP_ERROR_WRONG_CALLBACK_OR_NULL 12
#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
const int _flags = (flags); \
@@ -1282,8 +1335,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
zval *_real_arg, *_arg = NULL; \
zend_expected_type _expected_type = Z_EXPECTED_LONG; \
char *_error = NULL; \
- ZEND_ATTRIBUTE_UNUSED zend_bool _dummy; \
- zend_bool _optional = 0; \
+ ZEND_ATTRIBUTE_UNUSED bool _dummy; \
+ bool _optional = 0; \
int _error_code = ZPP_ERROR_OK; \
((void)_i); \
((void)_real_arg); \
@@ -1314,6 +1367,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
} while (0)
#define ZEND_PARSE_PARAMETERS_END_EX(failure) \
+ ZEND_ASSERT(_i == _max_num_args || _max_num_args == (uint32_t) -1); \
} while (0); \
if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) { \
if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
@@ -1396,17 +1450,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_ITERABLE_EX(dest, 1)
/* old "b" */
-#define Z_PARAM_BOOL_EX2(dest, is_null, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_bool(_arg, &dest, &is_null, check_null))) { \
+#define Z_PARAM_BOOL_EX(dest, is_null, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_bool(_arg, &dest, &is_null, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_BOOL_OR_NULL : Z_EXPECTED_BOOL; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_BOOL_EX(dest, is_null, check_null, separate) \
- Z_PARAM_BOOL_EX2(dest, is_null, check_null, separate, separate)
-
#define Z_PARAM_BOOL(dest) \
Z_PARAM_BOOL_EX(dest, _dummy, 0, 0)
@@ -1414,16 +1465,13 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_BOOL_EX(dest, is_null, 1, 0)
/* old "C" */
-#define Z_PARAM_CLASS_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+#define Z_PARAM_CLASS_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _i, check_null))) { \
_error_code = ZPP_ERROR_FAILURE; \
break; \
}
-#define Z_PARAM_CLASS_EX(dest, check_null, separate) \
- Z_PARAM_CLASS_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_CLASS(dest) \
Z_PARAM_CLASS_EX(dest, 0, 0)
@@ -1446,7 +1494,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_OBJ_OR_STR_EX(destination_object, destination_string, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, NULL, &destination_string, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, NULL, &destination_string, allow_null, _i))) { \
_expected_type = allow_null ? Z_EXPECTED_OBJECT_OR_STRING_OR_NULL : Z_EXPECTED_OBJECT_OR_STRING; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1460,7 +1508,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_OBJ_OF_CLASS_OR_STR_EX(destination_object, base_ce, destination_string, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, base_ce, &destination_string, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, base_ce, &destination_string, allow_null, _i))) { \
if (base_ce) { \
_error = ZSTR_VAL((base_ce)->name); \
_error_code = allow_null ? ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL : ZPP_ERROR_WRONG_CLASS_OR_STRING; \
@@ -1479,17 +1527,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_OBJ_OF_CLASS_OR_STR_EX(destination_object, base_ce, destination_string, 1);
/* old "d" */
-#define Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_double(_arg, &dest, &is_null, check_null))) { \
+#define Z_PARAM_DOUBLE_EX(dest, is_null, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_double(_arg, &dest, &is_null, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_DOUBLE_OR_NULL : Z_EXPECTED_DOUBLE; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_DOUBLE_EX(dest, is_null, check_null, separate) \
- Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, separate, separate)
-
#define Z_PARAM_DOUBLE(dest) \
Z_PARAM_DOUBLE_EX(dest, _dummy, 0, 0)
@@ -1497,21 +1542,18 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_DOUBLE_EX(dest, is_null, 1, 0)
/* old "f" */
-#define Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error))) { \
if (!_error) { \
_expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \
_error_code = ZPP_ERROR_WRONG_ARG; \
} else { \
- _error_code = ZPP_ERROR_WRONG_CALLBACK; \
+ _error_code = check_null ? ZPP_ERROR_WRONG_CALLBACK_OR_NULL : ZPP_ERROR_WRONG_CALLBACK; \
} \
break; \
} \
-#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, separate) \
- Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, check_null, separate, separate)
-
#define Z_PARAM_FUNC(dest_fci, dest_fcc) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0)
@@ -1538,7 +1580,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_ARRAY_HT_OR_LONG_EX(dest_ht, dest_long, is_null, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_array_ht_or_long(_arg, &dest_ht, &dest_long, &is_null, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_array_ht_or_long(_arg, &dest_ht, &dest_long, &is_null, allow_null, _i))) { \
_expected_type = allow_null ? Z_EXPECTED_ARRAY_OR_LONG_OR_NULL : Z_EXPECTED_ARRAY_OR_LONG; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1566,17 +1608,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_ARRAY_OR_OBJECT_HT_EX(dest, 0, 0)
/* old "l" */
-#define Z_PARAM_LONG_EX2(dest, is_null, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null))) { \
+#define Z_PARAM_LONG_EX(dest, is_null, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_LONG_OR_NULL : Z_EXPECTED_LONG; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_LONG_EX(dest, is_null, check_null, separate) \
- Z_PARAM_LONG_EX2(dest, is_null, check_null, separate, separate)
-
#define Z_PARAM_LONG(dest) \
Z_PARAM_LONG_EX(dest, _dummy, 0, 0)
@@ -1586,7 +1625,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
/* old "n" */
#define Z_PARAM_NUMBER_EX(dest, check_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_number(_arg, &dest, check_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_number(_arg, &dest, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_NUMBER_OR_NULL : Z_EXPECTED_NUMBER; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1599,35 +1638,29 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_NUMBER_EX(dest, 0)
/* old "o" */
-#define Z_PARAM_OBJECT_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+#define Z_PARAM_OBJECT_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, NULL, check_null))) { \
_expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_OBJECT_EX(dest, check_null, separate) \
- Z_PARAM_OBJECT_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_OBJECT(dest) \
Z_PARAM_OBJECT_EX(dest, 0, 0)
#define Z_PARAM_OBJECT_OR_NULL(dest) \
Z_PARAM_OBJECT_EX(dest, 1, 0)
-/* The same as Z_PARAM_OBJECT_EX2 except that dest is a zend_object rather than a zval */
-#define Z_PARAM_OBJ_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+/* The same as Z_PARAM_OBJECT_EX except that dest is a zend_object rather than a zval */
+#define Z_PARAM_OBJ_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, NULL, check_null))) { \
_expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_OBJ_EX(dest, check_null, separate) \
- Z_PARAM_OBJ_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_OBJ(dest) \
Z_PARAM_OBJ_EX(dest, 0, 0)
@@ -1635,8 +1668,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_OBJ_EX(dest, 1, 0)
/* old "O" */
-#define Z_PARAM_OBJECT_OF_CLASS_EX2(dest, _ce, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+#define Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, _ce, check_null))) { \
if (_ce) { \
_error = ZSTR_VAL((_ce)->name); \
@@ -1649,18 +1682,15 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
} \
}
-#define Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, check_null, separate) \
- Z_PARAM_OBJECT_OF_CLASS_EX2(dest, _ce, check_null, separate, separate)
-
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce) \
Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, 0, 0)
#define Z_PARAM_OBJECT_OF_CLASS_OR_NULL(dest, _ce) \
Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, 1, 0)
-/* The same as Z_PARAM_OBJECT_OF_CLASS_EX2 except that dest is a zend_object rather than a zval */
-#define Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+/* The same as Z_PARAM_OBJECT_OF_CLASS_EX except that dest is a zend_object rather than a zval */
+#define Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, _ce, check_null))) { \
if (_ce) { \
_error = ZSTR_VAL((_ce)->name); \
@@ -1673,9 +1703,6 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
} \
}
-#define Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, check_null, separate) \
- Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, separate, separate)
-
#define Z_PARAM_OBJ_OF_CLASS(dest, _ce) \
Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, 0, 0)
@@ -1684,7 +1711,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_obj_or_long(_arg, &dest_obj, _ce, &dest_long, &is_null, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_obj_or_long(_arg, &dest_obj, _ce, &dest_long, &is_null, allow_null, _i))) { \
_error = ZSTR_VAL((_ce)->name); \
_error_code = allow_null ? ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL : ZPP_ERROR_WRONG_CLASS_OR_LONG; \
break; \
@@ -1697,17 +1724,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1)
/* old "p" */
-#define Z_PARAM_PATH_EX2(dest, dest_len, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_path(_arg, &dest, &dest_len, check_null))) { \
+#define Z_PARAM_PATH_EX(dest, dest_len, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_path(_arg, &dest, &dest_len, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_PATH_EX(dest, dest_len, check_null, separate) \
- Z_PARAM_PATH_EX2(dest, dest_len, check_null, separate, separate)
-
#define Z_PARAM_PATH(dest, dest_len) \
Z_PARAM_PATH_EX(dest, dest_len, 0, 0)
@@ -1715,17 +1739,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_PATH_EX(dest, dest_len, 1, 0)
/* old "P" */
-#define Z_PARAM_PATH_STR_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_path_str(_arg, &dest, check_null))) { \
+#define Z_PARAM_PATH_STR_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_path_str(_arg, &dest, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_PATH_STR_EX(dest, check_null, separate) \
- Z_PARAM_PATH_STR_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_PATH_STR(dest) \
Z_PARAM_PATH_STR_EX(dest, 0, 0)
@@ -1733,17 +1754,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_PATH_STR_EX(dest, 1, 0)
/* old "r" */
-#define Z_PARAM_RESOURCE_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
+#define Z_PARAM_RESOURCE_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_resource(_arg, &dest, check_null))) { \
_expected_type = check_null ? Z_EXPECTED_RESOURCE_OR_NULL : Z_EXPECTED_RESOURCE; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_RESOURCE_EX(dest, check_null, separate) \
- Z_PARAM_RESOURCE_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_RESOURCE(dest) \
Z_PARAM_RESOURCE_EX(dest, 0, 0)
@@ -1751,17 +1769,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_RESOURCE_EX(dest, 1, 0)
/* old "s" */
-#define Z_PARAM_STRING_EX2(dest, dest_len, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_string(_arg, &dest, &dest_len, check_null))) { \
+#define Z_PARAM_STRING_EX(dest, dest_len, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_string(_arg, &dest, &dest_len, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_STRING_EX(dest, dest_len, check_null, separate) \
- Z_PARAM_STRING_EX2(dest, dest_len, check_null, separate, separate)
-
#define Z_PARAM_STRING(dest, dest_len) \
Z_PARAM_STRING_EX(dest, dest_len, 0, 0)
@@ -1769,17 +1784,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_STRING_EX(dest, dest_len, 1, 0)
/* old "S" */
-#define Z_PARAM_STR_EX2(dest, check_null, deref, separate) \
- Z_PARAM_PROLOGUE(deref, separate); \
- if (UNEXPECTED(!zend_parse_arg_str(_arg, &dest, check_null))) { \
+#define Z_PARAM_STR_EX(dest, check_null, deref) \
+ Z_PARAM_PROLOGUE(deref, 0); \
+ if (UNEXPECTED(!zend_parse_arg_str(_arg, &dest, check_null, _i))) { \
_expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}
-#define Z_PARAM_STR_EX(dest, check_null, separate) \
- Z_PARAM_STR_EX2(dest, check_null, separate, separate)
-
#define Z_PARAM_STR(dest) \
Z_PARAM_STR_EX(dest, 0, 0)
@@ -1839,7 +1851,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_ARRAY_HT_OR_STR_EX(dest_ht, dest_str, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_array_ht_or_str(_arg, &dest_ht, &dest_str, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_array_ht_or_str(_arg, &dest_ht, &dest_str, allow_null, _i))) { \
_expected_type = allow_null ? Z_EXPECTED_ARRAY_OR_STRING_OR_NULL : Z_EXPECTED_ARRAY_OR_STRING; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1853,7 +1865,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_STR_OR_LONG_EX(dest_str, dest_long, is_null, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
- if (UNEXPECTED(!zend_parse_arg_str_or_long(_arg, &dest_str, &dest_long, &is_null, allow_null))) { \
+ if (UNEXPECTED(!zend_parse_arg_str_or_long(_arg, &dest_str, &dest_long, &is_null, allow_null, _i))) { \
_expected_type = allow_null ? Z_EXPECTED_STRING_OR_LONG_OR_NULL : Z_EXPECTED_STRING_OR_LONG; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1870,18 +1882,18 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
/* Inlined implementations shared by new and old parameter parsing APIs */
ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long);
-
-static zend_always_inline bool zend_parse_arg_bool(zval *arg, zend_bool *dest, zend_bool *is_null, bool check_null)
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, bool *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, bool *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num);
+
+static zend_always_inline bool zend_parse_arg_bool(zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num)
{
if (check_null) {
*is_null = 0;
@@ -1894,12 +1906,12 @@ static zend_always_inline bool zend_parse_arg_bool(zval *arg, zend_bool *dest, z
*is_null = 1;
*dest = 0;
} else {
- return zend_parse_arg_bool_slow(arg, dest);
+ return zend_parse_arg_bool_slow(arg, dest, arg_num);
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, zend_bool *is_null, bool check_null)
+static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, bool *is_null, bool check_null, uint32_t arg_num)
{
if (check_null) {
*is_null = 0;
@@ -1910,12 +1922,12 @@ static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, z
*is_null = 1;
*dest = 0;
} else {
- return zend_parse_arg_long_slow(arg, dest);
+ return zend_parse_arg_long_slow(arg, dest, arg_num);
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_double(zval *arg, double *dest, zend_bool *is_null, bool check_null)
+static zend_always_inline bool zend_parse_arg_double(zval *arg, double *dest, bool *is_null, bool check_null, uint32_t arg_num)
{
if (check_null) {
*is_null = 0;
@@ -1926,40 +1938,40 @@ static zend_always_inline bool zend_parse_arg_double(zval *arg, double *dest, ze
*is_null = 1;
*dest = 0.0;
} else {
- return zend_parse_arg_double_slow(arg, dest);
+ return zend_parse_arg_double_slow(arg, dest, arg_num);
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_number(zval *arg, zval **dest, bool check_null)
+static zend_always_inline bool zend_parse_arg_number(zval *arg, zval **dest, bool check_null, uint32_t arg_num)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_LONG || Z_TYPE_P(arg) == IS_DOUBLE)) {
*dest = arg;
} else if (check_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
*dest = NULL;
} else {
- return zend_parse_arg_number_slow(arg, dest);
+ return zend_parse_arg_number_slow(arg, dest, arg_num);
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, bool check_null)
+static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, bool check_null, uint32_t arg_num)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
*dest = Z_STR_P(arg);
} else if (check_null && Z_TYPE_P(arg) == IS_NULL) {
*dest = NULL;
} else {
- return zend_parse_arg_str_slow(arg, dest);
+ return zend_parse_arg_str_slow(arg, dest, arg_num);
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_string(zval *arg, char **dest, size_t *dest_len, bool check_null)
+static zend_always_inline bool zend_parse_arg_string(zval *arg, char **dest, size_t *dest_len, bool check_null, uint32_t arg_num)
{
zend_string *str;
- if (!zend_parse_arg_str(arg, &str, check_null)) {
+ if (!zend_parse_arg_str(arg, &str, check_null, arg_num)) {
return 0;
}
if (check_null && UNEXPECTED(!str)) {
@@ -1972,20 +1984,20 @@ static zend_always_inline bool zend_parse_arg_string(zval *arg, char **dest, siz
return 1;
}
-static zend_always_inline bool zend_parse_arg_path_str(zval *arg, zend_string **dest, bool check_null)
+static zend_always_inline bool zend_parse_arg_path_str(zval *arg, zend_string **dest, bool check_null, uint32_t arg_num)
{
- if (!zend_parse_arg_str(arg, dest, check_null) ||
+ if (!zend_parse_arg_str(arg, dest, check_null, arg_num) ||
(*dest && UNEXPECTED(CHECK_NULL_PATH(ZSTR_VAL(*dest), ZSTR_LEN(*dest))))) {
return 0;
}
return 1;
}
-static zend_always_inline bool zend_parse_arg_path(zval *arg, char **dest, size_t *dest_len, bool check_null)
+static zend_always_inline bool zend_parse_arg_path(zval *arg, char **dest, size_t *dest_len, bool check_null, uint32_t arg_num)
{
zend_string *str;
- if (!zend_parse_arg_path_str(arg, &str, check_null)) {
+ if (!zend_parse_arg_path_str(arg, &str, check_null, arg_num)) {
return 0;
}
if (check_null && UNEXPECTED(!str)) {
@@ -2050,7 +2062,7 @@ static zend_always_inline bool zend_parse_arg_array_ht(zval *arg, HashTable **de
}
static zend_always_inline bool zend_parse_arg_array_ht_or_long(
- zval *arg, HashTable **dest_ht, zend_long *dest_long, zend_bool *is_null, bool allow_null
+ zval *arg, HashTable **dest_ht, zend_long *dest_long, bool *is_null, bool allow_null, uint32_t arg_num
) {
if (allow_null) {
*is_null = 0;
@@ -2066,7 +2078,7 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_long(
*is_null = 1;
} else {
*dest_ht = NULL;
- return zend_parse_arg_long_slow(arg, dest_long);
+ return zend_parse_arg_long_slow(arg, dest_long, arg_num);
}
return 1;
@@ -2099,7 +2111,7 @@ static zend_always_inline bool zend_parse_arg_obj(zval *arg, zend_object **dest,
}
static zend_always_inline bool zend_parse_arg_obj_or_long(
- zval *arg, zend_object **dest_obj, zend_class_entry *ce, zend_long *dest_long, zend_bool *is_null, bool allow_null
+ zval *arg, zend_object **dest_obj, zend_class_entry *ce, zend_long *dest_long, bool *is_null, bool allow_null, uint32_t arg_num
) {
if (allow_null) {
*is_null = 0;
@@ -2115,7 +2127,7 @@ static zend_always_inline bool zend_parse_arg_obj_or_long(
*is_null = 1;
} else {
*dest_obj = NULL;
- return zend_parse_arg_long_slow(arg, dest_long);
+ return zend_parse_arg_long_slow(arg, dest_long, arg_num);
}
return 1;
@@ -2163,7 +2175,7 @@ static zend_always_inline void zend_parse_arg_zval_deref(zval *arg, zval **dest,
}
static zend_always_inline bool zend_parse_arg_array_ht_or_str(
- zval *arg, HashTable **dest_ht, zend_string **dest_str, bool allow_null)
+ zval *arg, HashTable **dest_ht, zend_string **dest_str, bool allow_null, uint32_t arg_num)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
*dest_ht = NULL;
@@ -2176,13 +2188,13 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str(
*dest_str = NULL;
} else {
*dest_ht = NULL;
- return zend_parse_arg_str_slow(arg, dest_str);
+ return zend_parse_arg_str_slow(arg, dest_str, arg_num);
}
return 1;
}
static zend_always_inline bool zend_parse_arg_str_or_long(zval *arg, zend_string **dest_str, zend_long *dest_long,
- zend_bool *is_null, bool allow_null)
+ bool *is_null, bool allow_null, uint32_t arg_num)
{
if (allow_null) {
*is_null = 0;
@@ -2196,7 +2208,7 @@ static zend_always_inline bool zend_parse_arg_str_or_long(zval *arg, zend_string
*dest_str = NULL;
*is_null = 1;
} else {
- return zend_parse_arg_str_or_long_slow(arg, dest_str, dest_long);
+ return zend_parse_arg_str_or_long_slow(arg, dest_str, dest_long, arg_num);
}
return 1;
}
@@ -2220,7 +2232,7 @@ static zend_always_inline bool zend_parse_arg_obj_or_class_name(
}
static zend_always_inline bool zend_parse_arg_obj_or_str(
- zval *arg, zend_object **destination_object, zend_class_entry *base_ce, zend_string **destination_string, bool allow_null
+ zval *arg, zend_object **destination_object, zend_class_entry *base_ce, zend_string **destination_string, bool allow_null, uint32_t arg_num
) {
if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
if (!base_ce || EXPECTED(instanceof_function(Z_OBJCE_P(arg), base_ce))) {
@@ -2231,7 +2243,7 @@ static zend_always_inline bool zend_parse_arg_obj_or_str(
}
*destination_object = NULL;
- return zend_parse_arg_str(arg, destination_string, allow_null);
+ return zend_parse_arg_str(arg, destination_string, allow_null, arg_num);
}
END_EXTERN_C()
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index 098dda104a..10e739c0ee 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -114,10 +114,11 @@ static size_t _real_page_size = ZEND_MM_PAGE_SIZE;
#ifndef __APPLE__
# define ZEND_MM_FD -1
#else
+# include <mach/vm_statistics.h>
/* Mac allows to track anonymous page via vmmap per TAG id.
* user land applications are allowed to take from 240 to 255.
*/
-# define ZEND_MM_FD (250<<24)
+# define ZEND_MM_FD VM_MAKE_TAG(250U)
#endif
#ifndef ZEND_MM_STAT
@@ -1510,7 +1511,7 @@ static zend_never_inline void *zend_mm_realloc_huge(zend_mm_heap *heap, void *pt
return zend_mm_realloc_slow(heap, ptr, size, MIN(old_size, copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}
-static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, zend_bool use_copy_size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
+static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, bool use_copy_size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
size_t page_offset;
size_t old_size;
@@ -2789,7 +2790,7 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
#if ZEND_MM_CUSTOM
tmp = getenv("USE_ZEND_ALLOC");
if (tmp && !zend_atoi(tmp, 0)) {
- zend_bool tracked = (tmp = getenv("USE_TRACKED_ALLOC")) && zend_atoi(tmp, 0);
+ bool tracked = (tmp = getenv("USE_TRACKED_ALLOC")) && zend_atoi(tmp, 0);
zend_mm_heap *mm_heap = alloc_globals->mm_heap = malloc(sizeof(zend_mm_heap));
memset(mm_heap, 0, sizeof(zend_mm_heap));
mm_heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_STD;
diff --git a/Zend/zend_arena.h b/Zend/zend_arena.h
index ff2d0a5fe6..a44082e52f 100644
--- a/Zend/zend_arena.h
+++ b/Zend/zend_arena.h
@@ -110,7 +110,7 @@ static zend_always_inline void zend_arena_release(zend_arena **arena_ptr, void *
arena->ptr = (char*)checkpoint;
}
-static zend_always_inline zend_bool zend_arena_contains(zend_arena *arena, void *ptr)
+static zend_always_inline bool zend_arena_contains(zend_arena *arena, void *ptr)
{
while (arena) {
if ((char*)ptr > (char*)arena && (char*)ptr <= arena->ptr) {
@@ -213,7 +213,7 @@ static zend_always_inline void zend_arena_release(zend_arena **arena_ptr, void *
}
}
-static zend_always_inline zend_bool zend_arena_contains(zend_arena *arena, void *ptr)
+static zend_always_inline bool zend_arena_contains(zend_arena *arena, void *ptr)
{
/* TODO: Dummy */
return 1;
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index cb61bec5d7..73f2390c11 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -423,7 +423,7 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki
}
#endif
-static inline zend_bool is_power_of_two(uint32_t n) {
+static inline bool is_power_of_two(uint32_t n) {
return ((n != 0) && (n == (n & (~n + 1))));
}
@@ -485,16 +485,15 @@ static zend_result zend_ast_add_unpacked_element(zval *result, zval *expr) {
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
if (key) {
- zend_throw_error(NULL, "Cannot unpack array with string keys");
- return FAILURE;
+ zend_hash_update(Z_ARRVAL_P(result), key, val);
} else {
if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), val)) {
zend_throw_error(NULL,
"Cannot add element to the array as the next element is already occupied");
return FAILURE;
}
- Z_TRY_ADDREF_P(val);
}
+ Z_TRY_ADDREF_P(val);
} ZEND_HASH_FOREACH_END();
return SUCCESS;
}
@@ -1375,7 +1374,7 @@ static ZEND_COLD void zend_ast_export_attribute_group(smart_str *str, zend_ast *
}
}
-static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) {
+static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, bool newlines) {
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
@@ -1497,7 +1496,7 @@ tail_call:
case ZEND_AST_METHOD:
decl = (zend_ast_decl *) ast;
if (decl->child[4]) {
- zend_bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC);
+ bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC);
zend_ast_export_attributes(str, decl->child[4], indent, newlines);
}
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index eb02e9bea0..4d7335853e 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -301,11 +301,11 @@ ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast);
typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr);
ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn);
-static zend_always_inline zend_bool zend_ast_is_special(zend_ast *ast) {
+static zend_always_inline bool zend_ast_is_special(zend_ast *ast) {
return (ast->kind >> ZEND_AST_SPECIAL_SHIFT) & 1;
}
-static zend_always_inline zend_bool zend_ast_is_list(zend_ast *ast) {
+static zend_always_inline bool zend_ast_is_list(zend_ast *ast) {
return (ast->kind >> ZEND_AST_IS_LIST_SHIFT) & 1;
}
static zend_always_inline zend_ast_list *zend_ast_get_list(zend_ast *ast) {
diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c
index f8d384301a..2823d3bafb 100644
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@ -163,7 +163,7 @@ ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
return smart_str_extract(&str);
}
-ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
+ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
{
zend_attribute *other;
@@ -263,15 +263,12 @@ ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcnam
void zend_register_attribute_ce(void)
{
zend_internal_attribute *attr;
- zend_class_entry ce;
- zend_string *str;
- zval tmp;
zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
- INIT_CLASS_ENTRY(ce, "Attribute", class_Attribute_methods);
- zend_ce_attribute = zend_register_internal_class(&ce);
- zend_ce_attribute->ce_flags |= ZEND_ACC_FINAL;
+ zend_ce_attribute = register_class_Attribute();
+ attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS);
+ attr->validator = validate_attribute;
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_CLASS"), ZEND_ATTRIBUTE_TARGET_CLASS);
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_FUNCTION"), ZEND_ATTRIBUTE_TARGET_FUNCTION);
@@ -281,14 +278,6 @@ void zend_register_attribute_ce(void)
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER);
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL);
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE);
-
- ZVAL_UNDEF(&tmp);
- str = zend_string_init(ZEND_STRL("flags"), 1);
- zend_declare_typed_property(zend_ce_attribute, str, &tmp, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0));
- zend_string_release(str);
-
- attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS);
- attr->validator = validate_attribute;
}
void zend_attributes_shutdown(void)
diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h
index d19b7e470d..fa21896447 100644
--- a/Zend/zend_attributes.h
+++ b/Zend/zend_attributes.h
@@ -72,7 +72,7 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes,
ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope);
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets);
-ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr);
+ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr);
ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags);
ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname);
diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php
index 90f1a171db..26defa8d4d 100644
--- a/Zend/zend_attributes.stub.php
+++ b/Zend/zend_attributes.stub.php
@@ -1,8 +1,10 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
final class Attribute
{
+ public int $flags;
+
public function __construct(int $flags = Attribute::TARGET_ALL) {}
}
diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h
index 1b0da2ccb8..a09f9161fd 100644
--- a/Zend/zend_attributes_arginfo.h
+++ b/Zend/zend_attributes_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 54eede8541597ec2ac5c04e31d14e2db7e8c5556 */
+ * Stub hash: 0183e750e66999862a7688ecb251017110d06d1f */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
@@ -13,3 +13,20 @@ static const zend_function_entry class_Attribute_methods[] = {
ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
+
+static zend_class_entry *register_class_Attribute(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Attribute", class_Attribute_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL;
+
+ zval property_flags_default_value;
+ ZVAL_UNDEF(&property_flags_default_value);
+ zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, 1);
+ zend_declare_typed_property(class_entry, property_flags_name, &property_flags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+ zend_string_release(property_flags_name);
+
+ return class_entry;
+}
diff --git a/Zend/zend_bitset.h b/Zend/zend_bitset.h
index b7c369c749..2bc0ca105d 100644
--- a/Zend/zend_bitset.h
+++ b/Zend/zend_bitset.h
@@ -122,7 +122,7 @@ static inline uint32_t zend_bitset_len(uint32_t n)
return (n + ((sizeof(zend_long) * 8) - 1)) / (sizeof(zend_long) * 8);
}
-static inline zend_bool zend_bitset_in(zend_bitset set, uint32_t n)
+static inline bool zend_bitset_in(zend_bitset set, uint32_t n)
{
return ZEND_BIT_TEST(set, n);
}
@@ -158,7 +158,7 @@ static inline void zend_bitset_fill(zend_bitset set, uint32_t len)
memset(set, 0xff, len * ZEND_BITSET_ELM_SIZE);
}
-static inline zend_bool zend_bitset_equal(zend_bitset set1, zend_bitset set2, uint32_t len)
+static inline bool zend_bitset_equal(zend_bitset set1, zend_bitset set2, uint32_t len)
{
return memcmp(set1, set2, len * ZEND_BITSET_ELM_SIZE) == 0;
}
@@ -213,7 +213,7 @@ static inline void zend_bitset_union_with_difference(zend_bitset set1, zend_bits
}
}
-static inline zend_bool zend_bitset_subset(zend_bitset set1, zend_bitset set2, uint32_t len)
+static inline bool zend_bitset_subset(zend_bitset set1, zend_bitset set2, uint32_t len)
{
uint32_t i;
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 5ec365c920..519bf02d88 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -32,10 +32,7 @@
/* }}} */
ZEND_MINIT_FUNCTION(core) { /* {{{ */
- zend_class_entry class_entry;
-
- INIT_CLASS_ENTRY(class_entry, "stdClass", NULL);
- zend_standard_class_def = zend_register_internal_class(&class_entry);
+ zend_standard_class_def = register_class_stdClass();
zend_register_default_classes();
@@ -370,7 +367,7 @@ ZEND_FUNCTION(strncasecmp)
ZEND_FUNCTION(error_reporting)
{
zend_long err;
- zend_bool err_is_null = 1;
+ bool err_is_null = 1;
int old_error_reporting;
ZEND_PARSE_PARAMETERS_START(0, 1)
@@ -426,7 +423,7 @@ static bool validate_constant_array_argument(HashTable *ht, int argument_number)
zval *val;
GC_PROTECT_RECURSION(ht);
- ZEND_HASH_FOREACH_VAL_IND(ht, val) {
+ ZEND_HASH_FOREACH_VAL(ht, val) {
ZVAL_DEREF(val);
if (Z_REFCOUNTED_P(val)) {
if (Z_TYPE_P(val) == IS_ARRAY) {
@@ -459,7 +456,7 @@ static void copy_constant_array(zval *dst, zval *src) /* {{{ */
zval *new_val, *val;
array_init_size(dst, zend_hash_num_elements(Z_ARRVAL_P(src)));
- ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(src), idx, key, val) {
+ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(src), idx, key, val) {
/* constant arrays can't contain references */
ZVAL_DEREF(val);
if (key) {
@@ -483,7 +480,7 @@ ZEND_FUNCTION(define)
{
zend_string *name;
zval *val, val_free;
- zend_bool non_cs = 0;
+ bool non_cs = 0;
zend_constant c;
ZEND_PARSE_PARAMETERS_START(2, 3)
@@ -631,14 +628,14 @@ ZEND_FUNCTION(get_parent_class)
}
/* }}} */
-static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) /* {{{ */
+static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ */
{
zval *obj;
zend_string *class_name;
zend_class_entry *instance_ce;
zend_class_entry *ce;
- zend_bool allow_string = only_subclass;
- zend_bool retval;
+ bool allow_string = only_subclass;
+ bool retval;
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ZVAL(obj)
@@ -703,6 +700,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
zend_property_info *prop_info;
zval *prop, prop_copy;
zend_string *key;
+ zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce);
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop_info) {
if (((prop_info->flags & ZEND_ACC_PROTECTED) &&
@@ -716,7 +714,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
prop = &ce->default_static_members_table[prop_info->offset];
ZVAL_DEINDIRECT(prop);
} else if (!statics && (prop_info->flags & ZEND_ACC_STATIC) == 0) {
- prop = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)];
+ prop = &default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)];
}
if (!prop) {
continue;
@@ -734,7 +732,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
/* this is necessary to make it able to work with default array
* properties, returned to user */
if (Z_OPT_TYPE_P(prop) == IS_CONSTANT_AST) {
- if (UNEXPECTED(zval_update_constant_ex(prop, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(prop, ce) != SUCCESS)) {
return;
}
}
@@ -769,7 +767,6 @@ ZEND_FUNCTION(get_class_vars)
/* {{{ Returns an array of object properties */
ZEND_FUNCTION(get_object_vars)
{
- zval *obj;
zval *value;
HashTable *properties;
zend_string *key;
@@ -777,10 +774,9 @@ ZEND_FUNCTION(get_object_vars)
zend_ulong num_key;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_OBJECT(obj)
+ Z_PARAM_OBJ(zobj)
ZEND_PARSE_PARAMETERS_END();
- zobj = Z_OBJ_P(obj);
properties = zobj->handlers->get_properties(zobj);
if (properties == NULL) {
RETURN_EMPTY_ARRAY();
@@ -796,7 +792,7 @@ ZEND_FUNCTION(get_object_vars)
array_init_size(return_value, zend_hash_num_elements(properties));
ZEND_HASH_FOREACH_KEY_VAL(properties, num_key, key, value) {
- zend_bool is_dynamic = 1;
+ bool is_dynamic = 1;
if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (UNEXPECTED(Z_ISUNDEF_P(value))) {
@@ -839,45 +835,27 @@ ZEND_FUNCTION(get_object_vars)
/* {{{ Returns an array of mangled object properties. Does not respect property visibility. */
ZEND_FUNCTION(get_mangled_object_vars)
{
- zval *obj;
+ zend_object *obj;
HashTable *properties;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_OBJECT(obj)
+ Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
- properties = Z_OBJ_HT_P(obj)->get_properties(Z_OBJ_P(obj));
+ properties = obj->handlers->get_properties(obj);
if (!properties) {
ZVAL_EMPTY_ARRAY(return_value);
return;
}
properties = zend_proptable_to_symtable(properties,
- (Z_OBJCE_P(obj)->default_properties_count ||
- Z_OBJ_P(obj)->handlers != &std_object_handlers ||
+ (obj->ce->default_properties_count ||
+ obj->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(properties)));
RETURN_ARR(properties);
}
/* }}} */
-static bool same_name(zend_string *key, zend_string *name) /* {{{ */
-{
- zend_string *lcname;
- bool ret;
-
- if (key == name) {
- return 1;
- }
- if (ZSTR_LEN(key) != ZSTR_LEN(name)) {
- return 0;
- }
- lcname = zend_string_tolower(name);
- ret = memcmp(ZSTR_VAL(lcname), ZSTR_VAL(key), ZSTR_LEN(key)) == 0;
- zend_string_release_ex(lcname, 0);
- return ret;
-}
-/* }}} */
-
/* {{{ Returns an array of method names for class or class instance. */
ZEND_FUNCTION(get_class_methods)
{
@@ -1009,7 +987,7 @@ static inline void class_exists_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, in
zend_string *name;
zend_string *lcname;
zend_class_entry *ce;
- zend_bool autoload = 1;
+ bool autoload = 1;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(name)
@@ -1065,7 +1043,7 @@ ZEND_FUNCTION(trait_exists)
ZEND_FUNCTION(function_exists)
{
zend_string *name;
- zend_bool exists;
+ bool exists;
zend_string *lcname;
ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -1091,23 +1069,25 @@ ZEND_FUNCTION(function_exists)
ZEND_FUNCTION(class_alias)
{
zend_string *class_name;
- char *alias_name;
+ zend_string *alias_name;
zend_class_entry *ce;
- size_t alias_name_len;
- zend_bool autoload = 1;
+ bool autoload = 1;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ss|b", &class_name, &alias_name, &alias_name_len, &autoload) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(2, 3)
+ Z_PARAM_STR(class_name)
+ Z_PARAM_STR(alias_name)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_BOOL(autoload)
+ ZEND_PARSE_PARAMETERS_END();
ce = zend_lookup_class_ex(class_name, NULL, !autoload ? ZEND_FETCH_CLASS_NO_AUTOLOAD : 0);
if (ce) {
if (ce->type == ZEND_USER_CLASS) {
- if (zend_register_class_alias_ex(alias_name, alias_name_len, ce, 0) == SUCCESS) {
+ if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, 0) == SUCCESS) {
RETURN_TRUE;
} else {
- zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), alias_name);
+ zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(alias_name));
RETURN_FALSE;
}
} else {
@@ -1174,10 +1154,11 @@ ZEND_FUNCTION(set_error_handler)
zend_fcall_info_cache fcc;
zend_long error_type = E_ALL;
- /* callable argument corresponds to the error handler */
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!|l", &fci, &fcc, &error_type) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 2)
+ Z_PARAM_FUNC_OR_NULL(fci, fcc)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(error_type)
+ ZEND_PARSE_PARAMETERS_END();
if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) {
ZVAL_COPY(return_value, &EG(user_error_handler));
@@ -1231,10 +1212,9 @@ ZEND_FUNCTION(set_exception_handler)
zend_fcall_info fci;
zend_fcall_info_cache fcc;
- /* callable argument corresponds to the exception handler */
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_FUNC_OR_NULL(fci, fcc)
+ ZEND_PARSE_PARAMETERS_END();
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
ZVAL_COPY(return_value, &EG(user_exception_handler));
@@ -1272,30 +1252,28 @@ ZEND_FUNCTION(restore_exception_handler)
}
/* }}} */
-static void copy_class_or_interface_name(zval *array, zend_string *key, zend_class_entry *ce) /* {{{ */
-{
- if ((ce->refcount == 1 && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) ||
- same_name(key, ce->name)) {
- key = ce->name;
- }
- add_next_index_str(array, zend_string_copy(key));
-}
-/* }}} */
-
static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, int skip_flags) /* {{{ */
{
zend_string *key;
+ zval *zv, tmp;
zend_class_entry *ce;
ZEND_PARSE_PARAMETERS_NONE();
array_init(return_value);
- ZEND_HASH_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) {
+ ce = Z_PTR_P(zv);
if (key
&& ZSTR_VAL(key)[0] != 0
&& (ce->ce_flags & flags)
&& !(ce->ce_flags & skip_flags)) {
- copy_class_or_interface_name(return_value, key, ce);
+ if (EXPECTED(Z_TYPE_P(zv) == IS_PTR)) {
+ ZVAL_STR_COPY(&tmp, ce->name);
+ } else {
+ ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
+ ZVAL_STR_COPY(&tmp, key);
+ }
+ zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
}
} ZEND_HASH_FOREACH_END();
}
@@ -1328,7 +1306,7 @@ ZEND_FUNCTION(get_defined_functions)
zval internal, user;
zend_string *key;
zend_function *func;
- zend_bool exclude_disabled = 1;
+ bool exclude_disabled = 1;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &exclude_disabled) == FAILURE) {
RETURN_THROWS();
@@ -1476,7 +1454,7 @@ static void add_zendext_info(zend_extension *ext, void *arg) /* {{{ */
/* {{{ Return an array containing names of loaded extensions */
ZEND_FUNCTION(get_loaded_extensions)
{
- zend_bool zendext = 0;
+ bool zendext = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &zendext) == FAILURE) {
RETURN_THROWS();
@@ -1499,7 +1477,7 @@ ZEND_FUNCTION(get_loaded_extensions)
/* {{{ Return an array containing the names and values of all defined constants */
ZEND_FUNCTION(get_defined_constants)
{
- zend_bool categorize = 0;
+ bool categorize = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &categorize) == FAILURE) {
RETURN_THROWS();
@@ -1668,7 +1646,7 @@ void debug_print_backtrace_args(zval *arg_array) /* {{{ */
}
/* }}} */
-static inline zend_bool skip_internal_handler(zend_execute_data *skip) /* {{{ */
+static inline bool skip_internal_handler(zend_execute_data *skip) /* {{{ */
{
return !(skip->func && ZEND_USER_CODE(skip->func->common.type))
&& skip->prev_execute_data
@@ -1780,7 +1758,7 @@ ZEND_FUNCTION(debug_print_backtrace)
}
} else {
/* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
- zend_bool build_filename_arg = 1;
+ bool build_filename_arg = 1;
uint32_t include_kind = 0;
if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) {
include_kind = ptr->opline->extended_value;
@@ -2011,7 +1989,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
} else {
/* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
- zend_bool build_filename_arg = 1;
+ bool build_filename_arg = 1;
zend_string *pseudo_function_name;
uint32_t include_kind = 0;
if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) {
diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php
index 187e789d23..87a530200d 100644
--- a/Zend/zend_builtin_functions.stub.php
+++ b/Zend/zend_builtin_functions.stub.php
@@ -1,6 +1,10 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
+
+class stdClass
+{
+}
function zend_version(): string {}
diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h
index e2d640d22f..6baaead27d 100644
--- a/Zend/zend_builtin_functions_arginfo.h
+++ b/Zend/zend_builtin_functions_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b09e9199a21595a3b6f6c02db81c8e22c36c277f */
+ * Stub hash: 429fc9b22054348101d0b9d6746494e52dc04edf */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_version, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -333,3 +333,18 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(gc_status, arginfo_gc_status)
ZEND_FE_END
};
+
+
+static const zend_function_entry class_stdClass_methods[] = {
+ ZEND_FE_END
+};
+
+static zend_class_entry *register_class_stdClass(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "stdClass", class_stdClass_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+ return class_entry;
+}
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index e7777c58cf..f1ffe78c69 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -69,11 +69,11 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
}
/* }}} */
-static zend_bool zend_valid_closure_binding(
+static bool zend_valid_closure_binding(
zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
{
zend_function *func = &closure->func;
- zend_bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
+ bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
if (newthis) {
if (func->common.fn_flags & ZEND_ACC_STATIC) {
zend_error(E_WARNING, "Cannot bind an instance to a static closure");
@@ -128,6 +128,7 @@ ZEND_METHOD(Closure, call)
zend_fcall_info_cache fci_cache;
zend_function my_function;
zend_object *newobj;
+ zend_class_entry *newclass;
fci.param_count = 0;
fci.params = NULL;
@@ -140,26 +141,27 @@ ZEND_METHOD(Closure, call)
closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
newobj = Z_OBJ_P(newthis);
+ newclass = newobj->ce;
- if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
+ if (!zend_valid_closure_binding(closure, newthis, newclass)) {
return;
}
if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
zval new_closure;
- zend_create_closure(&new_closure, &closure->func, Z_OBJCE_P(newthis), closure->called_scope, newthis);
+ zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
closure = (zend_closure *) Z_OBJ(new_closure);
fci_cache.function_handler = &closure->func;
} else {
memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
/* use scope of passed object */
- my_function.common.scope = Z_OBJCE_P(newthis);
+ my_function.common.scope = newclass;
fci_cache.function_handler = &my_function;
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
if (ZEND_USER_CODE(my_function.type)
- && (closure->func.common.scope != Z_OBJCE_P(newthis)
+ && (closure->func.common.scope != newclass
|| (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
void *ptr;
@@ -172,7 +174,7 @@ ZEND_METHOD(Closure, call)
}
}
- fci_cache.called_scope = newobj->ce;
+ fci_cache.called_scope = newclass;
fci_cache.object = fci.object = newobj;
fci.size = sizeof(fci);
@@ -354,9 +356,9 @@ ZEND_METHOD(Closure, fromCallable)
zval *callable;
char *error = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(callable)
+ ZEND_PARSE_PARAMETERS_END();
if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
/* It's already a closure */
@@ -484,6 +486,10 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
zend_object_std_dtor(&closure->std);
if (closure->func.type == ZEND_USER_FUNCTION) {
+ /* We don't own the static variables of fake closures. */
+ if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
+ zend_destroy_static_vars(&closure->func.op_array);
+ }
destroy_op_array(&closure->func.op_array);
}
@@ -518,7 +524,7 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
}
/* }}} */
-int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */
+int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
{
zend_closure *closure = (zend_closure *)obj;
*fptr_ptr = &closure->func;
@@ -541,7 +547,7 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zval val;
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
HashTable *debug_info;
- zend_bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
+ bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
*is_temp = 1;
@@ -613,7 +619,9 @@ static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /*
*table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
*n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
- return (closure->func.type == ZEND_USER_FUNCTION) ?
+ /* Fake closures don't own the static variables they reference. */
+ return (closure->func.type == ZEND_USER_FUNCTION
+ && !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ?
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
}
/* }}} */
@@ -627,11 +635,7 @@ ZEND_COLD ZEND_METHOD(Closure, __construct)
void zend_register_closure_ce(void) /* {{{ */
{
- zend_class_entry ce;
-
- INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods);
- zend_ce_closure = zend_register_internal_class(&ce);
- zend_ce_closure->ce_flags |= ZEND_ACC_FINAL;
+ zend_ce_closure = register_class_Closure();
zend_ce_closure->create_object = zend_closure_new;
zend_ce_closure->serialize = zend_class_serialize_deny;
zend_ce_closure->unserialize = zend_class_unserialize_deny;
@@ -662,7 +666,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
}
/* }}} */
-ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
+static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */
{
zend_closure *closure;
@@ -681,12 +685,15 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
- if (closure->func.op_array.static_variables) {
- closure->func.op_array.static_variables =
- zend_array_dup(closure->func.op_array.static_variables);
+ /* For fake closures, we want to reuse the static variables of the original function. */
+ if (!is_fake) {
+ if (closure->func.op_array.static_variables) {
+ closure->func.op_array.static_variables =
+ zend_array_dup(closure->func.op_array.static_variables);
+ }
+ ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
+ &closure->func.op_array.static_variables);
}
- ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
- &closure->func.op_array.static_variables);
/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache)
@@ -756,11 +763,16 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
}
/* }}} */
+ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
+{
+ zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ false);
+}
+
ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
zend_closure *closure;
- zend_create_closure(res, func, scope, called_scope, this_ptr);
+ zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true);
closure = (zend_closure *)Z_OBJ_P(res);
closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
diff --git a/Zend/zend_closures.stub.php b/Zend/zend_closures.stub.php
index 906dedc5cf..4bd93e241f 100644
--- a/Zend/zend_closures.stub.php
+++ b/Zend/zend_closures.stub.php
@@ -1,6 +1,6 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
final class Closure
{
diff --git a/Zend/zend_closures_arginfo.h b/Zend/zend_closures_arginfo.h
index 1ccde0d6dd..56bd16ffb6 100644
--- a/Zend/zend_closures_arginfo.h
+++ b/Zend/zend_closures_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0a2dd53716d30893aa5dd92a9907b2298abb3f70 */
+ * Stub hash: 62da9b1e75331f30a0c63e82c9fd366e26b5724d */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Closure___construct, 0, 0, 0)
ZEND_END_ARG_INFO()
@@ -40,3 +40,14 @@ static const zend_function_entry class_Closure_methods[] = {
ZEND_ME(Closure, fromCallable, arginfo_class_Closure_fromCallable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
};
+
+static zend_class_entry *register_class_Closure(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL;
+
+ return class_entry;
+}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 19a1c543ab..deff0dbe57 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -87,7 +87,7 @@ ZEND_API zend_executor_globals executor_globals;
#endif
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2);
-static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast);
+static bool zend_try_ct_eval_array(zval *result, zend_ast *ast);
static void init_op(zend_op *op)
{
@@ -145,7 +145,7 @@ static zend_string *zend_build_runtime_definition_key(zend_string *name, uint32_
}
/* }}} */
-static zend_bool zend_get_unqualified_name(const zend_string *name, const char **result, size_t *result_len) /* {{{ */
+static bool zend_get_unqualified_name(const zend_string *name, const char **result, size_t *result_len) /* {{{ */
{
const char *ns_separator = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
if (ns_separator != NULL) {
@@ -180,7 +180,7 @@ static const struct reserved_class_name reserved_class_names[] = {
{NULL, 0}
};
-static zend_bool zend_is_reserved_class_name(const zend_string *name) /* {{{ */
+static bool zend_is_reserved_class_name(const zend_string *name) /* {{{ */
{
const struct reserved_class_name *reserved = reserved_class_names;
@@ -259,7 +259,7 @@ static zend_always_inline zend_uchar zend_lookup_builtin_type_by_name(const zend
}
/* }}} */
-static zend_always_inline zend_bool zend_is_confusable_type(const zend_string *name, const char **correct_name) /* {{{ */
+static zend_always_inline bool zend_is_confusable_type(const zend_string *name, const char **correct_name) /* {{{ */
{
const confusable_type_info *info = confusable_types;
@@ -278,7 +278,7 @@ static zend_always_inline zend_bool zend_is_confusable_type(const zend_string *n
}
/* }}} */
-static zend_bool zend_is_not_imported(zend_string *name) {
+static bool zend_is_not_imported(zend_string *name) {
/* Assuming "name" is unqualified here. */
return !FC(imports) || zend_hash_find_ptr_lc(FC(imports), name) == NULL;
}
@@ -393,7 +393,7 @@ static void zend_register_seen_symbol(zend_string *name, uint32_t kind) {
}
}
-static zend_bool zend_have_seen_symbol(zend_string *name, uint32_t kind) {
+static bool zend_have_seen_symbol(zend_string *name, uint32_t kind) {
zval *zv = zend_hash_find(&FC(seen_symbols), name);
return zv && (Z_LVAL_P(zv) & kind) != 0;
}
@@ -417,6 +417,8 @@ void init_compiler(void) /* {{{ */
CG(delayed_variance_obligations) = NULL;
CG(delayed_autoloads) = NULL;
+ CG(unlinked_uses) = NULL;
+ CG(current_linking_class) = NULL;
}
/* }}} */
@@ -428,7 +430,6 @@ void shutdown_compiler(void) /* {{{ */
zend_stack_destroy(&CG(loop_var_stack));
zend_stack_destroy(&CG(delayed_oplines_stack));
zend_stack_destroy(&CG(short_circuiting_opnums));
- zend_arena_destroy(CG(arena));
if (CG(delayed_variance_obligations)) {
zend_hash_destroy(CG(delayed_variance_obligations));
@@ -440,6 +441,12 @@ void shutdown_compiler(void) /* {{{ */
FREE_HASHTABLE(CG(delayed_autoloads));
CG(delayed_autoloads) = NULL;
}
+ if (CG(unlinked_uses)) {
+ zend_hash_destroy(CG(unlinked_uses));
+ FREE_HASHTABLE(CG(unlinked_uses));
+ CG(unlinked_uses) = NULL;
+ }
+ CG(current_linking_class) = NULL;
}
/* }}} */
@@ -472,7 +479,7 @@ ZEND_API int zend_get_compiled_lineno(void) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_is_compiling(void) /* {{{ */
+ZEND_API bool zend_is_compiling(void) /* {{{ */
{
return CG(in_compilation);
}
@@ -609,7 +616,7 @@ static int zend_add_class_name_literal(zend_string *name) /* {{{ */
}
/* }}} */
-static int zend_add_const_name_literal(zend_string *name, zend_bool unqualified) /* {{{ */
+static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {{{ */
{
zend_string *tmp_name;
@@ -658,7 +665,7 @@ void zend_stop_lexing(void)
}
static inline void zend_begin_loop(
- zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */
+ zend_uchar free_opcode, const znode *loop_var, bool is_switch) /* {{{ */
{
zend_brk_cont_element *brk_cont_element;
int parent = CG(context).current_brk_cont;
@@ -860,8 +867,8 @@ zend_string *zend_prefix_with_ns(zend_string *name) {
}
zend_string *zend_resolve_non_class_name(
- zend_string *name, uint32_t type, zend_bool *is_fully_qualified,
- zend_bool case_sensitive, HashTable *current_import_sub
+ zend_string *name, uint32_t type, bool *is_fully_qualified,
+ bool case_sensitive, HashTable *current_import_sub
) {
char *compound;
*is_fully_qualified = 0;
@@ -917,14 +924,14 @@ zend_string *zend_resolve_non_class_name(
}
/* }}} */
-zend_string *zend_resolve_function_name(zend_string *name, uint32_t type, zend_bool *is_fully_qualified) /* {{{ */
+zend_string *zend_resolve_function_name(zend_string *name, uint32_t type, bool *is_fully_qualified) /* {{{ */
{
return zend_resolve_non_class_name(
name, type, is_fully_qualified, 0, FC(imports_function));
}
/* }}} */
-zend_string *zend_resolve_const_name(zend_string *name, uint32_t type, zend_bool *is_fully_qualified) /* {{{ */ {
+zend_string *zend_resolve_const_name(zend_string *name, uint32_t type, bool *is_fully_qualified) /* {{{ */ {
return zend_resolve_non_class_name(
name, type, is_fully_qualified, 1, FC(imports_const));
}
@@ -934,22 +941,35 @@ zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* {{{ */
{
char *compound;
+ if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) {
+ if (type == ZEND_NAME_FQ) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "'\\%s' is an invalid class name", ZSTR_VAL(name));
+ }
+ if (type == ZEND_NAME_RELATIVE) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "'namespace\\%s' is an invalid class name", ZSTR_VAL(name));
+ }
+ ZEND_ASSERT(type == ZEND_NAME_NOT_FQ);
+ return zend_string_copy(name);
+ }
+
if (type == ZEND_NAME_RELATIVE) {
return zend_prefix_with_ns(name);
}
- if (type == ZEND_NAME_FQ || ZSTR_VAL(name)[0] == '\\') {
- /* Remove \ prefix (only relevant if this is a string rather than a label) */
+ if (type == ZEND_NAME_FQ) {
if (ZSTR_VAL(name)[0] == '\\') {
+ /* Remove \ prefix (only relevant if this is a string rather than a label) */
name = zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0);
- } else {
- zend_string_addref(name);
- }
- /* Ensure that \self, \parent and \static are not used */
- if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) {
- zend_error_noreturn(E_COMPILE_ERROR, "'\\%s' is an invalid class name", ZSTR_VAL(name));
+ if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "'\\%s' is an invalid class name", ZSTR_VAL(name));
+ }
+ return name;
}
- return name;
+
+ return zend_string_copy(name);
}
if (FC(imports)) {
@@ -1001,7 +1021,7 @@ static void str_dtor(zval *zv) /* {{{ */ {
}
/* }}} */
-static zend_bool zend_is_call(zend_ast *ast);
+static bool zend_is_call(zend_ast *ast);
static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
{
@@ -1022,28 +1042,27 @@ static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
}
/* }}} */
+void zend_init_static_variables_map_ptr(zend_op_array *op_array)
+{
+ if (op_array->static_variables) {
+ ZEND_MAP_PTR_INIT(op_array->static_variables_ptr,
+ zend_arena_alloc(&CG(arena), sizeof(HashTable *)));
+ ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
+ }
+}
+
ZEND_API void function_add_ref(zend_function *function) /* {{{ */
{
if (function->type == ZEND_USER_FUNCTION) {
zend_op_array *op_array = &function->op_array;
-
if (op_array->refcount) {
(*op_array->refcount)++;
}
- if (op_array->static_variables
- && !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
- GC_ADDREF(op_array->static_variables);
- }
- if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
- ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_PRELOADED);
- ZEND_MAP_PTR_NEW(op_array->run_time_cache);
- 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_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
- ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
- }
+ ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void *)));
+ ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
+
+ zend_init_static_variables_map_ptr(op_array);
}
if (function->common.function_name) {
@@ -1052,7 +1071,7 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
}
/* }}} */
-static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, zend_bool compile_time) /* {{{ */
+static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, bool compile_time) /* {{{ */
{
zval *zv = zend_hash_find_ex(compile_time ? CG(function_table) : EG(function_table), lcname, 1);
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
@@ -1072,27 +1091,19 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen
}
}
-ZEND_API zend_result do_bind_function(zval *lcname) /* {{{ */
+ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{ */
{
- zend_function *function;
- zval *rtd_key, *zv;
-
- rtd_key = lcname + 1;
- zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1);
- if (UNEXPECTED(!zv)) {
- do_bind_function_error(Z_STR_P(lcname), NULL, 0);
+ zend_function *added_func = zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), func);
+ if (UNEXPECTED(!added_func)) {
+ do_bind_function_error(Z_STR_P(lcname), &func->op_array, 0);
return FAILURE;
}
- function = (zend_function*)Z_PTR_P(zv);
- if (UNEXPECTED(function->common.fn_flags & ZEND_ACC_PRELOADED)
- && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD)) {
- zv = zend_hash_add(EG(function_table), Z_STR_P(lcname), zv);
- } else {
- zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname));
+
+ if (func->op_array.refcount) {
+ ++*func->op_array.refcount;
}
- if (UNEXPECTED(!zv)) {
- do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0);
- return FAILURE;
+ if (func->common.function_name) {
+ zend_string_addref(func->common.function_name);
}
return SUCCESS;
}
@@ -1136,7 +1147,12 @@ ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name) /*
return FAILURE;
}
- if (zend_do_link_class(ce, lc_parent_name) == FAILURE) {
+ if (ce->ce_flags & ZEND_ACC_LINKED) {
+ return SUCCESS;
+ }
+
+ ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
+ if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(rtd_key));
@@ -1187,13 +1203,34 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
if (ZEND_TYPE_HAS_CE(*list_type)) {
str = add_type_string(str, ZEND_TYPE_CE(*list_type)->name);
} else {
- zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope);
- str = add_type_string(str, resolved);
- zend_string_release(resolved);
+ if (ZEND_TYPE_HAS_CE_CACHE(*list_type)
+ && ZEND_TYPE_CE_CACHE(*list_type)) {
+ zend_class_entry *ce = ZEND_TYPE_CE_CACHE(*list_type);
+ if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
+ zend_string *tmp = zend_string_init(ZSTR_VAL(ce->name), strlen(ZSTR_VAL(ce->name)), 0);
+ str = add_type_string(str, tmp);
+ } else {
+ str = add_type_string(str, ce->name);
+ }
+ } else {
+ zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope);
+ str = add_type_string(str, resolved);
+ zend_string_release(resolved);
+ }
}
} ZEND_TYPE_LIST_FOREACH_END();
} else if (ZEND_TYPE_HAS_NAME(type)) {
- str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
+ if (ZEND_TYPE_HAS_CE_CACHE(type)
+ && ZEND_TYPE_CE_CACHE(type)) {
+ zend_class_entry *ce = ZEND_TYPE_CE_CACHE(type);
+ if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
+ str = zend_string_init(ZSTR_VAL(ce->name), strlen(ZSTR_VAL(ce->name)), 0);
+ } else {
+ str = zend_string_copy(ce->name);
+ }
+ } else {
+ str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
+ }
} else if (ZEND_TYPE_HAS_CE(type)) {
str = zend_string_copy(ZEND_TYPE_CE(type)->name);
}
@@ -1246,7 +1283,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
}
if (type_mask & MAY_BE_NULL) {
- zend_bool is_union = !str || memchr(ZSTR_VAL(str), '|', ZSTR_LEN(str)) != NULL;
+ bool is_union = !str || memchr(ZSTR_VAL(str), '|', ZSTR_LEN(str)) != NULL;
if (!is_union) {
zend_string *nullable_str = zend_string_concat2("?", 1, ZSTR_VAL(str), ZSTR_LEN(str));
zend_string_release(str);
@@ -1262,7 +1299,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type) {
return zend_type_to_string_resolved(type, NULL);
}
-static zend_bool is_generator_compatible_class_type(zend_string *name) {
+static bool is_generator_compatible_class_type(zend_string *name) {
return zend_string_equals_literal_ci(name, "Traversable")
|| zend_string_equals_literal_ci(name, "Iterator")
|| zend_string_equals_literal_ci(name, "Generator");
@@ -1277,7 +1314,7 @@ static void zend_mark_function_as_generator() /* {{{ */
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_type return_type = CG(active_op_array)->arg_info[-1].type;
- zend_bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & (MAY_BE_ITERABLE | MAY_BE_OBJECT)) != 0;
+ bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & (MAY_BE_ITERABLE | MAY_BE_OBJECT)) != 0;
if (!valid_type) {
zend_type *single_type;
ZEND_TYPE_FOREACH(return_type, single_type) {
@@ -1326,7 +1363,7 @@ ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_
ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline) /* {{{ */
{
if (first_early_binding_opline != (uint32_t)-1) {
- zend_bool orig_in_compilation = CG(in_compilation);
+ bool orig_in_compilation = CG(in_compilation);
uint32_t opline_num = first_early_binding_opline;
void **run_time_cache;
@@ -1354,7 +1391,8 @@ ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t fi
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), lc_parent_name, 1);
if (parent_ce) {
- if (zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv)) {
+ ce = zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv);
+ if (ce) {
/* Store in run-time cache */
((void**)((char*)run_time_cache + opline->extended_value))[0] = ce;
}
@@ -1433,7 +1471,7 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con
}
/* }}} */
-static zend_bool can_ct_eval_const(zend_constant *c) {
+static bool can_ct_eval_const(zend_constant *c) {
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
return 0;
}
@@ -1450,7 +1488,7 @@ static zend_bool can_ct_eval_const(zend_constant *c) {
return 0;
}
-static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool is_fully_qualified) /* {{{ */
+static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qualified) /* {{{ */
{
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c && can_ct_eval_const(c)) {
@@ -1477,8 +1515,13 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i
}
/* }}} */
-static inline zend_bool zend_is_scope_known() /* {{{ */
+static inline bool zend_is_scope_known() /* {{{ */
{
+ if (!CG(active_op_array)) {
+ /* This can only happen when evaluating a default value string. */
+ return 0;
+ }
+
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
/* Closures can be rebound to a different scope */
return 0;
@@ -1495,7 +1538,7 @@ static inline zend_bool zend_is_scope_known() /* {{{ */
}
/* }}} */
-static inline zend_bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */
+static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */
{
if (!CG(active_class_entry)) {
return 0;
@@ -1560,7 +1603,7 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
}
/* }}} */
-static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */
+static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */
{
uint32_t fetch_type;
zval *class_name;
@@ -1603,7 +1646,7 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
/* }}} */
/* We don't use zend_verify_const_access because we need to deal with unlinked classes. */
-static zend_bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope)
+static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope)
{
if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PUBLIC) {
return 1;
@@ -1632,7 +1675,7 @@ static zend_bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_
}
}
-static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */
+static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */
{
uint32_t fetch_type = zend_get_class_fetch_type(class_name);
zend_class_constant *cc;
@@ -1733,7 +1776,7 @@ void zend_do_extended_fcall_end(void) /* {{{ */
}
/* }}} */
-zend_bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ {
+bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ {
zend_auto_global *auto_global;
if ((auto_global = zend_hash_str_find_ptr(CG(auto_globals), name, len)) != NULL) {
@@ -1746,7 +1789,7 @@ zend_bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ {
}
/* }}} */
-zend_bool zend_is_auto_global(zend_string *name) /* {{{ */
+bool zend_is_auto_global(zend_string *name) /* {{{ */
{
zend_auto_global *auto_global;
@@ -1760,7 +1803,7 @@ zend_bool zend_is_auto_global(zend_string *name) /* {{{ */
}
/* }}} */
-zend_result zend_register_auto_global(zend_string *name, zend_bool jit, zend_auto_global_callback auto_global_callback) /* {{{ */
+zend_result zend_register_auto_global(zend_string *name, bool jit, zend_auto_global_callback auto_global_callback) /* {{{ */
{
zend_auto_global auto_global;
zend_result retval;
@@ -1808,9 +1851,9 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem) /* {{{ */
}
/* }}} */
-ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */
+ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers) /* {{{ */
{
- zend_bool persistent_hashes = ce->type == ZEND_INTERNAL_CLASS;
+ bool persistent_hashes = ce->type == ZEND_INTERNAL_CLASS;
ce->refcount = 1;
ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED;
@@ -1831,6 +1874,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
ce->info.user.doc_comment = NULL;
}
+ ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
ce->default_properties_count = 0;
ce->default_static_members_count = 0;
@@ -2243,7 +2287,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
}
/* }}} */
-static zend_bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind)
+static bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind)
{
switch (ast_kind) {
case ZEND_AST_DIM:
@@ -2259,7 +2303,7 @@ static zend_bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind)
}
}
-static zend_bool zend_ast_is_short_circuited(const zend_ast *ast)
+static bool zend_ast_is_short_circuited(const zend_ast *ast)
{
switch (ast->kind) {
case ZEND_AST_DIM:
@@ -2295,7 +2339,7 @@ static uint32_t zend_short_circuiting_checkpoint()
static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zend_ast *ast)
{
- zend_bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind)
+ bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind)
|| ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY;
if (!is_short_circuited) {
ZEND_ASSERT(zend_stack_count(&CG(short_circuiting_opnums)) == checkpoint
@@ -2382,7 +2426,7 @@ static size_t zend_type_get_num_classes(zend_type type) {
}
static void zend_emit_return_type_check(
- znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
+ znode *expr, zend_arg_info *return_info, bool implicit) /* {{{ */
{
zend_type type = return_info->type;
if (ZEND_TYPE_IS_SET(type)) {
@@ -2439,7 +2483,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */
{
znode zn;
zend_op *ret;
- zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
+ bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
if ((CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)
&& !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) {
@@ -2458,7 +2502,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */
}
/* }}} */
-static inline zend_bool zend_is_variable(zend_ast *ast) /* {{{ */
+static inline bool zend_is_variable(zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_VAR
|| ast->kind == ZEND_AST_DIM
@@ -2468,7 +2512,7 @@ static inline zend_bool zend_is_variable(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline zend_bool zend_is_call(zend_ast *ast) /* {{{ */
+static inline bool zend_is_call(zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_CALL
|| ast->kind == ZEND_AST_METHOD_CALL
@@ -2477,13 +2521,13 @@ static inline zend_bool zend_is_call(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline zend_bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */
+static inline bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */
{
return zend_is_variable(ast) || zend_is_call(ast);
}
/* }}} */
-static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */
+static inline bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL
|| ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_GROUP
@@ -2491,7 +2535,7 @@ static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
+static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
{
while (
ast->kind == ZEND_AST_DIM
@@ -2504,7 +2548,7 @@ static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */
+static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */
{
if (name_ast->kind != ZEND_AST_ZVAL) {
return 0;
@@ -2674,7 +2718,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
}
/* }}} */
-static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
+static bool is_this_fetch(zend_ast *ast) /* {{{ */
{
if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
zval *name = zend_ast_get_zval(ast->child[0]);
@@ -2685,7 +2729,22 @@ static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
}
/* }}} */
-static zend_bool this_guaranteed_exists() /* {{{ */
+static bool is_globals_fetch(const zend_ast *ast)
+{
+ if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
+ zval *name = zend_ast_get_zval(ast->child[0]);
+ return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "GLOBALS");
+ }
+
+ return 0;
+}
+
+static bool is_global_var_fetch(zend_ast *ast)
+{
+ return ast->kind == ZEND_AST_DIM && is_globals_fetch(ast->child[0]);
+}
+
+static bool this_guaranteed_exists() /* {{{ */
{
zend_op_array *op_array = CG(active_op_array);
/* Instance methods always have a $this.
@@ -2705,6 +2764,13 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
}
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
return opline;
+ } else if (is_globals_fetch(ast)) {
+ zend_op *opline = zend_emit_op(result, ZEND_FETCH_GLOBALS, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
+ return opline;
} else if (zend_try_compile_cv(result, ast) == FAILURE) {
return zend_compile_simple_var_no_cv(result, ast, type, delayed);
}
@@ -2726,7 +2792,7 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
}
/* }}} */
-zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref);
+zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref);
void zend_compile_assign(znode *result, zend_ast *ast);
static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
@@ -2750,10 +2816,26 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
znode var_node, dim_node;
- zend_short_circuiting_mark_inner(var_ast);
- opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
- if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
- opline->extended_value |= ZEND_FETCH_DIM_WRITE;
+ if (is_globals_fetch(var_ast)) {
+ if (dim_ast == NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot append to $GLOBALS");
+ }
+
+ zend_compile_expr(&dim_node, dim_ast);
+ if (dim_node.op_type == IS_CONST) {
+ convert_to_string(&dim_node.u.constant);
+ }
+
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &dim_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL;
+ zend_adjust_for_fetch_type(opline, result, type);
+ return opline;
+ } else {
+ zend_short_circuiting_mark_inner(var_ast);
+ opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
+ if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
+ opline->extended_value |= ZEND_FETCH_DIM_WRITE;
+ }
}
zend_separate_if_call_and_write(&var_node, var_ast, type);
@@ -2795,7 +2877,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
znode obj_node, prop_node;
zend_op *opline;
- zend_bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_PROP;
+ bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_PROP;
if (is_this_fetch(obj_ast)) {
if (this_guaranteed_exists()) {
@@ -2812,7 +2894,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
if (nullsafe) {
- /* We will push to the short_cirtcuiting_opnums stack in zend_delayed_compile_end(). */
+ /* We will push to the short_circuiting_opnums stack in zend_delayed_compile_end(). */
opline = zend_delayed_emit_op(NULL, ZEND_JMP_NULL, &obj_node, NULL);
if (opline->op1_type == IS_CONST) {
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
@@ -2904,9 +2986,9 @@ static void zend_verify_list_assign_target(zend_ast *var_ast, zend_ast_attr arra
static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node);
/* Propagate refs used on leaf elements to the surrounding list() structures. */
-static zend_bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */
+static bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */
zend_ast_list *list = zend_ast_get_list(ast);
- zend_bool has_refs = 0;
+ bool has_refs = 0;
uint32_t i;
for (i = 0; i < list->children; ++i) {
@@ -2925,14 +3007,23 @@ static zend_bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */
}
/* }}} */
+static bool list_is_keyed(zend_ast_list *list)
+{
+ for (uint32_t i = 0; i < list->children; i++) {
+ if (list->child[i]) {
+ return list->child[i]->child[1] != NULL;
+ }
+ }
+ return false;
+}
+
static void zend_compile_list_assign(
znode *result, zend_ast *ast, znode *expr_node, zend_ast_attr array_style) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
- zend_bool has_elems = 0;
- zend_bool is_keyed =
- list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL;
+ bool has_elems = 0;
+ bool is_keyed = list_is_keyed(list);
if (list->children && expr_node->op_type == IS_CONST && Z_TYPE(expr_node->u.constant) == IS_STRING) {
zval_make_interned_string(&expr_node->u.constant);
@@ -3031,11 +3122,15 @@ static void zend_ensure_writable_variable(const zend_ast *ast) /* {{{ */
if (zend_ast_is_short_circuited(ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Can't use nullsafe operator in write context");
}
+ if (is_globals_fetch(ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "$GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax");
+ }
}
/* }}} */
/* Detects $a... = $a pattern */
-zend_bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ */
+bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ */
{
if (expr_ast->kind != ZEND_AST_VAR || expr_ast->child[0]->kind != ZEND_AST_ZVAL) {
return 0;
@@ -3052,7 +3147,7 @@ zend_bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ *
{
zend_string *name1 = zval_get_string(zend_ast_get_zval(var_ast->child[0]));
zend_string *name2 = zval_get_string(zend_ast_get_zval(expr_ast->child[0]));
- zend_bool result = zend_string_equals(name1, name2);
+ bool result = zend_string_equals(name1, name2);
zend_string_release_ex(name1, 0);
zend_string_release_ex(name2, 0);
return result;
@@ -3074,7 +3169,9 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
- switch (var_ast->kind) {
+ /* Treat $GLOBALS['x'] assignment like assignment to variable. */
+ zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind;
+ switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0);
@@ -3182,6 +3279,9 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
if (zend_ast_is_short_circuited(source_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain");
}
+ if (is_globals_fetch(source_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot acquire reference to $GLOBALS");
+ }
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1);
@@ -3251,7 +3351,9 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
- switch (var_ast->kind) {
+ /* Treat $GLOBALS['x'] assignment like assignment to variable. */
+ zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind;
+ switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
@@ -3337,14 +3439,14 @@ uint32_t zend_compile_args(
{
zend_ast_list *args = zend_ast_get_list(ast);
uint32_t i;
- zend_bool uses_arg_unpack = 0;
+ bool uses_arg_unpack = 0;
uint32_t arg_count = 0; /* number of arguments not including unpacks */
/* Whether named arguments are used syntactically, to enforce language level limitations.
* May not actually use named argument passing. */
- zend_bool uses_named_args = 0;
+ bool uses_named_args = 0;
/* Whether there may be any undef arguments due to the use of named arguments. */
- zend_bool may_have_undef = 0;
+ bool may_have_undef = 0;
/* Whether there may be any extra named arguments collected into a variadic. */
*may_have_extra_named_args = 0;
@@ -3421,7 +3523,9 @@ uint32_t zend_compile_args(
arg_count++;
}
- if (zend_is_call(arg)) {
+ /* Treat passing of $GLOBALS the same as passing a call.
+ * This will error at runtime if the argument is by-ref. */
+ if (zend_is_call(arg) || is_globals_fetch(arg)) {
zend_compile_var(&arg_node, arg, BP_VAR_R, 0);
if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) {
/* Function call was converted into builtin instruction */
@@ -3586,10 +3690,10 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
}
/* }}} */
-zend_bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /* {{{ */
+bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /* {{{ */
{
zend_string *orig_name = zend_ast_get_str(name_ast);
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
name_node->op_type = IS_CONST;
ZVAL_STR(&name_node->u.constant, zend_resolve_function_name(
@@ -3646,7 +3750,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
}
/* }}} */
-static inline zend_bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */
+static inline bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */
{
uint32_t i;
for (i = 0; i < args->children; ++i) {
@@ -3802,7 +3906,7 @@ zend_result zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */
/* We can only calculate the stack size for functions that have been fully compiled, otherwise
* additional CV or TMP slots may still be added. This prevents the use of INIT_FCALL for
* directly or indirectly recursive function calls. */
-static zend_bool fbc_is_finalized(zend_function *fbc) {
+static bool fbc_is_finalized(zend_function *fbc) {
return !ZEND_USER_CODE(fbc->type) || (fbc->common.fn_flags & ZEND_ACC_DONE_PASS_TWO);
}
@@ -3874,7 +3978,7 @@ zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_stri
&& args->child[1]->child[1]->kind == ZEND_AST_ARG_LIST) {
zend_string *orig_name = zend_ast_get_str(args->child[1]->child[0]);
zend_ast_list *list = zend_ast_get_list(args->child[1]->child[1]);
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zend_string *name = zend_resolve_function_name(orig_name, args->child[1]->child[0]->attr, &is_fully_qualified);
if (zend_string_equals_literal_ci(name, "array_slice")
@@ -3988,7 +4092,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string
static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args) /* {{{ */
{
- zend_bool strict = 0;
+ bool strict = 0;
znode array, needly;
zend_op *opline;
@@ -3998,7 +4102,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
} else if (args->child[2]->kind == ZEND_AST_CONST) {
zval value;
zend_ast *name_ast = args->child[2]->child[0];
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zend_string *resolved_name = zend_resolve_const_name(
zend_ast_get_str(name_ast), name_ast->attr, &is_fully_qualified);
@@ -4023,7 +4127,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
}
if (zend_hash_num_elements(Z_ARRVAL(array.u.constant)) > 0) {
- zend_bool ok = 1;
+ bool ok = 1;
zval *val, tmp;
HashTable *src = Z_ARRVAL(array.u.constant);
HashTable *dst = zend_new_array(zend_hash_num_elements(src));
@@ -4180,7 +4284,7 @@ zend_result zend_compile_func_array_slice(znode *result, zend_ast_list *args) /*
&& args->child[1]->kind == ZEND_AST_ZVAL) {
zend_string *orig_name = zend_ast_get_str(args->child[0]->child[0]);
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zend_string *name = zend_resolve_function_name(orig_name, args->child[0]->child[0]->attr, &is_fully_qualified);
zend_ast_list *list = zend_ast_get_list(args->child[0]->child[1]);
zval *zv = zend_ast_get_zval(args->child[1]);
@@ -4302,7 +4406,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
{
- zend_bool runtime_resolution = zend_compile_function_name(&name_node, name_ast);
+ bool runtime_resolution = zend_compile_function_name(&name_node, name_ast);
if (runtime_resolution) {
if (zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert")) {
zend_compile_assert(result, zend_ast_get_list(args_ast), Z_STR(name_node.u.constant), NULL);
@@ -4368,7 +4472,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
znode obj_node, method_node;
zend_op *opline;
zend_function *fbc = NULL;
- zend_bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL;
+ bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL;
if (is_this_fetch(obj_ast)) {
if (this_guaranteed_exists()) {
@@ -4421,7 +4525,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
/* }}} */
-static zend_bool zend_is_constructor(zend_string *name) /* {{{ */
+static bool zend_is_constructor(zend_string *name) /* {{{ */
{
return zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME);
}
@@ -4513,7 +4617,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
/* }}} */
-void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel);
+void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel);
void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
{
@@ -4569,6 +4673,7 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */
convert_to_string(&name_node.u.constant);
}
+ // TODO(GLOBALS) Forbid "global $GLOBALS"?
if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable");
} else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) {
@@ -4640,6 +4745,21 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
+ if (is_global_var_fetch(var_ast)) {
+ if (!var_ast->child[1]) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for unsetting");
+ }
+
+ zend_compile_expr(&var_node, var_ast->child[1]);
+ if (var_node.op_type == IS_CONST) {
+ convert_to_string(&var_node.u.constant);
+ }
+
+ opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL;
+ return;
+ }
+
switch (var_ast->kind) {
case ZEND_AST_VAR:
if (is_this_fetch(var_ast)) {
@@ -4759,8 +4879,8 @@ static bool zend_has_finally(void) /* {{{ */
void zend_compile_return(zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
- zend_bool is_generator = (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0;
- zend_bool by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
+ bool is_generator = (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0;
+ bool by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
znode expr_node;
zend_op *opline;
@@ -5127,8 +5247,8 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
zend_ast *value_ast = ast->child[1];
zend_ast *key_ast = ast->child[2];
zend_ast *stmt_ast = ast->child[3];
- zend_bool by_ref = value_ast->kind == ZEND_AST_REF;
- zend_bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
+ bool by_ref = value_ast->kind == ZEND_AST_REF;
+ bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
znode expr_node, reset_node, value_node, key_node;
zend_op *opline;
@@ -5297,7 +5417,7 @@ static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) {
return common_type;
}
-static zend_bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) {
+static bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) {
if (CG(compiler_options) & ZEND_COMPILE_NO_JUMPTABLES) {
return 0;
}
@@ -5318,7 +5438,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_ast_list *cases = zend_ast_get_list(ast->child[1]);
uint32_t i;
- zend_bool has_default_case = 0;
+ bool has_default_case = 0;
znode expr_node, case_node;
zend_op *opline;
@@ -5463,7 +5583,7 @@ static uint32_t count_match_conds(zend_ast_list *arms)
return num_conds;
}
-static zend_bool can_match_use_jumptable(zend_ast_list *arms) {
+static bool can_match_use_jumptable(zend_ast_list *arms) {
for (uint32_t i = 0; i < arms->children; i++) {
zend_ast *arm_ast = arms->child[i];
if (!arm_ast->child[0]) {
@@ -5494,7 +5614,7 @@ void zend_compile_match(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
zend_ast_list *arms = zend_ast_get_list(ast->child[1]);
- zend_bool has_default_arm = 0;
+ bool has_default_arm = 0;
uint32_t opnum_match = (uint32_t)-1;
znode expr_node;
@@ -5506,7 +5626,7 @@ void zend_compile_match(znode *result, zend_ast *ast)
uint32_t num_conds = count_match_conds(arms);
zend_uchar can_use_jumptable = can_match_use_jumptable(arms);
- zend_bool uses_jumptable = can_use_jumptable && num_conds >= 2;
+ bool uses_jumptable = can_use_jumptable && num_conds >= 2;
HashTable *jumptable = NULL;
uint32_t *jmpnz_opnums = NULL;
@@ -5572,7 +5692,7 @@ void zend_compile_match(znode *result, zend_ast *ast)
opnum_default_jmp = zend_emit_jump(0);
}
- zend_bool is_first_case = 1;
+ bool is_first_case = 1;
uint32_t cond_count = 0;
uint32_t *jmp_end_opnums = safe_emalloc(sizeof(uint32_t), arms->children, 0);
@@ -5731,7 +5851,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_ast *var_ast = catch_ast->child[1];
zend_ast *stmt_ast = catch_ast->child[2];
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
- zend_bool is_last_catch = (i + 1 == catches->children);
+ bool is_last_catch = (i + 1 == catches->children);
uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
uint32_t opnum_catch = (uint32_t)-1;
@@ -5740,7 +5860,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
for (j = 0; j < classes->children; j++) {
zend_ast *class_ast = classes->child[j];
- zend_bool is_last_class = (j + 1 == classes->children);
+ bool is_last_class = (j + 1 == classes->children);
if (!zend_is_const_default_class_ref(class_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement");
@@ -5847,7 +5967,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
/* }}} */
/* Encoding declarations must already be handled during parsing */
-zend_bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
+bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
{
zend_ast_list *declares = zend_ast_get_list(ast);
uint32_t i;
@@ -5899,7 +6019,7 @@ zend_bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
/* }}} */
/* Check whether this is the first statement, not counting declares. */
-static zend_result zend_is_first_statement(zend_ast *ast, zend_bool allow_nop) /* {{{ */
+static zend_result zend_is_first_statement(zend_ast *ast, bool allow_nop) /* {{{ */
{
uint32_t i = 0;
zend_ast_list *file_ast = zend_ast_get_list(CG(ast));
@@ -6075,7 +6195,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
}
}
-static zend_bool zend_type_contains_traversable(zend_type type) {
+static bool zend_type_contains_traversable(zend_type type) {
zend_type *single_type;
ZEND_TYPE_FOREACH(type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)
@@ -6089,9 +6209,9 @@ static zend_bool zend_type_contains_traversable(zend_type type) {
// TODO: Ideally we'd canonicalize "iterable" into "array|Traversable" and essentially
// treat it as a built-in type alias.
static zend_type zend_compile_typename(
- zend_ast *ast, zend_bool force_allow_null, zend_bool use_arena) /* {{{ */
+ zend_ast *ast, bool force_allow_null) /* {{{ */
{
- zend_bool allow_null = force_allow_null;
+ bool allow_null = force_allow_null;
zend_ast_attr orig_ast_attr = ast->attr;
zend_type type = ZEND_TYPE_INIT_NONE(0);
if (ast->attr & ZEND_TYPE_NULLABLE) {
@@ -6101,6 +6221,12 @@ static zend_type zend_compile_typename(
if (ast->kind == ZEND_AST_TYPE_UNION) {
zend_ast_list *list = zend_ast_get_list(ast);
+ zend_type_list *type_list;
+ ALLOCA_FLAG(use_heap)
+
+ type_list = do_alloca(ZEND_TYPE_LIST_SIZE(list->children), use_heap);
+ type_list->num_types = 0;
+
for (uint32_t i = 0; i < list->children; i++) {
zend_ast *type_ast = list->child[i];
zend_type single_type = zend_compile_single_typename(type_ast);
@@ -6126,37 +6252,20 @@ static zend_type zend_compile_typename(
ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type));
ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT;
} else {
- zend_type_list *list;
- if (ZEND_TYPE_HAS_LIST(type)) {
- /* Add name to existing name list. */
- zend_type_list *old_list = ZEND_TYPE_LIST(type);
- if (use_arena) {
- // TODO: Add a zend_arena_realloc API?
- list = zend_arena_alloc(
- &CG(arena), ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
- memcpy(list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
- } else {
- list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
- }
- } else {
+ if (type_list->num_types == 0) {
/* Switch from single name to name list. */
- size_t size = ZEND_TYPE_LIST_SIZE(2);
- list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size);
- list->num_types = 1;
- list->types[0] = type;
- ZEND_TYPE_FULL_MASK(list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
+ type_list->num_types = 1;
+ type_list->types[0] = type;
+ ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
+ ZEND_TYPE_SET_LIST(type, type_list);
}
- list->types[list->num_types++] = single_type;
- ZEND_TYPE_SET_LIST(type, list);
- if (use_arena) {
- ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;
- }
+ type_list->types[type_list->num_types++] = single_type;
/* Check for trivially redundant class types */
- for (size_t i = 0; i < list->num_types - 1; i++) {
+ for (size_t i = 0; i < type_list->num_types - 1; i++) {
if (zend_string_equals_ci(
- ZEND_TYPE_NAME(list->types[i]), ZEND_TYPE_NAME(single_type))) {
+ ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) {
zend_string *single_type_str = zend_type_to_string(single_type);
zend_error_noreturn(E_COMPILE_ERROR,
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
@@ -6165,6 +6274,16 @@ static zend_type zend_compile_typename(
}
}
}
+
+ if (type_list->num_types) {
+ zend_type_list *list = zend_arena_alloc(
+ &CG(arena), ZEND_TYPE_LIST_SIZE(type_list->num_types));
+ memcpy(list, type_list, ZEND_TYPE_LIST_SIZE(type_list->num_types));
+ ZEND_TYPE_SET_LIST(type, list);
+ ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;
+ }
+
+ free_alloca(type_list, use_heap);
} else {
type = zend_compile_single_typename(ast);
}
@@ -6217,7 +6336,7 @@ static zend_type zend_compile_typename(
/* }}} */
/* May convert value from int to float. */
-static zend_bool zend_is_valid_default_value(zend_type type, zval *value)
+static bool zend_is_valid_default_value(zend_type type, zval *value)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(value))) {
@@ -6266,7 +6385,7 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
if (args) {
ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
- zend_bool uses_named_args = 0;
+ bool uses_named_args = 0;
for (j = 0; j < args->children; j++) {
zend_ast **arg_ast_ptr = &args->child[j];
zend_ast *arg_ast = *arg_ast_ptr;
@@ -6341,7 +6460,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
arg_infos->name = NULL;
if (return_type_ast) {
arg_infos->type = zend_compile_typename(
- return_type_ast, /* force_allow_null */ 0, /* use_arena */ 0);
+ return_type_ast, /* force_allow_null */ 0);
ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS(
(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0);
} else {
@@ -6364,8 +6483,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
zend_ast *attributes_ast = param_ast->child[3];
zend_ast *doc_comment_ast = param_ast->child[4];
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
- zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
- zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
+ bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
+ bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
uint32_t visibility =
param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE);
@@ -6414,7 +6533,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
if (!optional_param) {
/* Ignore parameters of the form "Type $param = null".
* This is the PHP 5 style way of writing "?Type $param", so allow it for now. */
- zend_bool is_implicit_nullable =
+ bool is_implicit_nullable =
type_ast && Z_TYPE(default_node.u.constant) == IS_NULL;
if (!is_implicit_nullable) {
optional_param = name;
@@ -6440,10 +6559,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
if (type_ast) {
uint32_t default_type = *default_ast_ptr ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
- zend_bool force_nullable = default_type == IS_NULL && !visibility;
+ bool force_nullable = default_type == IS_NULL && !visibility;
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- arg_info->type = zend_compile_typename(type_ast, force_nullable, /* use_arena */ 0);
+ arg_info->type = zend_compile_typename(type_ast, force_nullable);
if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID) {
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
@@ -6480,7 +6599,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
if (visibility) {
zend_op_array *op_array = CG(active_op_array);
zend_class_entry *scope = op_array->scope;
- zend_bool is_ctor =
+ bool is_ctor =
scope && zend_is_constructor(op_array->function_name);
if (!is_ctor) {
zend_error_noreturn(E_COMPILE_ERROR,
@@ -6509,7 +6628,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
/* Recompile the type, as it has different memory management requirements. */
zend_type type = ZEND_TYPE_INIT_NONE(0);
if (type_ast) {
- type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1);
+ type = zend_compile_typename(type_ast, /* force_allow_null */ 0);
}
/* Don't give the property an explicit default value. For typed properties this means
@@ -6544,7 +6663,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
for (i = 0; i < list->children; i++) {
zend_ast *param_ast = list->child[i];
- zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
+ bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
uint32_t visibility =
param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE);
if (!visibility) {
@@ -6614,7 +6733,7 @@ static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array
typedef struct {
HashTable uses;
- zend_bool varvars_used;
+ bool varvars_used;
} closure_info;
static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) {
@@ -6770,10 +6889,10 @@ static void add_stringable_interface(zend_class_entry *ce) {
zend_string_init("stringable", sizeof("stringable") - 1, 0);
}
-zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
+zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
- zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;
+ bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;
uint32_t fn_flags = op_array->fn_flags;
zend_string *lcname;
@@ -6827,9 +6946,18 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name,
}
/* }}} */
-static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, zend_bool toplevel) /* {{{ */
+static uint32_t zend_add_dynamic_func_def(zend_op_array *def) {
+ zend_op_array *op_array = CG(active_op_array);
+ uint32_t def_offset = op_array->num_dynamic_func_defs++;
+ op_array->dynamic_func_defs = erealloc(
+ op_array->dynamic_func_defs, op_array->num_dynamic_func_defs * sizeof(zend_op_array *));
+ op_array->dynamic_func_defs[def_offset] = def;
+ return def_offset;
+}
+
+static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */
{
- zend_string *unqualified_name, *name, *lcname, *key;
+ zend_string *unqualified_name, *name, *lcname;
zend_op *opline;
unqualified_name = decl->name;
@@ -6865,38 +6993,29 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
return;
}
- /* Generate RTD keys until we find one that isn't in use yet. */
- key = NULL;
- do {
- zend_tmp_string_release(key);
- key = zend_build_runtime_definition_key(lcname, decl->start_lineno);
- } while (!zend_hash_add_ptr(CG(function_table), key, op_array));
-
+ uint32_t func_ref = zend_add_dynamic_func_def(op_array);
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
- opline->extended_value = zend_alloc_cache_slot();
- opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, key);
+ opline->op2.num = func_ref;
} else {
opline = get_next_op();
opline->opcode = ZEND_DECLARE_FUNCTION;
opline->op1_type = IS_CONST;
LITERAL_STR(opline->op1, zend_string_copy(lcname));
- /* RTD key is placed after lcname literal in op1 */
- zend_add_literal_string(&key);
+ opline->op2.num = func_ref;
}
zend_string_release_ex(lcname, 0);
}
/* }}} */
-void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */
+void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *params_ast = decl->child[0];
zend_ast *uses_ast = decl->child[1];
zend_ast *stmt_ast = decl->child[2];
zend_ast *return_type_ast = decl->child[3];
- zend_bool is_method = decl->kind == ZEND_AST_METHOD;
+ bool is_method = decl->kind == ZEND_AST_METHOD;
zend_string *method_lcname;
zend_class_entry *orig_class_entry = CG(active_class_entry);
@@ -6910,13 +7029,11 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
op_array->fn_flags |= ZEND_ACC_PRELOADED;
- ZEND_MAP_PTR_NEW(op_array->run_time_cache);
- ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
- } else {
- ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
- ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
}
+ ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void *)));
+ ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
+
op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES);
op_array->fn_flags |= decl->flags;
op_array->line_start = decl->start_lineno;
@@ -6930,7 +7047,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*
}
if (is_method) {
- zend_bool has_body = stmt_ast != NULL;
+ bool has_body = stmt_ast != NULL;
method_lcname = zend_begin_method_decl(op_array, decl->name, has_body);
} else {
zend_begin_func_decl(result, op_array, decl, toplevel);
@@ -7002,6 +7119,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*
zend_do_extended_stmt();
zend_emit_final_return(0);
+ zend_init_static_variables_map_ptr(op_array);
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
@@ -7039,7 +7157,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z
zend_type type = ZEND_TYPE_INIT_NONE(0);
if (type_ast) {
- type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1);
+ type = zend_compile_typename(type_ast, /* force_allow_null */ 0);
if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE)) {
zend_string *str = zend_type_to_string(type);
@@ -7311,7 +7429,7 @@ static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl)
return zend_new_interned_string(result);
}
-void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */
+void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *extends_ast = decl->child[0];
@@ -7412,31 +7530,36 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /
ce->ce_flags |= ZEND_ACC_TOP_LEVEL;
}
- if (toplevel
- /* We currently don't early-bind classes that implement interfaces or use traits */
- && !ce->num_interfaces && !ce->num_traits
+ /* We currently don't early-bind classes that implement interfaces or use traits */
+ if (!ce->num_interfaces && !ce->num_traits
&& !(CG(compiler_options) & ZEND_COMPILE_WITHOUT_EXECUTION)) {
- if (extends_ast) {
- zend_class_entry *parent_ce = zend_lookup_class_ex(
- ce->parent_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
-
- if (parent_ce
- && ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
- && ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))) {
-
- CG(zend_lineno) = decl->end_lineno;
- if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
+ if (toplevel) {
+ if (extends_ast) {
+ zend_class_entry *parent_ce = zend_lookup_class_ex(
+ ce->parent_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
+
+ if (parent_ce
+ && ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
+ && ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))) {
+
+ CG(zend_lineno) = decl->end_lineno;
+ if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
+ CG(zend_lineno) = ast->lineno;
+ zend_string_release(lcname);
+ return;
+ }
CG(zend_lineno) = ast->lineno;
- zend_string_release(lcname);
- return;
}
- CG(zend_lineno) = ast->lineno;
+ } else if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
+ zend_string_release(lcname);
+ zend_build_properties_info_table(ce);
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ return;
}
- } else if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
- zend_string_release(lcname);
+ } else if (!extends_ast) {
+ /* Link unbound simple class */
zend_build_properties_info_table(ce);
ce->ce_flags |= ZEND_ACC_LINKED;
- return;
}
}
@@ -7550,7 +7673,7 @@ void zend_compile_use(zend_ast *ast) /* {{{ */
zend_string *current_ns = FC(current_namespace);
uint32_t type = ast->attr;
HashTable *current_import = zend_get_import_ht(type);
- zend_bool case_sensitive = type == ZEND_SYMBOL_CONST;
+ bool case_sensitive = type == ZEND_SYMBOL_CONST;
for (i = 0; i < list->children; ++i) {
zend_ast *use_ast = list->child[i];
@@ -7684,7 +7807,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */
zend_ast *name_ast = ast->child[0];
zend_ast *stmt_ast = ast->child[1];
zend_string *name;
- zend_bool with_bracket = stmt_ast != NULL;
+ bool with_bracket = stmt_ast != NULL;
/* handle mixed syntax declaration or nested namespaces */
if (!FC(has_bracketed_namespaces)) {
@@ -7705,7 +7828,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */
}
}
- zend_bool is_first_namespace = (!with_bracket && !FC(current_namespace))
+ bool is_first_namespace = (!with_bracket && !FC(current_namespace))
|| (with_bracket && !FC(has_bracketed_namespaces));
if (is_first_namespace && FAILURE == zend_is_first_statement(ast, /* allow_nop */ 1)) {
zend_error_noreturn(E_COMPILE_ERROR, "Namespace declaration statement has to be "
@@ -7764,7 +7887,7 @@ void zend_compile_halt_compiler(zend_ast *ast) /* {{{ */
}
/* }}} */
-static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
+static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
{
zend_op_array *op_array = CG(active_op_array);
zend_class_entry *ce = CG(active_class_entry);
@@ -7855,7 +7978,7 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */
+ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */
{
if ((opcode == ZEND_CONCAT || opcode == ZEND_FAST_CONCAT)) {
/* Array to string warning. */
@@ -7910,7 +8033,7 @@ ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zva
}
/* }}} */
-static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */
+static inline bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */
{
if (zend_binary_op_produces_error(opcode, op1, op2)) {
return 0;
@@ -7922,7 +8045,7 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode
}
/* }}} */
-zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
+bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
{
if (opcode == ZEND_BW_NOT) {
return Z_TYPE_P(op) <= IS_TRUE || Z_TYPE_P(op) == IS_ARRAY;
@@ -7931,7 +8054,7 @@ zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
return 0;
}
-static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
+static inline bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
{
if (zend_unary_op_produces_error(opcode, op)) {
return 0;
@@ -7943,7 +8066,7 @@ static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode,
}
/* }}} */
-static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
+static inline bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
{
zval right;
ZVAL_LONG(&right, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
@@ -7959,12 +8082,12 @@ static inline void zend_ct_eval_greater(zval *result, zend_ast_kind kind, zval *
}
/* }}} */
-static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
+static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
zend_ast *last_elem_ast = NULL;
uint32_t i;
- zend_bool is_constant = 1;
+ bool is_constant = 1;
if (ast->attr == ZEND_ARRAY_SYNTAX_LIST) {
zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression");
@@ -8026,9 +8149,8 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
if (key) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot unpack array with string keys");
- }
- if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), val)) {
+ zend_hash_update(Z_ARRVAL_P(result), key, val);
+ } else if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), val)) {
zval_ptr_dtor(result);
return 0;
}
@@ -8490,7 +8612,7 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
znode var_node_is, var_node_w, default_node, assign_node, *node;
zend_op *opline;
uint32_t coalesce_opnum;
- zend_bool need_frees = 0;
+ bool need_frees = 0;
/* Remember expressions compiled during the initial BP_VAR_IS lookup,
* to avoid double-evaluation when we compile again with BP_VAR_W. */
@@ -8620,7 +8742,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
znode value_node, key_node;
znode *value_node_ptr = NULL, *key_node_ptr = NULL;
zend_op *opline;
- zend_bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
+ bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
zend_mark_function_as_generator();
@@ -8733,6 +8855,28 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
}
}
+ if (is_globals_fetch(var_ast)) {
+ result->op_type = IS_CONST;
+ ZVAL_BOOL(&result->u.constant, ast->kind == ZEND_AST_ISSET);
+ return;
+ }
+
+ if (is_global_var_fetch(var_ast)) {
+ if (!var_ast->child[1]) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
+ }
+
+ zend_compile_expr(&var_node, var_ast->child[1]);
+ if (var_node.op_type == IS_CONST) {
+ convert_to_string(&var_node.u.constant);
+ }
+
+ opline = zend_emit_op_tmp(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL);
+ opline->extended_value =
+ ZEND_FETCH_GLOBAL | (ast->kind == ZEND_AST_EMPTY ? ZEND_ISEMPTY : 0);
+ return;
+ }
+
zend_short_circuiting_mark_inner(var_ast);
switch (var_ast->kind) {
case ZEND_AST_VAR:
@@ -8811,7 +8955,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
zend_ast_list *list = zend_ast_get_list(ast);
zend_op *opline;
uint32_t i, opnum_init = -1;
- zend_bool packed = 1;
+ bool packed = 1;
if (zend_try_ct_eval_array(&result->u.constant, ast)) {
result->op_type = IS_CONST;
@@ -8824,7 +8968,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
zend_ast *value_ast, *key_ast;
- zend_bool by_ref;
+ bool by_ref;
znode value_node, key_node, *key_node_ptr = NULL;
if (elem_ast == NULL) {
@@ -8891,7 +9035,7 @@ void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */
zend_op *opline;
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zend_string *orig_name = zend_ast_get_str(name_ast);
zend_string *resolved_name = zend_resolve_const_name(orig_name, name_ast->attr, &is_fully_qualified);
@@ -9168,7 +9312,7 @@ void zend_compile_magic_const(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
+bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
{
return kind == ZEND_AST_ZVAL || kind == ZEND_AST_BINARY_OP
|| kind == ZEND_AST_GREATER || kind == ZEND_AST_GREATER_EQUAL
@@ -9255,7 +9399,7 @@ void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */
zend_ast *ast = *ast_ptr;
zend_ast *name_ast = ast->child[0];
zend_string *orig_name = zend_ast_get_str(name_ast);
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zval result;
zend_string *resolved_name;
@@ -9676,7 +9820,7 @@ zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_r
return opcode;
}
-zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref) /* {{{ */
+zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref) /* {{{ */
{
switch (ast->kind) {
case ZEND_AST_VAR:
@@ -9737,7 +9881,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
case ZEND_AST_AND:
case ZEND_AST_OR:
{
- zend_bool child0_is_true, child1_is_true;
+ bool child0_is_true, child1_is_true;
zend_eval_const_expr(&ast->child[0]);
zend_eval_const_expr(&ast->child[1]);
if (ast->child[0]->kind != ZEND_AST_ZVAL) {
@@ -9911,7 +10055,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
case ZEND_AST_CONST:
{
zend_ast *name_ast = ast->child[0];
- zend_bool is_fully_qualified;
+ bool is_fully_qualified;
zend_string *resolved_name = zend_resolve_const_name(
zend_ast_get_str(name_ast), name_ast->attr, &is_fully_qualified);
@@ -9940,7 +10084,6 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
}
resolved_name = zend_resolve_class_name_ast(class_ast);
-
if (!zend_try_ct_eval_class_const(&result, resolved_name, zend_ast_get_str(name_ast))) {
zend_string_release_ex(resolved_name, 0);
return;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index a4cb6fca74..6f64624fd8 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -107,8 +107,8 @@ typedef struct _zend_file_context {
zend_declarables declarables;
zend_string *current_namespace;
- zend_bool in_namespace;
- zend_bool has_bracketed_namespaces;
+ bool in_namespace;
+ bool has_bracketed_namespaces;
HashTable *imports;
HashTable *imports_function;
@@ -117,17 +117,12 @@ typedef struct _zend_file_context {
HashTable seen_symbols;
} zend_file_context;
-typedef struct {
- uint32_t offset;
- uint32_t len;
-} zend_lexer_ident_ref;
-
typedef union _zend_parser_stack_elem {
zend_ast *ast;
zend_string *str;
zend_ulong num;
unsigned char *ptr;
- zend_lexer_ident_ref ident;
+ unsigned char *ident;
} zend_parser_stack_elem;
void zend_compile_top_stmt(zend_ast *ast);
@@ -158,7 +153,7 @@ typedef struct _zend_brk_cont_element {
int cont;
int brk;
int parent;
- zend_bool is_switch;
+ bool is_switch;
} zend_brk_cont_element;
typedef struct _zend_label {
@@ -238,7 +233,7 @@ typedef struct _zend_oparray_context {
/* op_array or class is preloaded | | | */
#define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */
/* | | | */
-/* Class Flags (unused: 22...) | | | */
+/* Class Flags (unused: 28...) | | | */
/* =========== | | | */
/* | | | */
/* Special class types | | | */
@@ -287,6 +282,19 @@ typedef struct _zend_oparray_context {
/* Whether this class was used in its unlinked state. | | | */
#define ZEND_ACC_HAS_UNLINKED_USES (1 << 21) /* X | | | */
/* | | | */
+/* stored in opcache (may be partially) | | | */
+#define ZEND_ACC_CACHED (1 << 22) /* X | | | */
+/* | | | */
+/* temporary flag used during delayed variance checks | | | */
+#define ZEND_ACC_CACHEABLE (1 << 23) /* X | | | */
+/* | | | */
+#define ZEND_ACC_HAS_AST_CONSTANTS (1 << 24) /* X | | | */
+#define ZEND_ACC_HAS_AST_PROPERTIES (1 << 25) /* X | | | */
+#define ZEND_ACC_HAS_AST_STATICS (1 << 26) /* X | | | */
+/* | | | */
+/* loaded from file cache to process memory | | | */
+#define ZEND_ACC_FILE_CACHED (1 << 27) /* X | | | */
+/* | | | */
/* Function Flags (unused: 27-30) | | | */
/* ============== | | | */
/* | | | */
@@ -448,8 +456,13 @@ struct _zend_op_array {
zend_string *doc_comment;
int last_literal;
+ uint32_t num_dynamic_func_defs;
zval *literals;
+ /* Functions that are declared dynamically are stored here and
+ * referenced by index from opcodes. */
+ zend_op_array **dynamic_func_defs;
+
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
@@ -768,12 +781,12 @@ zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right);
zend_ast *zend_negate_num_string(zend_ast *ast);
uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag);
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag);
-zend_bool zend_handle_encoding_declaration(zend_ast *ast);
+bool zend_handle_encoding_declaration(zend_ast *ast);
/* parser-driven code generators */
void zend_do_free(znode *op1);
-ZEND_API zend_result do_bind_function(zval *lcname);
+ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname);
ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name);
ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_array);
ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline);
@@ -787,6 +800,7 @@ void zend_verify_namespace(void);
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline);
ZEND_API void function_add_ref(zend_function *function);
+void zend_init_static_variables_map_ptr(zend_op_array *op_array);
#define INITIAL_OP_ARRAY_SIZE 64
@@ -798,15 +812,17 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type);
ZEND_API zend_op_array *compile_string(zend_string *source_string, const char *filename);
ZEND_API zend_op_array *compile_filename(int type, zval *filename);
ZEND_API zend_ast *zend_compile_string_to_ast(
- zend_string *code, struct _zend_arena **ast_arena, const char *filename);
+ zend_string *code, struct _zend_arena **ast_arena, zend_string *filename);
ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...);
ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
ZEND_API void destroy_op_array(zend_op_array *op_array);
+ZEND_API void zend_destroy_static_vars(zend_op_array *op_array);
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
+ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_classes(void);
-ZEND_API void zend_type_release(zend_type type, zend_bool persistent);
+ZEND_API void zend_type_release(zend_type type, bool persistent);
ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name);
@@ -840,30 +856,30 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen
#define ZEND_FUNCTION_DTOR zend_function_dtor
#define ZEND_CLASS_DTOR destroy_zend_class
-typedef zend_bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline);
+typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline);
ZEND_API void zend_recalc_live_ranges(
zend_op_array *op_array, zend_needs_live_range_cb needs_live_range);
ZEND_API void pass_two(zend_op_array *op_array);
-ZEND_API zend_bool zend_is_compiling(void);
+ZEND_API bool zend_is_compiling(void);
ZEND_API char *zend_make_compiled_string_description(const char *name);
-ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers);
+ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers);
uint32_t zend_get_class_fetch_type(zend_string *name);
ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc);
ZEND_API bool zend_is_smart_branch(const zend_op *opline);
-typedef zend_bool (*zend_auto_global_callback)(zend_string *name);
+typedef bool (*zend_auto_global_callback)(zend_string *name);
typedef struct _zend_auto_global {
zend_string *name;
zend_auto_global_callback auto_global_callback;
- zend_bool jit;
- zend_bool armed;
+ bool jit;
+ bool armed;
} zend_auto_global;
-ZEND_API zend_result zend_register_auto_global(zend_string *name, zend_bool jit, zend_auto_global_callback auto_global_callback);
+ZEND_API zend_result zend_register_auto_global(zend_string *name, bool jit, zend_auto_global_callback auto_global_callback);
ZEND_API void zend_activate_auto_globals(void);
-ZEND_API zend_bool zend_is_auto_global(zend_string *name);
-ZEND_API zend_bool zend_is_auto_global_str(const char *name, size_t len);
+ZEND_API bool zend_is_auto_global(zend_string *name);
+ZEND_API bool zend_is_auto_global_str(const char *name, size_t len);
ZEND_API size_t zend_dirname(char *path, size_t len);
ZEND_API void zend_set_function_arg_flags(zend_function *func);
@@ -1134,6 +1150,6 @@ END_EXTERN_C()
/* The default value for CG(compiler_options) during eval() */
#define ZEND_COMPILE_DEFAULT_FOR_EVAL 0
-ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2);
+ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2);
#endif /* ZEND_COMPILE_H */
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index f289cc5d91..9534190f8d 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -158,7 +158,7 @@ ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int
zend_register_constant(&c);
}
-ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, zend_bool bval, int flags, int module_number)
+ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number)
{
zend_constant c;
@@ -374,7 +374,7 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
ce = zend_fetch_class(class_name, flags);
}
if (ce) {
- c = zend_hash_find_ptr(&ce->constants_table, constant_name);
+ c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constant_name);
if (c == NULL) {
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
zend_throw_error(NULL, "Undefined constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
@@ -482,7 +482,7 @@ ZEND_API zend_result zend_register_constant(zend_constant *c)
zend_string *lowercase_name = NULL;
zend_string *name;
zend_result ret = SUCCESS;
- zend_bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0;
+ bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0;
#if 0
printf("Registering constant for module %d\n", c->module_number);
diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h
index 0a3c5a7377..0723514447 100644
--- a/Zend/zend_constants.h
+++ b/Zend/zend_constants.h
@@ -76,7 +76,7 @@ ZEND_API bool zend_verify_const_access(zend_class_constant *c, zend_class_entry
ZEND_API zval *zend_get_constant(zend_string *name);
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
-ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, zend_bool bval, int flags, int module_number);
+ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number);
ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number);
ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number);
ZEND_API void zend_register_double_constant(const char *name, size_t name_len, double dval, int flags, int module_number);
diff --git a/Zend/zend_cpuinfo.c b/Zend/zend_cpuinfo.c
index 529ab529a3..bd4dcf298f 100644
--- a/Zend/zend_cpuinfo.c
+++ b/Zend/zend_cpuinfo.c
@@ -90,7 +90,7 @@ static unsigned get_xcr0_eax() {
# endif
}
-static zend_bool is_avx_supported() {
+static bool is_avx_supported() {
if (!(cpuinfo.ecx & ZEND_CPU_FEATURE_AVX)) {
/* No support for AVX */
return 0;
@@ -106,7 +106,7 @@ static zend_bool is_avx_supported() {
return 1;
}
#else
-static zend_bool is_avx_supported() {
+static bool is_avx_supported() {
return 0;
}
#endif
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index b52b1ab113..af71ac7a65 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -53,14 +53,21 @@ static zend_object_handlers default_exception_handlers;
/* {{{ zend_implement_throwable */
static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type)
{
- if (instanceof_function(class_type, zend_ce_exception) || instanceof_function(class_type, zend_ce_error)) {
+ /* zend_ce_exception and zend_ce_error may not be initialized yet when this is caleld (e.g when
+ * implementing Throwable for Exception itself). Perform a manual inheritance check. */
+ zend_class_entry *root = class_type;
+ while (root->parent) {
+ root = root->parent;
+ }
+ if (zend_string_equals_literal(root->name, "Exception")
+ || zend_string_equals_literal(root->name, "Error")) {
return SUCCESS;
}
- zend_error_noreturn(E_ERROR, "Class %s cannot implement interface %s, extend %s or %s instead",
+
+ zend_error_noreturn(E_ERROR,
+ "Class %s cannot implement interface %s, extend Exception or Error instead",
ZSTR_VAL(class_type->name),
- ZSTR_VAL(interface->name),
- ZSTR_VAL(zend_ce_exception->name),
- ZSTR_VAL(zend_ce_error->name));
+ ZSTR_VAL(interface->name));
return FAILURE;
}
/* }}} */
@@ -93,7 +100,7 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo
}
ZEND_ASSERT(instanceof_function(add_previous->ce, zend_ce_throwable)
- && "Previous execption must implement Throwable");
+ && "Previous exception must implement Throwable");
ZVAL_OBJ(&pv, add_previous);
ZVAL_OBJ(&zv, exception);
@@ -144,7 +151,7 @@ void zend_exception_restore(void) /* {{{ */
}
/* }}} */
-static zend_always_inline zend_bool is_handle_exception_set() {
+static zend_always_inline bool is_handle_exception_set() {
zend_execute_data *execute_data = EG(current_execute_data);
return !execute_data->func
|| !ZEND_USER_CODE(execute_data->func->common.type)
@@ -339,7 +346,7 @@ ZEND_METHOD(ErrorException, __construct)
{
zend_string *message = NULL, *filename = NULL;
zend_long code = 0, severity = E_ERROR, lineno;
- zend_bool lineno_is_null = 1;
+ bool lineno_is_null = 1;
zval tmp, *object, *previous = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SllS!l!O!", &message, &code, &severity, &filename, &lineno, &lineno_is_null, &previous, zend_ce_throwable) == FAILURE) {
@@ -478,7 +485,7 @@ ZEND_METHOD(ErrorException, getSeverity)
static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
{
/* the trivial way would be to do
- * convert_to_string_ex(arg);
+ * convert_to_string(arg);
* append it and kill the now tmp arg.
* but that could cause some E_NOTICE and also damn long lines.
*/
@@ -740,87 +747,50 @@ ZEND_METHOD(Exception, __toString)
}
/* }}} */
-static void declare_exception_properties(zend_class_entry *ce)
-{
- zval val;
-
- zend_declare_property_string(ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
- zend_declare_property_string(ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
- zend_declare_property_long(ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
- zend_declare_property_null(ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
- zend_declare_property_null(ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
-
- ZVAL_EMPTY_ARRAY(&val);
- zend_declare_typed_property(
- ce, ZSTR_KNOWN(ZEND_STR_TRACE), &val, ZEND_ACC_PRIVATE, NULL,
- (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
-
- ZVAL_NULL(&val);
- zend_declare_typed_property(
- ce, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &val, ZEND_ACC_PRIVATE, NULL,
- (zend_type) ZEND_TYPE_INIT_CE(zend_ce_throwable, /* allow_null */ 1, 0));
-}
-
void zend_register_default_exception(void) /* {{{ */
{
- zend_class_entry ce;
-
- REGISTER_MAGIC_INTERFACE(throwable, Throwable);
- zend_class_implements(zend_ce_throwable, 1, zend_ce_stringable);
+ zend_ce_throwable = register_class_Throwable(zend_ce_stringable);
+ zend_ce_throwable->interface_gets_implemented = zend_implement_throwable;
memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers));
default_exception_handlers.clone_obj = NULL;
- INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods);
- zend_ce_exception = zend_register_internal_class_ex(&ce, NULL);
+ zend_ce_exception = register_class_Exception(zend_ce_throwable);
zend_ce_exception->create_object = zend_default_exception_new;
- zend_class_implements(zend_ce_exception, 1, zend_ce_throwable);
- declare_exception_properties(zend_ce_exception);
- INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods);
- zend_ce_error_exception = zend_register_internal_class_ex(&ce, zend_ce_exception);
+ zend_ce_error_exception = register_class_ErrorException(zend_ce_exception);
zend_ce_error_exception->create_object = zend_error_exception_new;
+ /* Declared manually because it uses constant E_ERROR. */
zend_declare_property_long(zend_ce_error_exception, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED);
- INIT_CLASS_ENTRY(ce, "Error", class_Error_methods);
- zend_ce_error = zend_register_internal_class_ex(&ce, NULL);
+ zend_ce_error = register_class_Error(zend_ce_throwable);
zend_ce_error->create_object = zend_default_exception_new;
- zend_class_implements(zend_ce_error, 1, zend_ce_throwable);
- declare_exception_properties(zend_ce_error);
- INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods);
- zend_ce_compile_error = zend_register_internal_class_ex(&ce, zend_ce_error);
+ zend_ce_compile_error = register_class_CompileError(zend_ce_error);
zend_ce_compile_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods);
- zend_ce_parse_error = zend_register_internal_class_ex(&ce, zend_ce_compile_error);
+ zend_ce_parse_error = register_class_ParseError(zend_ce_compile_error);
zend_ce_parse_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods);
- zend_ce_type_error = zend_register_internal_class_ex(&ce, zend_ce_error);
+ zend_ce_type_error = register_class_TypeError(zend_ce_error);
zend_ce_type_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods);
- zend_ce_argument_count_error = zend_register_internal_class_ex(&ce, zend_ce_type_error);
+ zend_ce_argument_count_error = register_class_ArgumentCountError(zend_ce_type_error);
zend_ce_argument_count_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods);
- zend_ce_value_error = zend_register_internal_class_ex(&ce, zend_ce_error);
+ zend_ce_value_error = register_class_ValueError(zend_ce_error);
zend_ce_value_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods);
- zend_ce_arithmetic_error = zend_register_internal_class_ex(&ce, zend_ce_error);
+ zend_ce_arithmetic_error = register_class_ArithmeticError(zend_ce_error);
zend_ce_arithmetic_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods);
- zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error);
+ zend_ce_division_by_zero_error = register_class_DivisionByZeroError(zend_ce_arithmetic_error);
zend_ce_division_by_zero_error->create_object = zend_default_exception_new;
- INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
-
- INIT_CLASS_ENTRY(ce, "UnhandledMatchError", NULL);
- zend_ce_unhandled_match_error = zend_register_internal_class_ex(&ce, zend_ce_error);
+ zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error);
zend_ce_unhandled_match_error->create_object = zend_default_exception_new;
+
+ INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
}
/* }}} */
@@ -1017,7 +987,7 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void)
EG(current_execute_data)->opline = EG(exception_op);
}
-ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex)
+ZEND_API bool zend_is_unwind_exit(zend_object *ex)
{
return ex->ce == &zend_ce_unwind_exit;
}
diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h
index 9d0c18a6da..74724c2424 100644
--- a/Zend/zend_exceptions.h
+++ b/Zend/zend_exceptions.h
@@ -70,7 +70,7 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex);
ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity);
ZEND_API ZEND_COLD void zend_throw_unwind_exit(void);
-ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex);
+ZEND_API bool zend_is_unwind_exit(zend_object *ex);
#include "zend_globals.h"
diff --git a/Zend/zend_exceptions.stub.php b/Zend/zend_exceptions.stub.php
index 3df58543ed..5f6a3a53e4 100644
--- a/Zend/zend_exceptions.stub.php
+++ b/Zend/zend_exceptions.stub.php
@@ -1,6 +1,6 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
interface Throwable extends Stringable
{
@@ -22,6 +22,19 @@ interface Throwable extends Stringable
class Exception implements Throwable
{
+ /** @var string */
+ protected $message = "";
+ /** @var string */
+ private $string = "";
+ /** @var int */
+ protected $code = 0;
+ /** @var string|null */
+ protected $file = null;
+ /** @var int|null */
+ protected $line = null;
+ private array $trace = [];
+ private ?Throwable $previous = null;
+
final private function __clone(): void {}
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null) {}
@@ -49,13 +62,36 @@ class Exception implements Throwable
class ErrorException extends Exception
{
- public function __construct(string $message = "", int $code = 0, int $severity = E_ERROR, ?string $filename = null, ?int $line = null, ?Throwable $previous = null) {}
+ /** @var int */
+ protected $severity = E_ERROR;
+
+ public function __construct(
+ string $message = "",
+ int $code = 0,
+ int $severity = E_ERROR,
+ ?string $filename = null,
+ ?int $line = null,
+ ?Throwable $previous = null
+ ) {}
final public function getSeverity(): int {}
}
class Error implements Throwable
{
+ /** @var string */
+ protected $message = "";
+ /** @var string */
+ private $string = "";
+ /** @var int */
+ protected $code = 0;
+ /** @var string|null */
+ protected $file = null;
+ /** @var int|null */
+ protected $line = null;
+ private array $trace = [];
+ private ?Throwable $previous = null;
+
/** @implementation-alias Exception::__clone */
final private function __clone(): void {}
diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h
index f46ac083dd..b1e4844717 100644
--- a/Zend/zend_exceptions_arginfo.h
+++ b/Zend/zend_exceptions_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2fa61163fb7db8d87b14f9cf9a42c3cd8093f2a6 */
+ * Stub hash: f322ba2ed3e636b6e99400edfbff98102b7e3d06 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Throwable_getMessage, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -185,3 +185,212 @@ static const zend_function_entry class_DivisionByZeroError_methods[] = {
static const zend_function_entry class_UnhandledMatchError_methods[] = {
ZEND_FE_END
};
+
+static zend_class_entry *register_class_Throwable(zend_class_entry *class_entry_Stringable)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Throwable", class_Throwable_methods);
+ class_entry = zend_register_internal_interface(&ce);
+ zend_class_implements(class_entry, 1, class_entry_Stringable);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Exception(zend_class_entry *class_entry_Throwable)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ zend_class_implements(class_entry, 1, class_entry_Throwable);
+
+ zval property_message_default_value;
+ ZVAL_EMPTY_STRING(&property_message_default_value);
+ zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1);
+ zend_declare_property_ex(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_message_name);
+
+ zval property_string_default_value;
+ ZVAL_EMPTY_STRING(&property_string_default_value);
+ zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1);
+ zend_declare_property_ex(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL);
+ zend_string_release(property_string_name);
+
+ zval property_code_default_value;
+ ZVAL_LONG(&property_code_default_value, 0);
+ zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1);
+ zend_declare_property_ex(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_code_name);
+
+ zval property_file_default_value;
+ ZVAL_NULL(&property_file_default_value);
+ zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1);
+ zend_declare_property_ex(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_file_name);
+
+ zval property_line_default_value;
+ ZVAL_NULL(&property_line_default_value);
+ zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1);
+ zend_declare_property_ex(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_line_name);
+
+ zval property_trace_default_value;
+ ZVAL_EMPTY_ARRAY(&property_trace_default_value);
+ zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1);
+ zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
+ zend_string_release(property_trace_name);
+
+ zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1);
+ zval property_previous_default_value;
+ ZVAL_NULL(&property_previous_default_value);
+ zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1);
+ zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
+ zend_string_release(property_previous_name);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ErrorException(zend_class_entry *class_entry_Exception)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Error(zend_class_entry *class_entry_Throwable)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Error", class_Error_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ zend_class_implements(class_entry, 1, class_entry_Throwable);
+
+ zval property_message_default_value;
+ ZVAL_EMPTY_STRING(&property_message_default_value);
+ zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1);
+ zend_declare_property_ex(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_message_name);
+
+ zval property_string_default_value;
+ ZVAL_EMPTY_STRING(&property_string_default_value);
+ zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1);
+ zend_declare_property_ex(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL);
+ zend_string_release(property_string_name);
+
+ zval property_code_default_value;
+ ZVAL_LONG(&property_code_default_value, 0);
+ zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1);
+ zend_declare_property_ex(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_code_name);
+
+ zval property_file_default_value;
+ ZVAL_NULL(&property_file_default_value);
+ zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1);
+ zend_declare_property_ex(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_file_name);
+
+ zval property_line_default_value;
+ ZVAL_NULL(&property_line_default_value);
+ zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1);
+ zend_declare_property_ex(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL);
+ zend_string_release(property_line_name);
+
+ zval property_trace_default_value;
+ ZVAL_EMPTY_ARRAY(&property_trace_default_value);
+ zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1);
+ zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
+ zend_string_release(property_trace_name);
+
+ zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1);
+ zval property_previous_default_value;
+ ZVAL_NULL(&property_previous_default_value);
+ zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1);
+ zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
+ zend_string_release(property_previous_name);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_CompileError(zend_class_entry *class_entry_Error)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Error);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ParseError(zend_class_entry *class_entry_CompileError)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_CompileError);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_TypeError(zend_class_entry *class_entry_Error)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Error);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ArgumentCountError(zend_class_entry *class_entry_TypeError)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_TypeError);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ValueError(zend_class_entry *class_entry_Error)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Error);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ArithmeticError(zend_class_entry *class_entry_Error)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Error);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_DivisionByZeroError(zend_class_entry *class_entry_ArithmeticError)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_ArithmeticError);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_UnhandledMatchError(zend_class_entry *class_entry_Error)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "UnhandledMatchError", class_UnhandledMatchError_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Error);
+
+ return class_entry;
+}
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 69b0fb5242..11a140e845 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -716,12 +716,12 @@ ZEND_API ZEND_COLD void zend_verify_arg_error(
zend_string_release(need_msg);
}
-static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg)
+static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg)
{
zend_long lval;
double dval;
zend_string *str;
- zend_bool bval;
+ bool bval;
/* Type preference order: int -> float -> string -> bool */
if (type_mask & MAY_BE_LONG) {
@@ -739,22 +739,22 @@ static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg
ZVAL_DOUBLE(arg, dval);
return 1;
}
- } else if (zend_parse_arg_long_weak(arg, &lval)) {
+ } else if (zend_parse_arg_long_weak(arg, &lval, 0)) {
zval_ptr_dtor(arg);
ZVAL_LONG(arg, lval);
return 1;
}
}
- if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval)) {
+ if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, 0)) {
zval_ptr_dtor(arg);
ZVAL_DOUBLE(arg, dval);
return 1;
}
- if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, &str)) {
+ if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, &str, 0)) {
/* on success "arg" is converted to IS_STRING */
return 1;
}
- if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval)) {
+ if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval, 0)) {
zval_ptr_dtor(arg);
ZVAL_BOOL(arg, bval);
return 1;
@@ -775,29 +775,29 @@ static bool can_convert_to_string(zval *zv) {
}
/* Used to sanity-check internal arginfo types without performing any actual type conversions. */
-static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg)
+static bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg)
{
zend_long lval;
double dval;
- zend_bool bval;
+ bool bval;
- if ((type_mask & MAY_BE_LONG) && zend_parse_arg_long_weak(arg, &lval)) {
+ if ((type_mask & MAY_BE_LONG) && zend_parse_arg_long_weak(arg, &lval, 0)) {
return 1;
}
- if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval)) {
+ if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, 0)) {
return 1;
}
if ((type_mask & MAY_BE_STRING) && can_convert_to_string(arg)) {
return 1;
}
- if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval)) {
+ if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval, 0)) {
return 1;
}
return 0;
}
#endif
-ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg)
+ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg)
{
if (UNEXPECTED(strict)) {
/* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */
@@ -851,20 +851,32 @@ static zend_class_entry *resolve_single_class_type(zend_string *name, zend_class
}
}
-static zend_bool zend_check_and_resolve_property_class_type(
+static bool zend_check_and_resolve_property_class_type(
zend_property_info *info, zend_class_entry *object_ce) {
zend_class_entry *ce;
if (ZEND_TYPE_HAS_LIST(info->type)) {
zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
if (ZEND_TYPE_HAS_NAME(*list_type)) {
- zend_string *name = ZEND_TYPE_NAME(*list_type);
- ce = resolve_single_class_type(name, info->ce);
- if (!ce) {
- continue;
+ if (ZEND_TYPE_HAS_CE_CACHE(*list_type)) {
+ ce = ZEND_TYPE_CE_CACHE(*list_type);
+ if (!ce) {
+ zend_string *name = ZEND_TYPE_NAME(*list_type);
+ ce = resolve_single_class_type(name, info->ce);
+ if (UNEXPECTED(!ce)) {
+ continue;
+ }
+ ZEND_TYPE_SET_CE_CACHE(*list_type, ce);
+ }
+ } else {
+ zend_string *name = ZEND_TYPE_NAME(*list_type);
+ ce = resolve_single_class_type(name, info->ce);
+ if (!ce) {
+ continue;
+ }
+ zend_string_release(name);
+ ZEND_TYPE_SET_CE(*list_type, ce);
}
- zend_string_release(name);
- ZEND_TYPE_SET_CE(*list_type, ce);
} else {
ce = ZEND_TYPE_CE(*list_type);
}
@@ -875,14 +887,26 @@ static zend_bool zend_check_and_resolve_property_class_type(
return 0;
} else {
if (UNEXPECTED(ZEND_TYPE_HAS_NAME(info->type))) {
- zend_string *name = ZEND_TYPE_NAME(info->type);
- ce = resolve_single_class_type(name, info->ce);
- if (UNEXPECTED(!ce)) {
- return 0;
- }
+ if (ZEND_TYPE_HAS_CE_CACHE(info->type)) {
+ ce = ZEND_TYPE_CE_CACHE(info->type);
+ if (!ce) {
+ zend_string *name = ZEND_TYPE_NAME(info->type);
+ ce = resolve_single_class_type(name, info->ce);
+ if (UNEXPECTED(!ce)) {
+ return 0;
+ }
+ ZEND_TYPE_SET_CE_CACHE(info->type, ce);
+ }
+ } else {
+ zend_string *name = ZEND_TYPE_NAME(info->type);
+ ce = resolve_single_class_type(name, info->ce);
+ if (UNEXPECTED(!ce)) {
+ return 0;
+ }
- zend_string_release(name);
- ZEND_TYPE_SET_CE(info->type, ce);
+ zend_string_release(name);
+ ZEND_TYPE_SET_CE(info->type, ce);
+ }
} else {
ce = ZEND_TYPE_CE(info->type);
}
@@ -890,7 +914,7 @@ static zend_bool zend_check_and_resolve_property_class_type(
}
}
-static zend_always_inline zend_bool i_zend_check_property_type(zend_property_info *info, zval *property, zend_bool strict)
+static zend_always_inline bool i_zend_check_property_type(zend_property_info *info, zval *property, bool strict)
{
ZEND_ASSERT(!Z_ISREF_P(property));
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) {
@@ -910,7 +934,7 @@ static zend_always_inline zend_bool i_zend_check_property_type(zend_property_inf
return zend_verify_scalar_type_hint(type_mask, property, strict, 0);
}
-static zend_always_inline zend_bool i_zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict)
+static zend_always_inline bool i_zend_verify_property_type(zend_property_info *info, zval *property, bool strict)
{
if (i_zend_check_property_type(info, property, strict)) {
return 1;
@@ -920,7 +944,7 @@ static zend_always_inline zend_bool i_zend_verify_property_type(zend_property_in
return 0;
}
-ZEND_API zend_bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) {
+ZEND_API bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, bool strict) {
return i_zend_verify_property_type(info, property, strict);
}
@@ -939,7 +963,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES());
}
-ZEND_API zend_bool zend_value_instanceof_static(zval *zv) {
+ZEND_API bool zend_value_instanceof_static(zval *zv) {
if (Z_TYPE_P(zv) != IS_OBJECT) {
return 0;
}
@@ -959,18 +983,23 @@ ZEND_API zend_bool zend_value_instanceof_static(zval *zv) {
# define HAVE_CACHE_SLOT 1
#endif
-static zend_always_inline zend_bool zend_check_type_slow(
- zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
- zend_bool is_return_type, zend_bool is_internal)
+static zend_always_inline bool zend_check_type_slow(
+ zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
+ bool is_return_type, bool is_internal)
{
uint32_t type_mask;
- if (ZEND_TYPE_HAS_CLASS(type) && Z_TYPE_P(arg) == IS_OBJECT) {
+ if (ZEND_TYPE_HAS_CLASS(*type) && Z_TYPE_P(arg) == IS_OBJECT) {
zend_class_entry *ce;
- if (ZEND_TYPE_HAS_LIST(type)) {
+ if (ZEND_TYPE_HAS_LIST(*type)) {
zend_type *list_type;
- ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
if (HAVE_CACHE_SLOT && *cache_slot) {
ce = *cache_slot;
+ } else if (ZEND_TYPE_HAS_CE_CACHE(*list_type) && ZEND_TYPE_CE_CACHE(*list_type)) {
+ ce = ZEND_TYPE_CE_CACHE(*list_type);
+ if (HAVE_CACHE_SLOT) {
+ *cache_slot = ce;
+ }
} else {
ce = zend_fetch_class(ZEND_TYPE_NAME(*list_type),
(ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
@@ -983,6 +1012,9 @@ static zend_always_inline zend_bool zend_check_type_slow(
if (HAVE_CACHE_SLOT) {
*cache_slot = ce;
}
+ if (ZEND_TYPE_HAS_CE_CACHE(*list_type)) {
+ ZEND_TYPE_SET_CE_CACHE(*list_type, ce);
+ }
}
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
return 1;
@@ -994,14 +1026,22 @@ static zend_always_inline zend_bool zend_check_type_slow(
} else {
if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) {
ce = (zend_class_entry *) *cache_slot;
+ } else if (ZEND_TYPE_HAS_CE_CACHE(*type) && ZEND_TYPE_CE_CACHE(*type)) {
+ ce = ZEND_TYPE_CE_CACHE(*type);
+ if (HAVE_CACHE_SLOT) {
+ *cache_slot = ce;
+ }
} else {
- ce = zend_fetch_class(ZEND_TYPE_NAME(type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
+ ce = zend_fetch_class(ZEND_TYPE_NAME(*type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
if (UNEXPECTED(!ce)) {
goto builtin_types;
}
if (HAVE_CACHE_SLOT) {
*cache_slot = (void *) ce;
}
+ if (ZEND_TYPE_HAS_CE_CACHE(*type)) {
+ ZEND_TYPE_SET_CE_CACHE(*type, ce);
+ }
}
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
return 1;
@@ -1010,7 +1050,7 @@ static zend_always_inline zend_bool zend_check_type_slow(
}
builtin_types:
- type_mask = ZEND_TYPE_FULL_MASK(type);
+ type_mask = ZEND_TYPE_FULL_MASK(*type);
if ((type_mask & MAY_BE_CALLABLE) && zend_is_callable(arg, 0, NULL)) {
return 1;
}
@@ -1039,19 +1079,19 @@ builtin_types:
* because this case is already checked at compile-time. */
}
-static zend_always_inline zend_bool zend_check_type(
- zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope,
- zend_bool is_return_type, zend_bool is_internal)
+static zend_always_inline bool zend_check_type(
+ zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope,
+ bool is_return_type, bool is_internal)
{
zend_reference *ref = NULL;
- ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
+ ZEND_ASSERT(ZEND_TYPE_IS_SET(*type));
if (UNEXPECTED(Z_ISREF_P(arg))) {
ref = Z_REF_P(arg);
arg = Z_REFVAL_P(arg);
}
- if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
+ if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(*type, Z_TYPE_P(arg)))) {
return 1;
}
@@ -1066,7 +1106,7 @@ static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint
cur_arg_info = &zf->common.arg_info[arg_num-1];
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
- && UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
+ && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, cur_arg_info, arg_num, arg);
return 0;
}
@@ -1078,7 +1118,7 @@ static zend_always_inline bool zend_verify_variadic_arg_type(
zend_function *zf, zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
- if (UNEXPECTED(!zend_check_type(arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
+ if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, arg_info, arg_num, arg);
return 0;
}
@@ -1103,7 +1143,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
}
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
- && UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, /* cache_slot */ NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
+ && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, /* cache_slot */ NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
return 0;
}
arg++;
@@ -1115,7 +1155,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
/* Determine whether an internal call should throw, because the passed arguments violate
* an arginfo constraint. This is only checked in debug builds. In release builds, we
* trust that arginfo matches what is enforced by zend_parse_parameters. */
-static zend_always_inline zend_bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call)
+static zend_always_inline bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call)
{
if (fbc->internal_function.handler == ZEND_FN(pass)) {
/* Be lenient about the special pass function. */
@@ -1236,7 +1276,7 @@ static bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
return 1;
}
- if (UNEXPECTED(!zend_check_type(ret_info->type, ret, /* cache_slot */ NULL, NULL, 1, /* is_internal */ 1))) {
+ if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, /* cache_slot */ NULL, NULL, 1, /* is_internal */ 1))) {
zend_verify_internal_return_error(zf, ret);
return 0;
}
@@ -2100,31 +2140,7 @@ num_undef:
}
str_index:
retval = zend_hash_find_ex(ht, offset_key, ZEND_CONST_COND(dim_type == IS_CONST, 0));
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- switch (type) {
- case BP_VAR_R:
- zend_undefined_index(offset_key);
- /* break missing intentionally */
- case BP_VAR_UNSET:
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- if (UNEXPECTED(zend_undefined_index_write(ht, offset_key))) {
- return NULL;
- }
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- }
- }
- }
- } else {
+ if (!retval) {
switch (type) {
case BP_VAR_R:
zend_undefined_index(offset_key);
@@ -2478,7 +2494,7 @@ num_idx:
return zend_hash_index_find(ht, hval);
} else if (Z_TYPE_P(offset) == IS_NULL) {
str_idx:
- return zend_hash_find_ex_ind(ht, ZSTR_EMPTY_ALLOC(), 1);
+ return zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
} else if (Z_TYPE_P(offset) == IS_FALSE) {
hval = 0;
goto num_idx;
@@ -2576,7 +2592,7 @@ str_offset:
}
}
-static zend_never_inline zend_bool ZEND_FASTCALL zend_array_key_exists_fast(HashTable *ht, zval *key OPLINE_DC EXECUTE_DATA_DC)
+static zend_never_inline bool ZEND_FASTCALL zend_array_key_exists_fast(HashTable *ht, zval *key OPLINE_DC EXECUTE_DATA_DC)
{
zend_string *str;
zend_ulong hval;
@@ -2588,11 +2604,11 @@ try_again:
goto num_key;
}
str_key:
- return zend_hash_find_ind(ht, str) != NULL;
+ return zend_hash_exists(ht, str);
} else if (EXPECTED(Z_TYPE_P(key) == IS_LONG)) {
hval = Z_LVAL_P(key);
num_key:
- return zend_hash_index_find(ht, hval) != NULL;
+ return zend_hash_index_exists(ht, hval);
} else if (EXPECTED(Z_ISREF_P(key))) {
key = Z_REFVAL_P(key);
goto try_again;
@@ -2636,12 +2652,12 @@ static ZEND_COLD void ZEND_FASTCALL zend_array_key_exists_error(
}
}
-static zend_always_inline zend_bool promotes_to_array(zval *val) {
+static zend_always_inline bool promotes_to_array(zval *val) {
return Z_TYPE_P(val) <= IS_FALSE
|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE);
}
-static zend_always_inline zend_bool check_type_array_assignable(zend_type type) {
+static zend_always_inline bool check_type_array_assignable(zend_type type) {
if (!ZEND_TYPE_IS_SET(type)) {
return 1;
}
@@ -2649,7 +2665,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type)
}
/* Checks whether an array can be assigned to the reference. Throws error if not assignable. */
-ZEND_API zend_bool zend_verify_ref_array_assignable(zend_reference *ref) {
+ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref) {
zend_property_info *prop;
ZEND_ASSERT(ZEND_REF_HAS_TYPE_SOURCES(ref));
ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
@@ -2677,7 +2693,7 @@ static zend_property_info *zend_object_fetch_property_type_info(
return zend_get_typed_property_info_for_slot(obj, slot);
}
-static zend_never_inline zend_bool zend_handle_fetch_obj_flags(
+static zend_never_inline bool zend_handle_fetch_obj_flags(
zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags)
{
switch (flags) {
@@ -2722,7 +2738,7 @@ static zend_never_inline zend_bool zend_handle_fetch_obj_flags(
return 1;
}
-static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags, zend_bool init_undef OPLINE_DC EXECUTE_DATA_DC)
+static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags, bool init_undef OPLINE_DC EXECUTE_DATA_DC)
{
zval *ptr;
zend_object *zobj;
@@ -3063,7 +3079,7 @@ ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info
/* 1: valid, 0: invalid, -1: may be valid after type coercion */
static zend_always_inline int i_zend_verify_type_assignable_zval(
- zend_property_info *info, zval *zv, zend_bool strict) {
+ zend_property_info *info, zval *zv, bool strict) {
zend_type type = info->type;
uint32_t type_mask;
zend_uchar zv_type = Z_TYPE_P(zv);
@@ -3106,7 +3122,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval(
return -1;
}
-ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict)
+ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict)
{
zend_property_info *prop;
@@ -3185,9 +3201,9 @@ static zend_always_inline void i_zval_ptr_dtor_noref(zval *zval_ptr) {
}
}
-ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, zend_uchar value_type, zend_bool strict)
+ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, zend_uchar value_type, bool strict)
{
- zend_bool ret;
+ bool ret;
zval value;
zend_refcounted *ref = NULL;
@@ -3218,7 +3234,7 @@ ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, ze
return variable_ptr;
}
-ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict) {
+ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, bool strict) {
zval *val = orig_val;
if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) {
int result;
@@ -3526,7 +3542,7 @@ static zend_always_inline void zend_init_cvs(uint32_t first, uint32_t last EXECU
}
}
-static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, zend_bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */
+static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */
{
uint32_t first_extra_arg, num_args;
ZEND_ASSERT(EX(func) == (zend_function*)op_array);
@@ -4271,11 +4287,11 @@ already_compiled:
}
/* }}} */
-static zend_never_inline zend_bool ZEND_FASTCALL zend_fe_reset_iterator(zval *array_ptr, int by_ref OPLINE_DC EXECUTE_DATA_DC) /* {{{ */
+static zend_never_inline bool ZEND_FASTCALL zend_fe_reset_iterator(zval *array_ptr, int by_ref OPLINE_DC EXECUTE_DATA_DC) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(array_ptr);
zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, by_ref);
- zend_bool is_empty;
+ bool is_empty;
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (iter) {
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index ab96d43c4c..3339f3992d 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -43,7 +43,7 @@ ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value);
ZEND_API void execute_ex(zend_execute_data *execute_data);
ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value);
-ZEND_API zend_bool zend_is_valid_class_name(zend_string *name);
+ZEND_API bool zend_is_valid_class_name(zend_string *name);
ZEND_API zend_class_entry *zend_lookup_class(zend_string *name);
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags);
ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex);
@@ -60,21 +60,21 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc);
ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num);
-ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict);
-ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict);
+ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict);
+ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, bool strict);
ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv);
ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv);
ZEND_API ZEND_COLD zend_result ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht, zend_long lval);
ZEND_API ZEND_COLD zend_result ZEND_FASTCALL zend_undefined_index_write(HashTable *ht, zend_string *offset);
-ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg);
+ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
ZEND_API ZEND_COLD void zend_verify_arg_error(
const zend_function *zf, const zend_arg_info *arg_info, int arg_num, zval *value);
ZEND_API ZEND_COLD void zend_verify_return_error(
const zend_function *zf, zval *value);
-ZEND_API zend_bool zend_verify_ref_array_assignable(zend_reference *ref);
-ZEND_API zend_bool zend_value_instanceof_static(zval *zv);
+ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref);
+ZEND_API bool zend_value_instanceof_static(zval *zv);
#define ZEND_REF_TYPE_SOURCES(ref) \
@@ -92,7 +92,7 @@ ZEND_API zend_bool zend_value_instanceof_static(zval *zv);
ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop);
ZEND_API void ZEND_FASTCALL zend_ref_del_type_source(zend_property_info_source_list *source_list, zend_property_info *prop);
-ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict);
+ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, bool strict);
static zend_always_inline void zend_copy_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type)
{
@@ -121,7 +121,7 @@ static zend_always_inline void zend_copy_to_variable(zval *variable_ptr, zval *v
}
}
-static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict)
+static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, bool strict)
{
do {
if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
@@ -155,8 +155,8 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
return variable_ptr;
}
-ZEND_API zend_result zval_update_constant(zval *pp);
-ZEND_API zend_result zval_update_constant_ex(zval *pp, zend_class_entry *scope);
+ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp);
+ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *pp, zend_class_entry *scope);
/* dedicated Zend executor functions - do not use! */
struct _zend_vm_stack {
@@ -317,7 +317,7 @@ ZEND_API const char *zend_get_executed_filename(void);
ZEND_API zend_string *zend_get_executed_filename_ex(void);
ZEND_API uint32_t zend_get_executed_lineno(void);
ZEND_API zend_class_entry *zend_get_executed_scope(void);
-ZEND_API zend_bool zend_is_executing(void);
+ZEND_API bool zend_is_executing(void);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg_num);
ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals);
@@ -418,7 +418,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call);
#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS)
-ZEND_API zend_bool zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict);
+ZEND_API bool zend_verify_property_type(zend_property_info *info, zval *property, bool strict);
ZEND_COLD void zend_verify_property_type_error(zend_property_info *info, zval *property);
#define ZEND_REF_ADD_TYPE_SOURCE(ref, source) \
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index b55b2e7385..4e8c1d5a1f 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -258,9 +258,9 @@ void shutdown_executor(void) /* {{{ */
zend_string *key;
zval *zv;
#if ZEND_DEBUG
- zend_bool fast_shutdown = 0;
+ bool fast_shutdown = 0;
#else
- zend_bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup);
+ bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup);
#endif
zend_try {
@@ -285,27 +285,33 @@ void shutdown_executor(void) /* {{{ */
if (op_array->type == ZEND_INTERNAL_FUNCTION) {
break;
}
- if (op_array->static_variables) {
+ if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
if (ht) {
- zend_array_release(ht);
+ zend_array_destroy(ht);
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
}
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
zend_class_entry *ce = Z_PTR_P(zv);
+
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
}
+
+ if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) {
+ zend_cleanup_mutable_class_data(ce);
+ }
+
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) {
+ if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
if (ht) {
- zend_array_release(ht);
+ zend_array_destroy(ht);
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
}
}
@@ -604,13 +610,13 @@ ZEND_API zend_class_entry *zend_get_executed_scope(void) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_is_executing(void) /* {{{ */
+ZEND_API bool zend_is_executing(void) /* {{{ */
{
return EG(current_execute_data) != 0;
}
/* }}} */
-ZEND_API zend_result zval_update_constant_ex(zval *p, zend_class_entry *scope) /* {{{ */
+ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *p, zend_class_entry *scope) /* {{{ */
{
if (Z_TYPE_P(p) == IS_CONSTANT_AST) {
zend_ast *ast = Z_ASTVAL_P(p);
@@ -638,7 +644,7 @@ ZEND_API zend_result zval_update_constant_ex(zval *p, zend_class_entry *scope) /
}
/* }}} */
-ZEND_API zend_result zval_update_constant(zval *pp) /* {{{ */
+ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp) /* {{{ */
{
return zval_update_constant_ex(pp, EG(current_execute_data) ? zend_get_executed_scope() : CG(active_class_entry));
}
@@ -759,7 +765,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
for (i=0; i<fci->param_count; i++) {
zval *param = ZEND_CALL_ARG(call, i+1);
zval *arg = &fci->params[i];
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
if (UNEXPECTED(Z_ISUNDEF_P(arg))) {
/* Allow forwarding undef slots. This is only used by Closure::__invoke(). */
ZVAL_UNDEF(param);
@@ -806,9 +812,9 @@ cleanup_args:
zend_string *name;
zval *arg;
uint32_t arg_num = ZEND_CALL_NUM_ARGS(call) + 1;
- zend_bool have_named_params = 0;
+ bool have_named_params = 0;
ZEND_HASH_FOREACH_STR_KEY_VAL(fci->named_params, name, arg) {
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
zval *target;
if (name) {
void *cache_slot[2] = {NULL, NULL};
@@ -1017,7 +1023,7 @@ static const uint32_t valid_chars[8] = {
0xffffffff,
};
-ZEND_API zend_bool zend_is_valid_class_name(zend_string *name) {
+ZEND_API bool zend_is_valid_class_name(zend_string *name) {
for (size_t i = 0; i < ZSTR_LEN(name); i++) {
unsigned char c = ZSTR_VAL(name)[i];
if (!ZEND_BIT_TEST(valid_chars, c)) {
@@ -1059,7 +1065,15 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) ||
((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) &&
(ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) {
- ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
+ if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+ if (!CG(unlinked_uses)) {
+ ALLOC_HASHTABLE(CG(unlinked_uses));
+ zend_hash_init(CG(unlinked_uses), 0, NULL, NULL, 0);
+ }
+ zend_hash_index_add_empty_element(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce);
+ } else {
+ ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
+ }
return ce;
}
return NULL;
@@ -1210,6 +1224,7 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re
}
EG(no_extensions)=0;
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
retval = SUCCESS;
diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h
index 9f96d3b0fa..57c8775092 100644
--- a/Zend/zend_extensions.h
+++ b/Zend/zend_extensions.h
@@ -44,11 +44,11 @@ You can use the following macro to check the extension API version for compatibi
/* The first number is the engine version and the rest is the date (YYYYMMDD).
* This way engine 2/3 API no. is always greater than engine 1 API no.. */
-#define ZEND_EXTENSION_API_NO 420200930
+#define ZEND_EXTENSION_API_NO 420201009
typedef struct _zend_extension_version_info {
int zend_extension_api_no;
- char *build_id;
+ const char *build_id;
} zend_extension_version_info;
#define ZEND_EXTENSION_BUILD_ID "API" ZEND_TOSTR(ZEND_EXTENSION_API_NO) ZEND_BUILD_TS ZEND_BUILD_DEBUG ZEND_BUILD_SYSTEM ZEND_BUILD_EXTRA
@@ -75,11 +75,11 @@ typedef size_t (*op_array_persist_calc_func_t)(zend_op_array *op_array);
typedef size_t (*op_array_persist_func_t)(zend_op_array *op_array, void *mem);
struct _zend_extension {
- char *name;
- char *version;
- char *author;
- char *URL;
- char *copyright;
+ const char *name;
+ const char *version;
+ const char *author;
+ const char *URL;
+ const char *copyright;
startup_func_t startup;
shutdown_func_t shutdown;
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index bfc124719c..7a9dc4dd61 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -209,10 +209,10 @@ typedef struct _gc_root_buffer {
typedef struct _zend_gc_globals {
gc_root_buffer *buf; /* preallocated arrays of buffers */
- zend_bool gc_enabled;
- zend_bool gc_active; /* GC currently running, forbid nested GC */
- zend_bool gc_protected; /* GC protected, forbid root additions */
- zend_bool gc_full;
+ bool gc_enabled;
+ bool gc_active; /* GC currently running, forbid nested GC */
+ bool gc_protected; /* GC protected, forbid root additions */
+ bool gc_full;
uint32_t unused; /* linked list of unused buffers */
uint32_t first_unused; /* first unused buffer */
@@ -493,9 +493,9 @@ void gc_reset(void)
}
}
-ZEND_API zend_bool gc_enable(zend_bool enable)
+ZEND_API bool gc_enable(bool enable)
{
- zend_bool old_enabled = GC_G(gc_enabled);
+ bool old_enabled = GC_G(gc_enabled);
GC_G(gc_enabled) = enable;
if (enable && !old_enabled && GC_G(buf) == NULL) {
GC_G(buf) = (gc_root_buffer*) pemalloc(sizeof(gc_root_buffer) * GC_DEFAULT_BUF_SIZE, 1);
@@ -507,19 +507,19 @@ ZEND_API zend_bool gc_enable(zend_bool enable)
return old_enabled;
}
-ZEND_API zend_bool gc_enabled(void)
+ZEND_API bool gc_enabled(void)
{
return GC_G(gc_enabled);
}
-ZEND_API zend_bool gc_protect(zend_bool protect)
+ZEND_API bool gc_protect(bool protect)
{
- zend_bool old_protected = GC_G(gc_protected);
+ bool old_protected = GC_G(gc_protected);
GC_G(gc_protected) = protect;
return old_protected;
}
-ZEND_API zend_bool gc_protected(void)
+ZEND_API bool gc_protected(void)
{
return GC_G(gc_protected);
}
@@ -740,11 +740,8 @@ tail_call:
goto next;
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
- if ((zend_array*)ref != &EG(symbol_table)) {
- ht = (zend_array*)ref;
- } else {
- goto next;
- }
+ ZEND_ASSERT((zend_array*)ref != &EG(symbol_table));
+ ht = (zend_array*)ref;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
@@ -861,12 +858,8 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
goto next;
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
- if (((zend_array*)ref) == &EG(symbol_table)) {
- GC_REF_SET_BLACK(ref);
- goto next;
- } else {
- ht = (zend_array*)ref;
- }
+ ZEND_ASSERT(((zend_array*)ref) != &EG(symbol_table));
+ ht = (zend_array*)ref;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
@@ -1046,12 +1039,8 @@ tail_call:
goto next;
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
- if ((zend_array*)ref == &EG(symbol_table)) {
- GC_REF_SET_BLACK(ref);
- goto next;
- } else {
- ht = (zend_array*)ref;
- }
+ ZEND_ASSERT((zend_array*)ref != &EG(symbol_table));
+ ht = (zend_array*)ref;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h
index f44786425f..3221335733 100644
--- a/Zend/zend_gc.h
+++ b/Zend/zend_gc.h
@@ -35,12 +35,12 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref);
ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref);
/* enable/disable automatic start of GC collection */
-ZEND_API zend_bool gc_enable(zend_bool enable);
-ZEND_API zend_bool gc_enabled(void);
+ZEND_API bool gc_enable(bool enable);
+ZEND_API bool gc_enabled(void);
/* enable/disable possible root additions */
-ZEND_API zend_bool gc_protect(zend_bool protect);
-ZEND_API zend_bool gc_protected(void);
+ZEND_API bool gc_protect(bool protect);
+ZEND_API bool gc_protected(void);
/* The default implementation of the gc_collect_cycles callback. */
ZEND_API int zend_gc_collect_cycles(void);
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index 68c1865c00..df94446d6f 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -115,7 +115,7 @@ static void zend_generator_cleanup_unfinished_execution(
}
/* }}} */
-ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution) /* {{{ */
+ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution) /* {{{ */
{
if (EXPECTED(generator->execute_data)) {
zend_execute_data *execute_data = generator->execute_data;
@@ -165,14 +165,14 @@ static void zend_generator_remove_child(zend_generator_node *node, zend_generato
{
ZEND_ASSERT(node->children >= 1);
if (node->children == 1) {
- node->child.single.child = NULL;
+ node->child.single = NULL;
} else {
HashTable *ht = node->child.ht;
zend_hash_index_del(ht, (zend_ulong) child);
if (node->children == 2) {
zend_generator *other_child;
ZEND_HASH_FOREACH_PTR(ht, other_child) {
- node->child.single.child = other_child;
+ node->child.single = other_child;
break;
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(ht);
@@ -479,13 +479,13 @@ static void zend_generator_add_child(zend_generator *generator, zend_generator *
zend_generator_node *node = &generator->node;
if (node->children == 0) {
- node->child.single.child = child;
+ node->child.single = child;
} else {
if (node->children == 1) {
HashTable *ht = emalloc(sizeof(HashTable));
zend_hash_init(ht, 0, NULL, NULL, 0);
zend_hash_index_add_new_ptr(ht,
- (zend_ulong) node->child.single.child, node->child.single.child);
+ (zend_ulong) node->child.single, node->child.single);
node->child.ht = ht;
}
@@ -524,7 +524,7 @@ ZEND_API zend_generator *zend_generator_update_root(zend_generator *generator)
static zend_generator *get_new_root(zend_generator *generator, zend_generator *root)
{
while (!root->execute_data && root->node.children == 1) {
- root = root->node.child.single.child;
+ root = root->node.child.single;
}
if (root->execute_data) {
@@ -614,9 +614,6 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener
p = &ht->arData[pos];
value = &p->val;
- if (Z_TYPE_P(value) == IS_INDIRECT) {
- value = Z_INDIRECT_P(value);
- }
pos++;
} while (Z_ISUNDEF_P(value));
@@ -1115,17 +1112,11 @@ zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *ob
void zend_register_generator_ce(void) /* {{{ */
{
- zend_class_entry ce;
-
- INIT_CLASS_ENTRY(ce, "Generator", class_Generator_methods);
- zend_ce_generator = zend_register_internal_class(&ce);
- zend_ce_generator->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+ zend_ce_generator = register_class_Generator(zend_ce_iterator);
zend_ce_generator->create_object = zend_generator_create;
zend_ce_generator->serialize = zend_class_serialize_deny;
zend_ce_generator->unserialize = zend_class_unserialize_deny;
-
/* get_iterator has to be assigned *after* implementing the interface */
- zend_class_implements(zend_ce_generator, 1, zend_ce_iterator);
zend_ce_generator->get_iterator = zend_generator_get_iterator;
memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers));
@@ -1135,7 +1126,6 @@ void zend_register_generator_ce(void) /* {{{ */
zend_generator_handlers.clone_obj = NULL;
zend_generator_handlers.get_constructor = zend_generator_get_constructor;
- INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL);
- zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_ce_exception);
+ zend_ce_ClosedGeneratorException = register_class_ClosedGeneratorException(zend_ce_exception);
}
/* }}} */
diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h
index 35ff09c99d..17b25a99b8 100644
--- a/Zend/zend_generators.h
+++ b/Zend/zend_generators.h
@@ -41,10 +41,7 @@ struct _zend_generator_node {
uint32_t children;
union {
HashTable *ht; /* if multiple children */
- struct { /* if one child */
- zend_generator *leaf; /* TODO: Unused, remove. */
- zend_generator *child;
- } single;
+ zend_generator *single; /* if one child */
} child;
/* One generator can cache a direct pointer to the current root.
* The leaf member points back to the generator using the root cache. */
@@ -97,7 +94,7 @@ static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8;
void zend_register_generator_ce(void);
-ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution);
+ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution);
ZEND_API void zend_generator_resume(zend_generator *generator);
ZEND_API void zend_generator_restore_call_stack(zend_generator *generator);
diff --git a/Zend/zend_generators.stub.php b/Zend/zend_generators.stub.php
index 751b328d4c..538596213a 100644
--- a/Zend/zend_generators.stub.php
+++ b/Zend/zend_generators.stub.php
@@ -1,7 +1,8 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
+/** @strict-properties */
final class Generator implements Iterator
{
public function rewind(): void {}
diff --git a/Zend/zend_generators_arginfo.h b/Zend/zend_generators_arginfo.h
index 2b6f22f9f5..17a82b1f4b 100644
--- a/Zend/zend_generators_arginfo.h
+++ b/Zend/zend_generators_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 50044c6c8d2a162a988906c0b752a5fe28adb933 */
+ * Stub hash: 06d4e8126db48fe8633ecd40b93904a0f9c59263 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Generator_rewind, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
@@ -51,3 +51,25 @@ static const zend_function_entry class_Generator_methods[] = {
static const zend_function_entry class_ClosedGeneratorException_methods[] = {
ZEND_FE_END
};
+
+static zend_class_entry *register_class_Generator(zend_class_entry *class_entry_Iterator)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Generator", class_Generator_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+ zend_class_implements(class_entry, 1, class_entry_Iterator);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ClosedGeneratorException(zend_class_entry *class_entry_Exception)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", class_ClosedGeneratorException_methods);
+ class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception);
+
+ return class_entry;
+}
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index e0e8ac7700..e9b24fc0e3 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -81,19 +81,23 @@ struct _zend_compiler_globals {
/* Refer to zend_yytnamerr() in zend_language_parser.y for meaning of values */
zend_uchar parse_error;
- zend_bool in_compilation;
- zend_bool short_tags;
+ bool in_compilation;
+ bool short_tags;
- zend_bool unclean_shutdown;
+ bool unclean_shutdown;
- zend_bool ini_parser_unbuffered_errors;
+ bool ini_parser_unbuffered_errors;
zend_llist open_files;
struct _zend_ini_parser_param *ini_parser_param;
- zend_bool skip_shebang;
- zend_bool increment_lineno;
+ bool skip_shebang;
+ bool increment_lineno;
+
+ bool variable_width_locale; /* UTF-8, Shift-JIS, Big5, ISO 2022, EUC, etc */
+ bool ascii_compatible_locale; /* locale uses ASCII characters as singletons */
+ /* and don't use them as lead/trail units */
zend_string *doc_comment;
uint32_t extra_fn_flags;
@@ -109,9 +113,9 @@ struct _zend_compiler_globals {
const zend_encoding **script_encoding_list;
size_t script_encoding_list_size;
- zend_bool multibyte;
- zend_bool detect_unicode;
- zend_bool encoding_declared;
+ bool multibyte;
+ bool detect_unicode;
+ bool encoding_declared;
zend_ast *ast;
zend_arena *ast_arena;
@@ -126,6 +130,8 @@ struct _zend_compiler_globals {
HashTable *delayed_variance_obligations;
HashTable *delayed_autoloads;
+ HashTable *unlinked_uses;
+ zend_class_entry *current_linking_class;
uint32_t rtd_key_counter;
@@ -176,13 +182,13 @@ struct _zend_executor_globals {
uint32_t persistent_classes_count;
HashTable *in_autoload;
- zend_bool full_tables_cleanup;
+ bool full_tables_cleanup;
/* for extended information support */
- zend_bool no_extensions;
+ bool no_extensions;
- zend_bool vm_interrupt;
- zend_bool timed_out;
+ bool vm_interrupt;
+ bool timed_out;
zend_long hard_timeout;
#ifdef ZEND_WIN32
@@ -205,7 +211,7 @@ struct _zend_executor_globals {
/* timeout support */
zend_long timeout_seconds;
- int lambda_count;
+ int capture_warnings_during_sccp;
HashTable *ini_directives;
HashTable *modified_ini_directives;
@@ -218,7 +224,7 @@ struct _zend_executor_globals {
struct _zend_module_entry *current_module;
- zend_bool active;
+ bool active;
zend_uchar flags;
zend_long assertions;
@@ -238,7 +244,7 @@ struct _zend_executor_globals {
HashTable weakrefs;
- zend_bool exception_ignore_args;
+ bool exception_ignore_args;
zend_long exception_string_param_max_len;
zend_get_gc_buffer get_gc_buffer;
@@ -291,9 +297,9 @@ struct _zend_php_scanner_globals {
zend_stack state_stack;
zend_ptr_stack heredoc_label_stack;
zend_stack nest_location_stack; /* for syntax error reporting */
- zend_bool heredoc_scan_ahead;
+ bool heredoc_scan_ahead;
int heredoc_indentation;
- zend_bool heredoc_indentation_uses_spaces;
+ bool heredoc_indentation_uses_spaces;
/* original (unfiltered) script */
unsigned char *script_org;
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index a3b62160dc..1f83a889e7 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -246,7 +246,7 @@ ZEND_API const HashTable zend_empty_array = {
.pDestructor = ZVAL_PTR_DTOR
};
-static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
+static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
{
GC_SET_REFCOUNT(ht, 1);
GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);
@@ -261,7 +261,7 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize
ht->nTableSize = zend_hash_check_size(nSize);
}
-ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
+ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
{
_zend_hash_init_int(ht, nSize, pDestructor, persistent);
}
@@ -310,7 +310,7 @@ static void ZEND_FASTCALL zend_hash_packed_grow(HashTable *ht)
HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), HT_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
}
-ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed)
+ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed)
{
IS_CONSISTENT(ht);
@@ -365,7 +365,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
}
-ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed)
+ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed)
{
HT_ASSERT_RC1(ht);
if (nSize == 0) return;
@@ -636,7 +636,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosit
}
}
-static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key, zend_bool known_hash)
+static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key, bool known_hash)
{
zend_ulong h;
uint32_t nIndex;
@@ -2145,7 +2145,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
}
-ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, zend_bool overwrite)
+ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite)
{
uint32_t idx;
Bucket *p;
@@ -2203,7 +2203,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source
}
-static zend_bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
+static bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
{
zend_hash_key hash_key;
@@ -2494,7 +2494,7 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q)
q->h = h;
}
-ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, zend_bool renumber)
+ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
{
Bucket *p;
uint32_t i, j;
@@ -2566,7 +2566,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, b
}
}
-static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered) {
+static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered) {
uint32_t idx1, idx2;
if (ht1->nNumOfElements != ht2->nNumOfElements) {
@@ -2644,7 +2644,7 @@ static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *
return 0;
}
-ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered)
+ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered)
{
int result;
IS_CONSISTENT(ht1);
@@ -2803,7 +2803,7 @@ convert:
* a "symtable" (contains integer and non-numeric string keys).
* If the proptable didn't need duplicating, its refcount is incremented.
*/
-ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, zend_bool always_duplicate)
+ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate)
{
zend_ulong num_key;
zend_string *str_key;
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index ced21a2ca1..64a9220573 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -92,24 +92,24 @@ typedef struct _zend_hash_key {
zend_string *key;
} zend_hash_key;
-typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam);
+typedef bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam);
BEGIN_EXTERN_C()
/* startup/shutdown */
-ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
+ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent);
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht);
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
_zend_hash_init((ht), (nSize), (pDestructor), (persistent))
-ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed);
+ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed);
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht);
-ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed);
+ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed);
ZEND_API void ZEND_FASTCALL zend_hash_discard(HashTable *ht, uint32_t nNumUsed);
/* additions/updates/changes */
@@ -178,7 +178,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulo
/* The same as zend_hash_find(), but hash value of the key must be already calculated */
ZEND_API zval* ZEND_FASTCALL _zend_hash_find_known_hash(const HashTable *ht, zend_string *key);
-static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_string *key, zend_bool known_hash)
+static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_string *key, bool known_hash)
{
if (known_hash) {
return _zend_hash_find_known_hash(ht, key);
@@ -207,17 +207,17 @@ static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_stri
/* Misc */
-static zend_always_inline zend_bool zend_hash_exists(const HashTable *ht, zend_string *key)
+static zend_always_inline bool zend_hash_exists(const HashTable *ht, zend_string *key)
{
return zend_hash_find(ht, key) != NULL;
}
-static zend_always_inline zend_bool zend_hash_str_exists(const HashTable *ht, const char *str, size_t len)
+static zend_always_inline bool zend_hash_str_exists(const HashTable *ht, const char *str, size_t len)
{
return zend_hash_str_find(ht, str, len) != NULL;
}
-static zend_always_inline zend_bool zend_hash_index_exists(const HashTable *ht, zend_ulong h)
+static zend_always_inline bool zend_hash_index_exists(const HashTable *ht, zend_ulong h)
{
return zend_hash_index_find(ht, h) != NULL;
}
@@ -257,15 +257,15 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Ha
/* Copying, merging and sorting */
ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor);
-ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, zend_bool overwrite);
+ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite);
ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam);
ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q);
ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q);
ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q);
typedef int (*bucket_compare_func_t)(Bucket *a, Bucket *b);
-ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered);
-ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, zend_bool renumber);
+ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered);
+ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, bucket_compare_func_t compar, uint32_t flag);
#define zend_hash_sort(ht, compare_func, renumber) \
@@ -303,7 +303,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source);
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht);
ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht);
-ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, zend_bool always_duplicate);
+ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate);
ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx);
@@ -386,7 +386,7 @@ static zend_always_inline zval *zend_hash_find_ind(const HashTable *ht, zend_str
}
-static zend_always_inline zval *zend_hash_find_ex_ind(const HashTable *ht, zend_string *key, zend_bool known_hash)
+static zend_always_inline zval *zend_hash_find_ex_ind(const HashTable *ht, zend_string *key, bool known_hash)
{
zval *zv;
@@ -839,7 +839,7 @@ static zend_always_inline void *zend_hash_find_ptr(const HashTable *ht, zend_str
}
}
-static zend_always_inline void *zend_hash_find_ex_ptr(const HashTable *ht, zend_string *key, zend_bool known_hash)
+static zend_always_inline void *zend_hash_find_ex_ptr(const HashTable *ht, zend_string *key, bool known_hash)
{
zval *zv;
@@ -1188,6 +1188,33 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
ZEND_HASH_FILL_FINISH(); \
} while (0)
+/* Check if an array is a list */
+static zend_always_inline zend_bool zend_array_is_list(zend_array *array)
+{
+ zend_long expected_idx = 0;
+ zend_long num_idx;
+ zend_string* str_idx;
+ /* Empty arrays are lists */
+ if (zend_hash_num_elements(array) == 0) {
+ return 1;
+ }
+
+ /* Packed arrays are lists */
+ if (HT_IS_PACKED(array) && HT_IS_WITHOUT_HOLES(array)) {
+ return 1;
+ }
+
+ /* Check if the list could theoretically be repacked */
+ ZEND_HASH_FOREACH_KEY(array, num_idx, str_idx) {
+ if (str_idx != NULL || num_idx != expected_idx++) {
+ return 0;
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ return 1;
+}
+
+
static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string *key, zval *zv, bool interned)
{
uint32_t idx = ht->nNumUsed++;
diff --git a/Zend/zend_highlight.h b/Zend/zend_highlight.h
index c4f819d508..e54a339ec4 100644
--- a/Zend/zend_highlight.h
+++ b/Zend/zend_highlight.h
@@ -40,7 +40,7 @@ BEGIN_EXTERN_C()
ZEND_API void zend_highlight(zend_syntax_highlighter_ini *syntax_highlighter_ini);
ZEND_API void zend_strip(void);
ZEND_API zend_result highlight_file(const char *filename, zend_syntax_highlighter_ini *syntax_highlighter_ini);
-ZEND_API void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name);
+ZEND_API void highlight_string(zend_string *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name);
ZEND_API void zend_html_putc(char c);
ZEND_API void zend_html_puts(const char *s, size_t len);
END_EXTERN_C()
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 4b161692af..75f5536eaa 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -27,6 +27,9 @@
#include "zend_operators.h"
#include "zend_exceptions.h"
+ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL;
+ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL;
+
static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
static void add_compatibility_obligation(
zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope,
@@ -35,7 +38,7 @@ static void add_property_compatibility_obligation(
zend_class_entry *ce, const zend_property_info *child_prop,
const zend_property_info *parent_prop);
-static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) {
+static void zend_type_copy_ctor(zend_type *type, bool persistent) {
if (ZEND_TYPE_HAS_LIST(*type)) {
zend_type_list *old_list = ZEND_TYPE_LIST(*type);
size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types);
@@ -86,32 +89,14 @@ static zend_function *zend_duplicate_internal_function(zend_function *func, zend
static zend_function *zend_duplicate_user_function(zend_function *func) /* {{{ */
{
- zend_function *new_function;
-
- new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
- memcpy(new_function, func, sizeof(zend_op_array));
-
- if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
- ZEND_ASSERT(new_function->op_array.fn_flags & ZEND_ACC_PRELOADED);
- ZEND_MAP_PTR_NEW(new_function->op_array.static_variables_ptr);
- } else {
- ZEND_MAP_PTR_INIT(new_function->op_array.static_variables_ptr, &new_function->op_array.static_variables);
- }
-
- HashTable *static_properties_ptr = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr);
- if (static_properties_ptr) {
- /* See: Zend/tests/method_static_var.phpt */
- ZEND_MAP_PTR_SET(new_function->op_array.static_variables_ptr, static_properties_ptr);
- GC_TRY_ADDREF(static_properties_ptr);
- } else {
- GC_TRY_ADDREF(new_function->op_array.static_variables);
- }
-
- return new_function;
+ zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_op_array, func, sizeof(zend_op_array));
+ zend_init_static_variables_map_ptr(new_op_array);
+ return (zend_function *) new_op_array;
}
/* }}} */
-static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce, zend_bool is_interface) /* {{{ */
+static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce, bool is_interface) /* {{{ */
{
if (UNEXPECTED(func->type == ZEND_INTERNAL_FUNCTION)) {
return zend_duplicate_internal_function(func, ce);
@@ -229,7 +214,7 @@ static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *nam
}
}
-static zend_bool class_visible(zend_class_entry *ce) {
+static bool class_visible(zend_class_entry *ce) {
if (ce->type == ZEND_INTERNAL_CLASS) {
return !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES);
} else {
@@ -240,7 +225,7 @@ static zend_bool class_visible(zend_class_entry *ce) {
}
static zend_class_entry *lookup_class(
- zend_class_entry *scope, zend_string *name, zend_bool register_unresolved) {
+ zend_class_entry *scope, zend_string *name, bool register_unresolved) {
uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD;
zend_class_entry *ce = zend_lookup_class_ex(name, NULL, flags);
if (!CG(in_compilation)) {
@@ -271,7 +256,7 @@ static zend_class_entry *lookup_class(
}
/* Instanceof that's safe to use on unlinked classes. */
-static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
+static bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
if (ce1 == ce2) {
return 1;
}
@@ -322,7 +307,7 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
return 0;
}
-static zend_bool zend_type_contains_traversable(zend_type type) {
+static bool zend_type_contains_traversable(zend_type type) {
zend_type *single_type;
if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) {
return 1;
@@ -337,7 +322,7 @@ static zend_bool zend_type_contains_traversable(zend_type type) {
return 0;
}
-static zend_bool zend_type_permits_self(
+static bool zend_type_permits_self(
zend_type type, zend_class_entry *scope, zend_class_entry *self) {
if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) {
return 1;
@@ -368,11 +353,52 @@ typedef enum {
INHERITANCE_SUCCESS = 1,
} inheritance_status;
+
+static void track_class_dependency(zend_class_entry *ce, zend_string *class_name)
+{
+ HashTable *ht;
+
+ if (!CG(current_linking_class) || ce == CG(current_linking_class)) {
+ return;
+ } else if (!class_name) {
+ class_name = ce->name;
+ } else if (zend_string_equals_literal_ci(class_name, "self")
+ || zend_string_equals_literal_ci(class_name, "parent")) {
+ return;
+ }
+
+ ht = (HashTable*)CG(current_linking_class)->inheritance_cache;
+
+#ifndef ZEND_WIN32
+ if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+#else
+ if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+#endif
+ // TODO: dependency on not-immutable class ???
+ if (ht) {
+ zend_hash_destroy(ht);
+ FREE_HASHTABLE(ht);
+ CG(current_linking_class)->inheritance_cache = NULL;
+ }
+ CG(current_linking_class)->ce_flags &= ~ZEND_ACC_CACHEABLE;
+ CG(current_linking_class) = NULL;
+ return;
+ }
+
+ /* Record dependency */
+ if (!ht) {
+ ALLOC_HASHTABLE(ht);
+ zend_hash_init(ht, 0, NULL, NULL, 0);
+ CG(current_linking_class)->inheritance_cache = (zend_inheritance_cache_entry*)ht;
+ }
+ zend_hash_add_ptr(ht, class_name, ce);
+}
+
static inheritance_status zend_perform_covariant_class_type_check(
zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce,
zend_class_entry *proto_scope, zend_type proto_type,
- zend_bool register_unresolved) {
- zend_bool have_unresolved = 0;
+ bool register_unresolved) {
+ bool have_unresolved = 0;
if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_OBJECT) {
/* Currently, any class name would be allowed here. We still perform a class lookup
* for forward-compatibility reasons, as we may have named types in the future that
@@ -381,6 +407,7 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce) {
have_unresolved = 1;
} else {
+ track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}
@@ -389,6 +416,7 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce) {
have_unresolved = 1;
} else if (unlinked_instanceof(fe_ce, zend_ce_traversable)) {
+ track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}
@@ -396,8 +424,9 @@ static inheritance_status zend_perform_covariant_class_type_check(
zend_type *single_type;
ZEND_TYPE_FOREACH(proto_type, single_type) {
zend_class_entry *proto_ce;
+ zend_string *proto_class_name = NULL;
if (ZEND_TYPE_HAS_NAME(*single_type)) {
- zend_string *proto_class_name =
+ proto_class_name =
resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type));
if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
return INHERITANCE_SUCCESS;
@@ -416,6 +445,8 @@ static inheritance_status zend_perform_covariant_class_type_check(
if (!fe_ce || !proto_ce) {
have_unresolved = 1;
} else if (unlinked_instanceof(fe_ce, proto_ce)) {
+ track_class_dependency(fe_ce, fe_class_name);
+ track_class_dependency(proto_ce, proto_class_name);
return INHERITANCE_SUCCESS;
}
} ZEND_TYPE_FOREACH_END();
@@ -458,7 +489,7 @@ static inheritance_status zend_perform_covariant_type_check(
}
zend_type *single_type;
- zend_bool all_success = 1;
+ bool all_success = 1;
/* First try to check whether we can succeed without resolving anything */
ZEND_TYPE_FOREACH(fe_type, single_type) {
@@ -540,7 +571,7 @@ static inheritance_status zend_do_perform_implementation_check(
{
uint32_t i, num_args, proto_num_args, fe_num_args;
inheritance_status status, local_status;
- zend_bool proto_is_variadic, fe_is_variadic;
+ bool proto_is_variadic, fe_is_variadic;
/* Checks for constructors only if they are declared in an interface,
* or explicitly marked as abstract
@@ -663,8 +694,12 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
}
if (fptr->common.scope) {
- /* cut off on NULL byte ... class@anonymous */
- smart_str_appendl(&str, ZSTR_VAL(fptr->common.scope->name), strlen(ZSTR_VAL(fptr->common.scope->name)));
+ if (fptr->common.scope->ce_flags & ZEND_ACC_ANON_CLASS) {
+ /* cut off on NULL byte ... class@anonymous */
+ smart_str_appends(&str, ZSTR_VAL(fptr->common.scope->name));
+ } else {
+ smart_str_appendl(&str, ZSTR_VAL(fptr->common.scope->name), ZSTR_LEN(fptr->common.scope->name));
+ }
smart_str_appends(&str, "::");
}
@@ -833,7 +868,7 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
zend_function *child, zend_class_entry *child_scope,
zend_function *parent, zend_class_entry *parent_scope,
zend_class_entry *ce, zval *child_zv,
- zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */
+ bool check_visibility, bool check_only, bool checked) /* {{{ */
{
uint32_t child_flags;
uint32_t parent_flags = parent->common.fn_flags;
@@ -943,12 +978,12 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
static zend_never_inline void do_inheritance_check_on_method(
zend_function *child, zend_class_entry *child_scope,
zend_function *parent, zend_class_entry *parent_scope,
- zend_class_entry *ce, zval *child_zv, zend_bool check_visibility)
+ zend_class_entry *ce, zval *child_zv, bool check_visibility)
{
do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0);
}
-static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, zend_bool is_interface, zend_bool checked) /* {{{ */
+static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, bool is_interface, bool checked) /* {{{ */
{
zval *child = zend_hash_find_ex(&ce->function_table, key, 1);
@@ -1139,6 +1174,12 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
} else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) {
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ if (ce->parent->ce_flags & ZEND_ACC_IMMUTABLE) {
+ c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
+ memcpy(c, parent_const, sizeof(zend_class_constant));
+ parent_const = c;
+ }
}
if (ce->type & ZEND_INTERNAL_CLASS) {
c = pemalloc(sizeof(zend_class_constant), 1);
@@ -1189,7 +1230,7 @@ void zend_build_properties_info_table(zend_class_entry *ce)
} ZEND_HASH_FOREACH_END();
}
-ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked) /* {{{ */
+ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */
{
zend_property_info *property_info;
zend_function *func;
@@ -1251,6 +1292,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
ZVAL_COPY_OR_DUP_PROP(dst, src);
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
}
continue;
} while (dst != end);
@@ -1261,6 +1303,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
ZVAL_COPY_PROP(dst, src);
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES;
}
continue;
} while (dst != end);
@@ -1323,6 +1366,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
}
if (Z_TYPE_P(Z_INDIRECT_P(dst)) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS;
}
} while (dst != end);
} else {
@@ -1412,7 +1456,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
}
/* }}} */
-static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
+static bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
{
zval *zv = zend_hash_find_ex(child_constants_table, name, 1);
zend_class_constant *old_constant;
@@ -1434,6 +1478,12 @@ static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c,
zend_class_constant *ct;
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ if (iface->ce_flags & ZEND_ACC_IMMUTABLE) {
+ ct = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
+ memcpy(ct, c, sizeof(zend_class_constant));
+ c = ct;
+ }
}
if (ce->type & ZEND_INTERNAL_CLASS) {
ct = pemalloc(sizeof(zend_class_constant), 1);
@@ -1549,11 +1599,13 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
}
}
- for (i = 0; i < ce->num_interfaces; i++) {
- zend_string_release_ex(ce->interface_names[i].name, 0);
- zend_string_release_ex(ce->interface_names[i].lc_name, 0);
+ if (!(ce->ce_flags & ZEND_ACC_CACHED)) {
+ for (i = 0; i < ce->num_interfaces; i++) {
+ zend_string_release_ex(ce->interface_names[i].name, 0);
+ zend_string_release_ex(ce->interface_names[i].lc_name, 0);
+ }
+ efree(ce->interface_names);
}
- efree(ce->interface_names);
ce->num_interfaces = num_interfaces;
ce->interfaces = interfaces;
@@ -1876,9 +1928,6 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
}
aliases[i] = trait;
-
- /* TODO: try to avoid this assignment (it's necessary only for reflection) */
- cur_method_ref->class_name = zend_string_copy(trait->name);
}
zend_string_release_ex(lcname, 0);
i++;
@@ -1952,7 +2001,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
zend_property_info *new_prop;
zend_string* prop_name;
const char* class_name_unused;
- zend_bool not_compatible;
+ bool not_compatible;
zval* prop_value;
uint32_t flags;
zend_string *doc_comment;
@@ -2074,37 +2123,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
}
/* }}} */
-static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
+static void zend_do_bind_traits(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
{
HashTable **exclude_tables;
zend_class_entry **aliases;
- zend_class_entry **traits, *trait;
- uint32_t i, j;
ZEND_ASSERT(ce->num_traits > 0);
- traits = emalloc(sizeof(zend_class_entry*) * ce->num_traits);
-
- for (i = 0; i < ce->num_traits; i++) {
- trait = zend_fetch_class_by_name(ce->trait_names[i].name,
- ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT);
- if (UNEXPECTED(trait == NULL)) {
- return;
- }
- if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) {
- zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
- return;
- }
- for (j = 0; j < i; j++) {
- if (traits[j] == trait) {
- /* skip duplications */
- trait = NULL;
- break;
- }
- }
- traits[i] = trait;
- }
-
/* complete initialization of trait strutures in ce */
zend_traits_init_trait_structures(ce, traits, &exclude_tables, &aliases);
@@ -2121,8 +2146,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
/* then flatten the properties into it, to, mostly to notfiy developer about problems */
zend_do_traits_property_binding(ce, traits);
-
- efree(traits);
}
/* }}} */
@@ -2162,7 +2185,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
{
zend_function *func;
zend_abstract_info ai;
- zend_bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
+ bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
memset(&ai, 0, sizeof(ai));
ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
@@ -2294,7 +2317,12 @@ static int check_variance_obligation(zval *zv) {
if (obligation->type == OBLIGATION_DEPENDENCY) {
zend_class_entry *dependency_ce = obligation->dependency_ce;
if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
+ zend_class_entry *orig_linking_class = CG(current_linking_class);
+
+ CG(current_linking_class) =
+ (dependency_ce->ce_flags & ZEND_ACC_CACHEABLE) ? dependency_ce : NULL;
resolve_delayed_variance_obligations(dependency_ce);
+ CG(current_linking_class) = orig_linking_class;
}
if (!(dependency_ce->ce_flags & ZEND_ACC_LINKED)) {
return ZEND_HASH_APPLY_KEEP;
@@ -2402,7 +2430,10 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) {
* to remove the class from the class table and throw an exception, because there is already
* a dependence on the inheritance hierarchy of this specific class. Instead we fall back to
* a fatal error, as would happen if we did not allow exceptions in the first place. */
- if (ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES) {
+ if ((ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES)
+ || ((ce->ce_flags & ZEND_ACC_IMMUTABLE)
+ && CG(unlinked_uses)
+ && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce) == SUCCESS)) {
zend_string *exception_str;
zval exception_zv;
ZEND_ASSERT(EG(exception) && "Exception must have been thrown");
@@ -2414,13 +2445,178 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) {
}
}
-ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name) /* {{{ */
+#define zend_update_inherited_handler(handler) do { \
+ if (ce->handler == (zend_function*)op_array) { \
+ ce->handler = (zend_function*)new_op_array; \
+ } \
+ } while (0)
+
+static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
+{
+ zend_class_entry *ce;
+ Bucket *p, *end;
+
+ ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
+ memcpy(ce, pce, sizeof(zend_class_entry));
+ ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
+ ce->refcount = 1;
+ ce->inheritance_cache = NULL;
+ ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
+
+ /* properties */
+ if (ce->default_properties_table) {
+ zval *dst = emalloc(sizeof(zval) * ce->default_properties_count);
+ zval *src = ce->default_properties_table;
+ zval *end = src + ce->default_properties_count;
+
+ ce->default_properties_table = dst;
+ for (; src != end; src++, dst++) {
+ ZVAL_COPY_VALUE_PROP(dst, src);
+ }
+ }
+
+ /* methods */
+ ce->function_table.pDestructor = ZEND_FUNCTION_DTOR;
+ if (!(HT_FLAGS(&ce->function_table) & HASH_FLAG_UNINITIALIZED)) {
+ p = emalloc(HT_SIZE(&ce->function_table));
+ memcpy(p, HT_GET_DATA_ADDR(&ce->function_table), HT_USED_SIZE(&ce->function_table));
+ HT_SET_DATA_ADDR(&ce->function_table, p);
+ p = ce->function_table.arData;
+ end = p + ce->function_table.nNumUsed;
+ for (; p != end; p++) {
+ zend_op_array *op_array, *new_op_array;
+ void ***run_time_cache_ptr;
+ size_t alloc_size;
+
+ op_array = Z_PTR(p->val);
+ ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
+ ZEND_ASSERT(op_array->scope == pce);
+ ZEND_ASSERT(op_array->prototype == NULL);
+ alloc_size = sizeof(zend_op_array) + sizeof(void *);
+ if (op_array->static_variables) {
+ alloc_size += sizeof(HashTable *);
+ }
+ new_op_array = zend_arena_alloc(&CG(arena), alloc_size);
+ Z_PTR(p->val) = new_op_array;
+ memcpy(new_op_array, op_array, sizeof(zend_op_array));
+ run_time_cache_ptr = (void***)(new_op_array + 1);
+ *run_time_cache_ptr = NULL;
+ new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
+ new_op_array->scope = ce;
+ ZEND_MAP_PTR_INIT(new_op_array->run_time_cache, run_time_cache_ptr);
+ if (op_array->static_variables) {
+ HashTable **static_variables_ptr = (HashTable **) (run_time_cache_ptr + 1);
+ *static_variables_ptr = NULL;
+ ZEND_MAP_PTR_INIT(new_op_array->static_variables_ptr, static_variables_ptr);
+ }
+
+ zend_update_inherited_handler(constructor);
+ zend_update_inherited_handler(destructor);
+ zend_update_inherited_handler(clone);
+ zend_update_inherited_handler(__get);
+ zend_update_inherited_handler(__set);
+ zend_update_inherited_handler(__call);
+ zend_update_inherited_handler(__isset);
+ zend_update_inherited_handler(__unset);
+ zend_update_inherited_handler(__tostring);
+ zend_update_inherited_handler(__callstatic);
+ zend_update_inherited_handler(__debugInfo);
+ zend_update_inherited_handler(__serialize);
+ zend_update_inherited_handler(__unserialize);
+ }
+ }
+
+ /* static members */
+ if (ce->default_static_members_table) {
+ zval *dst = emalloc(sizeof(zval) * ce->default_static_members_count);
+ zval *src = ce->default_static_members_table;
+ zval *end = src + ce->default_static_members_count;
+
+ ce->default_static_members_table = dst;
+ for (; src != end; src++, dst++) {
+ ZVAL_COPY_VALUE(dst, src);
+ }
+ }
+ ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
+
+ /* properties_info */
+ if (!(HT_FLAGS(&ce->properties_info) & HASH_FLAG_UNINITIALIZED)) {
+ p = emalloc(HT_SIZE(&ce->properties_info));
+ memcpy(p, HT_GET_DATA_ADDR(&ce->properties_info), HT_USED_SIZE(&ce->properties_info));
+ HT_SET_DATA_ADDR(&ce->properties_info, p);
+ p = ce->properties_info.arData;
+ end = p + ce->properties_info.nNumUsed;
+ for (; p != end; p++) {
+ zend_property_info *prop_info, *new_prop_info;
+
+ prop_info = Z_PTR(p->val);
+ ZEND_ASSERT(prop_info->ce == pce);
+ new_prop_info= zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
+ Z_PTR(p->val) = new_prop_info;
+ memcpy(new_prop_info, prop_info, sizeof(zend_property_info));
+ new_prop_info->ce = ce;
+ if (ZEND_TYPE_HAS_LIST(new_prop_info->type)) {
+ zend_type_list *new_list;
+ zend_type_list *list = ZEND_TYPE_LIST(new_prop_info->type);
+
+ new_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->num_types));
+ memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types));
+ ZEND_TYPE_SET_PTR(new_prop_info->type, list);
+ ZEND_TYPE_FULL_MASK(new_prop_info->type) |= _ZEND_TYPE_ARENA_BIT;
+ }
+ }
+ }
+
+ /* constants table */
+ if (!(HT_FLAGS(&ce->constants_table) & HASH_FLAG_UNINITIALIZED)) {
+ p = emalloc(HT_SIZE(&ce->constants_table));
+ memcpy(p, HT_GET_DATA_ADDR(&ce->constants_table), HT_USED_SIZE(&ce->constants_table));
+ HT_SET_DATA_ADDR(&ce->constants_table, p);
+ p = ce->constants_table.arData;
+ end = p + ce->constants_table.nNumUsed;
+ for (; p != end; p++) {
+ zend_class_constant *c, *new_c;
+
+ c = Z_PTR(p->val);
+ ZEND_ASSERT(c->ce == pce);
+ new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
+ Z_PTR(p->val) = new_c;
+ memcpy(new_c, c, sizeof(zend_class_constant));
+ new_c->ce = ce;
+ }
+ }
+
+ return ce;
+}
+
+#ifndef ZEND_WIN32
+# define UPDATE_IS_CACHEABLE(ce) do { \
+ if ((ce)->type == ZEND_USER_CLASS) { \
+ is_cacheable &= (ce)->ce_flags; \
+ } \
+ } while (0)
+#else
+// TODO: ASLR may cause different addresses in different workers ???
+# define UPDATE_IS_CACHEABLE(ce) do { \
+ is_cacheable &= (ce)->ce_flags; \
+ } while (0)
+#endif
+
+ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key) /* {{{ */
{
/* Load parent/interface dependencies first, so we can still gracefully abort linking
* with an exception and remove the class from the class table. This is only possible
* if no variance obligations on the current class have been added during autoloading. */
zend_class_entry *parent = NULL;
zend_class_entry **interfaces = NULL;
+ zend_class_entry **traits_and_interfaces = NULL;
+ zend_class_entry *proto = NULL;
+ zend_class_entry *orig_linking_class;
+ uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
+ uint32_t i, j;
+ zval *zv;
+
+ ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
if (ce->parent_name) {
parent = zend_fetch_class_by_name(
@@ -2428,13 +2624,43 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
if (!parent) {
check_unrecoverable_load_failure(ce);
- return FAILURE;
+ return NULL;
+ }
+ UPDATE_IS_CACHEABLE(parent);
+ }
+
+ if (ce->num_traits || ce->num_interfaces) {
+ traits_and_interfaces = emalloc(sizeof(zend_class_entry*) * (ce->num_traits + ce->num_interfaces));
+
+ for (i = 0; i < ce->num_traits; i++) {
+ zend_class_entry *trait = zend_fetch_class_by_name(ce->trait_names[i].name,
+ ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT);
+ if (UNEXPECTED(trait == NULL)) {
+ efree(traits_and_interfaces);
+ return NULL;
+ }
+ if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) {
+ zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
+ efree(traits_and_interfaces);
+ return NULL;
+ }
+ for (j = 0; j < i; j++) {
+ if (traits_and_interfaces[j] == trait) {
+ /* skip duplications */
+ trait = NULL;
+ break;
+ }
+ }
+ traits_and_interfaces[i] = trait;
+ if (trait) {
+ UPDATE_IS_CACHEABLE(trait);
+ }
}
}
if (ce->num_interfaces) {
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
- uint32_t i, num_parent_interfaces = parent ? parent->num_interfaces : 0;
+ uint32_t num_parent_interfaces = parent ? parent->num_interfaces : 0;
interfaces = emalloc(
sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces));
if (num_parent_interfaces) {
@@ -2449,12 +2675,61 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
if (!iface) {
check_unrecoverable_load_failure(ce);
efree(interfaces);
- return FAILURE;
+ efree(traits_and_interfaces);
+ return NULL;
}
interfaces[num_parent_interfaces + i] = iface;
+ traits_and_interfaces[ce->num_traits + i] = iface;
+ if (iface) {
+ UPDATE_IS_CACHEABLE(iface);
+ }
+ }
+ }
+
+
+ if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+ if (is_cacheable) {
+ if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
+ zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
+ if (ret) {
+ if (traits_and_interfaces) {
+ efree(traits_and_interfaces);
+ }
+ if (traits_and_interfaces) {
+ efree(interfaces);
+ }
+ zv = zend_hash_find_ex(CG(class_table), key, 1);
+ Z_CE_P(zv) = ret;
+ return ret;
+ }
+ } else {
+ is_cacheable = 0;
+ }
+ proto = ce;
+ }
+ /* Lazy class loading */
+ ce = zend_lazy_class_load(ce);
+ zv = zend_hash_find_ex(CG(class_table), key, 1);
+ Z_CE_P(zv) = ce;
+ if (CG(unlinked_uses)
+ && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) {
+ ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
+ }
+ } else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
+ /* Lazy class loading */
+ ce = zend_lazy_class_load(ce);
+ ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
+ zv = zend_hash_find_ex(CG(class_table), key, 1);
+ Z_CE_P(zv) = ce;
+ if (CG(unlinked_uses)
+ && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) {
+ ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES;
}
}
+ orig_linking_class = CG(current_linking_class);
+ CG(current_linking_class) = is_cacheable ? ce : NULL;
+
if (parent) {
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, parent);
@@ -2462,7 +2737,7 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
zend_do_inheritance(ce, parent);
}
if (ce->num_traits) {
- zend_do_bind_traits(ce);
+ zend_do_bind_traits(ce, traits_and_interfaces);
}
if (interfaces) {
zend_do_implement_interfaces(ce, interfaces);
@@ -2479,19 +2754,53 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa
if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
ce->ce_flags |= ZEND_ACC_LINKED;
- return SUCCESS;
+ } else {
+ ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
+ if (CG(current_linking_class)) {
+ ce->ce_flags |= ZEND_ACC_CACHEABLE;
+ }
+ load_delayed_classes();
+ if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
+ resolve_delayed_variance_obligations(ce);
+ if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
+ CG(current_linking_class) = orig_linking_class;
+ report_variance_errors(ce);
+ }
+ }
+ if (ce->ce_flags & ZEND_ACC_CACHEABLE) {
+ ce->ce_flags &= ~ZEND_ACC_CACHEABLE;
+ } else {
+ CG(current_linking_class) = NULL;
+ }
+ }
+
+ if (!CG(current_linking_class)) {
+ is_cacheable = 0;
}
+ CG(current_linking_class) = orig_linking_class;
+
+ if (is_cacheable) {
+ HashTable *ht = (HashTable*)ce->inheritance_cache;
+ zend_class_entry *new_ce;
- ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
- load_delayed_classes();
- if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
- resolve_delayed_variance_obligations(ce);
- if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
- report_variance_errors(ce);
+ ce->inheritance_cache = NULL;
+ new_ce = zend_inheritance_cache_add(ce, proto, parent, traits_and_interfaces, ht);
+ if (new_ce) {
+ zv = zend_hash_find_ex(CG(class_table), key, 1);
+ ce = new_ce;
+ Z_CE_P(zv) = ce;
}
+ if (ht) {
+ zend_hash_destroy(ht);
+ FREE_HASHTABLE(ht);
+ }
+ }
+
+ if (traits_and_interfaces) {
+ efree(traits_and_interfaces);
}
- return SUCCESS;
+ return ce;
}
/* }}} */
@@ -2540,21 +2849,66 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
}
/* }}} */
-zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
+zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
{
- inheritance_status status = zend_can_early_bind(ce, parent_ce);
+ inheritance_status status;
+ zend_class_entry *proto = NULL;
+ zend_class_entry *orig_linking_class;
+ uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
+
+ UPDATE_IS_CACHEABLE(parent_ce);
+ if (is_cacheable) {
+ if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
+ zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL);
+ if (ret) {
+ if (delayed_early_binding) {
+ if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
+ return NULL;
+ }
+ Z_CE_P(delayed_early_binding) = ret;
+ } else {
+ if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ret) == NULL)) {
+ return NULL;
+ }
+ }
+ return ret;
+ }
+ } else {
+ is_cacheable = 0;
+ }
+ proto = ce;
+ }
+ orig_linking_class = CG(current_linking_class);
+ CG(current_linking_class) = NULL;
+ status = zend_can_early_bind(ce, parent_ce);
+ CG(current_linking_class) = orig_linking_class;
if (EXPECTED(status != INHERITANCE_UNRESOLVED)) {
+ if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+ /* Lazy class loading */
+ ce = zend_lazy_class_load(ce);
+ } else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
+ /* Lazy class loading */
+ ce = zend_lazy_class_load(ce);
+ ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
+ }
+
if (delayed_early_binding) {
if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
- return 0;
+ return NULL;
}
+ Z_CE_P(delayed_early_binding) = ce;
} else {
if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) == NULL)) {
- return 0;
+ return NULL;
}
}
+
+ orig_linking_class = CG(current_linking_class);
+ CG(current_linking_class) = is_cacheable ? ce : NULL;
+
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
if (parent_ce && parent_ce->num_interfaces) {
zend_do_inherit_interfaces(ce, parent_ce);
@@ -2565,8 +2919,28 @@ zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce,
}
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
ce->ce_flags |= ZEND_ACC_LINKED;
- return 1;
+
+ CG(current_linking_class) = orig_linking_class;
+
+ if (is_cacheable) {
+ HashTable *ht = (HashTable*)ce->inheritance_cache;
+ zend_class_entry *new_ce;
+
+ ce->inheritance_cache = NULL;
+ new_ce = zend_inheritance_cache_add(ce, proto, parent_ce, NULL, ht);
+ if (new_ce) {
+ zval *zv = zend_hash_find_ex(CG(class_table), lcname, 1);
+ ce = new_ce;
+ Z_CE_P(zv) = ce;
+ }
+ if (ht) {
+ zend_hash_destroy(ht);
+ FREE_HASHTABLE(ht);
+ }
+ }
+
+ return ce;
}
- return 0;
+ return NULL;
}
/* }}} */
diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h
index e49ec49b6f..c67032f129 100644
--- a/Zend/zend_inheritance.h
+++ b/Zend/zend_inheritance.h
@@ -25,16 +25,19 @@
BEGIN_EXTERN_C()
ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface);
-ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked);
+ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked);
#define zend_do_inheritance(ce, parent_ce) \
zend_do_inheritance_ex(ce, parent_ce, 0)
-ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name);
+ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key);
void zend_verify_abstract_class(zend_class_entry *ce);
void zend_build_properties_info_table(zend_class_entry *ce);
-zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
+zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
+
+ZEND_API extern zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces);
+ZEND_API extern zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);
END_EXTERN_C()
diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c
index 75c7faf4c0..b3418b1152 100644
--- a/Zend/zend_ini.c
+++ b/Zend/zend_ini.c
@@ -311,7 +311,7 @@ ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new
zend_ini_entry *ini_entry;
zend_string *duplicate;
uint8_t modifiable;
- zend_bool modified;
+ bool modified;
if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
return FAILURE;
@@ -431,7 +431,7 @@ ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig)
}
/* }}} */
-ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, zend_bool *exists) /* {{{ */
+ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
{
zend_ini_entry *ini_entry;
@@ -457,7 +457,7 @@ ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig
ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
{
- zend_bool exists = 1;
+ bool exists = 1;
char *return_value;
return_value = zend_ini_string_ex(name, name_length, orig, &exists);
@@ -483,7 +483,7 @@ ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool zend_ini_parse_bool(zend_string *str)
+ZEND_API bool zend_ini_parse_bool(zend_string *str)
{
if ((ZSTR_LEN(str) == 4 && strcasecmp(ZSTR_VAL(str), "true") == 0)
|| (ZSTR_LEN(str) == 3 && strcasecmp(ZSTR_VAL(str), "yes") == 0)
@@ -573,7 +573,7 @@ ZEND_INI_DISP(display_link_numbers) /* {{{ */
/* Standard message handlers */
ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
{
- zend_bool *p = (zend_bool *) ZEND_INI_GET_ADDR();
+ bool *p = (bool *) ZEND_INI_GET_ADDR();
*p = zend_ini_parse_bool(new_value);
return SUCCESS;
}
diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h
index d227f3779d..590ff09cef 100644
--- a/Zend/zend_ini.h
+++ b/Zend/zend_ini.h
@@ -84,9 +84,9 @@ ZEND_API void display_ini_entries(zend_module_entry *module);
ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig);
ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig);
ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig);
-ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, zend_bool *exists);
+ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists);
ZEND_API zend_string *zend_ini_get_value(zend_string *name);
-ZEND_API zend_bool zend_ini_parse_bool(zend_string *str);
+ZEND_API bool zend_ini_parse_bool(zend_string *str);
ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type));
@@ -141,12 +141,12 @@ END_EXTERN_C()
#define INI_INT(name) zend_ini_long((name), strlen(name), 0)
#define INI_FLT(name) zend_ini_double((name), strlen(name), 0)
#define INI_STR(name) zend_ini_string_ex((name), strlen(name), 0, NULL)
-#define INI_BOOL(name) ((zend_bool) INI_INT(name))
+#define INI_BOOL(name) ((bool) INI_INT(name))
#define INI_ORIG_INT(name) zend_ini_long((name), strlen(name), 1)
#define INI_ORIG_FLT(name) zend_ini_double((name), strlen(name), 1)
#define INI_ORIG_STR(name) zend_ini_string((name), strlen(name), 1)
-#define INI_ORIG_BOOL(name) ((zend_bool) INI_ORIG_INT(name))
+#define INI_ORIG_BOOL(name) ((bool) INI_ORIG_INT(name))
#define REGISTER_INI_ENTRIES() zend_register_ini_entries(ini_entries, module_number)
#define UNREGISTER_INI_ENTRIES() zend_unregister_ini_entries(module_number)
@@ -180,8 +180,8 @@ END_EXTERN_C()
/* INI parsing engine */
typedef void (*zend_ini_parser_cb_t)(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg);
BEGIN_EXTERN_C()
-ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg);
-ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg);
+ZEND_API int zend_parse_ini_file(zend_file_handle *fh, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg);
+ZEND_API int zend_parse_ini_string(char *str, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg);
END_EXTERN_C()
/* INI entries */
diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y
index f9170d9ecc..6d9dd09e34 100644
--- a/Zend/zend_ini_parser.y
+++ b/Zend/zend_ini_parser.y
@@ -212,7 +212,7 @@ static ZEND_COLD void ini_error(const char *msg)
/* }}} */
/* {{{ zend_parse_ini_file() */
-ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)
+ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)
{
int retval;
zend_ini_parser_param ini_parser_param;
@@ -240,7 +240,7 @@ ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffe
/* }}} */
/* {{{ zend_parse_ini_string() */
-ZEND_API zend_result zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)
+ZEND_API zend_result zend_parse_ini_string(char *str, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)
{
int retval;
zend_ini_parser_param ini_parser_param;
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index 0d5af66d92..275026f738 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -436,7 +436,7 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
typedef struct {
zend_object std;
zend_object_iterator *iter;
- zend_bool rewind_called;
+ bool rewind_called;
} zend_internal_iterator;
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
@@ -596,31 +596,25 @@ ZEND_METHOD(InternalIterator, rewind) {
/* {{{ zend_register_interfaces */
ZEND_API void zend_register_interfaces(void)
{
- zend_class_entry ce;
+ zend_ce_traversable = register_class_Traversable();
+ zend_ce_traversable->interface_gets_implemented = zend_implement_traversable;
- REGISTER_MAGIC_INTERFACE(traversable, Traversable);
+ zend_ce_aggregate = register_class_IteratorAggregate(zend_ce_traversable);
+ zend_ce_aggregate->interface_gets_implemented = zend_implement_aggregate;
- REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
- REGISTER_MAGIC_IMPLEMENT(aggregate, traversable);
+ zend_ce_iterator = register_class_Iterator(zend_ce_traversable);
+ zend_ce_iterator->interface_gets_implemented = zend_implement_iterator;
- REGISTER_MAGIC_INTERFACE(iterator, Iterator);
- REGISTER_MAGIC_IMPLEMENT(iterator, traversable);
+ zend_ce_serializable = register_class_Serializable();
+ zend_ce_serializable->interface_gets_implemented = zend_implement_serializable;
- REGISTER_MAGIC_INTERFACE(serializable, Serializable);
+ zend_ce_arrayaccess = register_class_ArrayAccess();
- INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
- zend_ce_arrayaccess = zend_register_internal_interface(&ce);
+ zend_ce_countable = register_class_Countable();
- INIT_CLASS_ENTRY(ce, "Countable", class_Countable_methods);
- zend_ce_countable = zend_register_internal_interface(&ce);
+ zend_ce_stringable = register_class_Stringable();
- INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
- zend_ce_stringable = zend_register_internal_interface(&ce);
-
- INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
- zend_ce_internal_iterator = zend_register_internal_class(&ce);
- zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
- zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
+ zend_ce_internal_iterator = register_class_InternalIterator(zend_ce_iterator);
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
zend_ce_internal_iterator->serialize = zend_class_serialize_deny;
zend_ce_internal_iterator->unserialize = zend_class_unserialize_deny;
diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h
index ecdc9b0e1b..78aee0d39f 100644
--- a/Zend/zend_interfaces.h
+++ b/Zend/zend_interfaces.h
@@ -49,17 +49,6 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z
#define zend_call_method_with_2_params(obj, obj_ce, fn_proxy, function_name, retval, arg1, arg2) \
zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 2, arg1, arg2)
-#define REGISTER_MAGIC_INTERFACE(class_name, class_name_str) \
- {\
- zend_class_entry ce;\
- INIT_CLASS_ENTRY(ce, # class_name_str, class_ ## class_name_str ## _methods) \
- zend_ce_ ## class_name = zend_register_internal_interface(&ce);\
- zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\
- }
-
-#define REGISTER_MAGIC_IMPLEMENT(class_name, interface_name) \
- zend_class_implements(zend_ce_ ## class_name, 1, zend_ce_ ## interface_name)
-
ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter);
ZEND_API zend_result zend_user_it_valid(zend_object_iterator *_iter);
ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key);
diff --git a/Zend/zend_interfaces.stub.php b/Zend/zend_interfaces.stub.php
index 8b2cdae981..d1bd4a5037 100644
--- a/Zend/zend_interfaces.stub.php
+++ b/Zend/zend_interfaces.stub.php
@@ -1,6 +1,6 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
interface Traversable {}
diff --git a/Zend/zend_interfaces_arginfo.h b/Zend/zend_interfaces_arginfo.h
index 8a66da1636..c34f5463a0 100644
--- a/Zend/zend_interfaces_arginfo.h
+++ b/Zend/zend_interfaces_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c6ef101bd3881348a74b60cecd22d1d7f80017c8 */
+ * Stub hash: 34aa50c74f10106c6abd0ed2956d41c98aae6452 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IteratorAggregate_getIterator, 0, 0, 0)
ZEND_END_ARG_INFO()
@@ -119,3 +119,87 @@ static const zend_function_entry class_InternalIterator_methods[] = {
ZEND_ME(InternalIterator, rewind, arginfo_class_InternalIterator_rewind, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
+
+static zend_class_entry *register_class_Traversable(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Traversable", class_Traversable_methods);
+ class_entry = zend_register_internal_interface(&ce);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_IteratorAggregate(zend_class_entry *class_entry_Traversable)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "IteratorAggregate", class_IteratorAggregate_methods);
+ class_entry = zend_register_internal_interface(&ce);
+ zend_class_implements(class_entry, 1, class_entry_Traversable);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Iterator(zend_class_entry *class_entry_Traversable)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Iterator", class_Iterator_methods);
+ class_entry = zend_register_internal_interface(&ce);
+ zend_class_implements(class_entry, 1, class_entry_Traversable);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_ArrayAccess(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
+ class_entry = zend_register_internal_interface(&ce);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Serializable(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Serializable", class_Serializable_methods);
+ class_entry = zend_register_internal_interface(&ce);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Countable(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Countable", class_Countable_methods);
+ class_entry = zend_register_internal_interface(&ce);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_Stringable(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
+ class_entry = zend_register_internal_interface(&ce);
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_InternalIterator(zend_class_entry *class_entry_Iterator)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL;
+ zend_class_implements(class_entry, 1, class_entry_Iterator);
+
+ return class_entry;
+}
diff --git a/Zend/zend_language_scanner.h b/Zend/zend_language_scanner.h
index f08dbb4ea9..ca32329a55 100644
--- a/Zend/zend_language_scanner.h
+++ b/Zend/zend_language_scanner.h
@@ -63,7 +63,7 @@ typedef struct _zend_heredoc_label {
char *label;
int length;
int indentation;
- zend_bool indentation_uses_spaces;
+ bool indentation_uses_spaces;
} zend_heredoc_label;
/* Track locations of unclosed {, [, (, etc. for better syntax error reporting */
@@ -75,10 +75,10 @@ typedef struct _zend_nest_location {
BEGIN_EXTERN_C()
ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state);
ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state);
-ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename);
+ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename);
ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, const zend_encoding *old_encoding);
ZEND_API zend_result zend_multibyte_set_filter(const zend_encoding *onetime_encoding);
-ZEND_API zend_result zend_lex_tstring(zval *zv, zend_lexer_ident_ref ident_ref);
+ZEND_API zend_result zend_lex_tstring(zval *zv, unsigned char *ident);
END_EXTERN_C()
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 7e60f5cf05..36de6aea37 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -307,20 +307,25 @@ ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle)
}
}
-ZEND_API zend_result zend_lex_tstring(zval *zv, zend_lexer_ident_ref ident_ref)
+ZEND_API zend_result zend_lex_tstring(zval *zv, unsigned char *ident)
{
- char *ident = (char *) SCNG(yy_start) + ident_ref.offset;
- size_t length = ident_ref.len;
- if (length == sizeof("<?=")-1 && memcmp(ident, "<?=", sizeof("<?=")-1) == 0) {
+ unsigned char *end = ident;
+ while ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || *end == '_') {
+ end++;
+ }
+
+ size_t length = end - ident;
+ if (length == 0) {
+ ZEND_ASSERT(ident[0] == '<' && ident[1] == '?' && ident[2] == '=');
zend_throw_exception(zend_ce_parse_error, "Cannot use \"<?=\" as an identifier", 0);
return FAILURE;
}
if (SCNG(on_event)) {
- SCNG(on_event)(ON_FEEDBACK, T_STRING, 0, ident, length, SCNG(on_event_context));
+ SCNG(on_event)(ON_FEEDBACK, T_STRING, 0, (char *) ident, length, SCNG(on_event_context));
}
- ZVAL_STRINGL(zv, ident, length);
+ ZVAL_STRINGL(zv, (char *) ident, length);
return SUCCESS;
}
@@ -601,7 +606,7 @@ END_EXTERN_C()
static zend_op_array *zend_compile(int type)
{
zend_op_array *op_array = NULL;
- zend_bool original_in_compilation = CG(in_compilation);
+ bool original_in_compilation = CG(in_compilation);
CG(in_compilation) = 1;
CG(ast) = NULL;
@@ -631,6 +636,7 @@ static zend_op_array *zend_compile(int type)
zend_emit_final_return(type == ZEND_USER_FUNCTION);
op_array->line_start = 1;
op_array->line_end = last_lineno;
+ zend_init_static_variables_map_ptr(op_array);
pass_two(op_array);
zend_oparray_context_end(&original_oparray_context);
zend_file_context_end(&original_file_context);
@@ -669,9 +675,9 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
}
ZEND_API zend_ast *zend_compile_string_to_ast(
- zend_string *code, zend_arena **ast_arena, const char *filename) {
+ zend_string *code, zend_arena **ast_arena, zend_string *filename) {
zval code_zv;
- zend_bool original_in_compilation;
+ bool original_in_compilation;
zend_lex_state original_lex_state;
zend_ast *ast;
@@ -737,11 +743,10 @@ zend_op_array *compile_filename(int type, zval *filename)
return retval;
}
-ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename)
+ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename)
{
char *buf;
size_t size, old_len;
- zend_string *new_compiled_filename;
/* enforce ZEND_MMAP_AHEAD trailing NULLs for flex... */
old_len = Z_STRLEN_P(str);
@@ -773,10 +778,7 @@ ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename)
}
yy_scan_buffer(buf, size);
-
- new_compiled_filename = zend_string_init(filename, strlen(filename), 0);
- zend_set_compiled_filename(new_compiled_filename);
- zend_string_release_ex(new_compiled_filename, 0);
+ zend_set_compiled_filename(filename);
CG(zend_lineno) = 1;
CG(increment_lineno) = 0;
RESET_DOC_COMMENT();
@@ -809,6 +811,7 @@ zend_op_array *compile_string(zend_string *source_string, const char *filename)
zend_lex_state original_lex_state;
zend_op_array *op_array = NULL;
zval tmp;
+ zend_string *filename_str;
if (ZSTR_LEN(source_string) == 0) {
return NULL;
@@ -817,7 +820,9 @@ zend_op_array *compile_string(zend_string *source_string, const char *filename)
ZVAL_STR_COPY(&tmp, source_string);
zend_save_lexical_state(&original_lex_state);
- zend_prepare_string_for_scanning(&tmp, filename);
+ filename_str = zend_string_init(filename, strlen(filename), 0);
+ zend_prepare_string_for_scanning(&tmp, filename_str);
+ zend_string_release(filename_str);
BEGIN(ST_IN_SCRIPTING);
op_array = zend_compile(ZEND_EVAL_CODE);
@@ -851,17 +856,15 @@ zend_result highlight_file(const char *filename, zend_syntax_highlighter_ini *sy
return SUCCESS;
}
-void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name)
+void highlight_string(zend_string *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *filename)
{
zend_lex_state original_lex_state;
- zval tmp;
-
- if (UNEXPECTED(Z_TYPE_P(str) != IS_STRING)) {
- ZVAL_STR(&tmp, zval_get_string_func(str));
- str = &tmp;
- }
+ zval str_zv;
+ zend_string *filename_str = zend_string_init(filename, strlen(filename), 0);
+ ZVAL_STR_COPY(&str_zv, str);
zend_save_lexical_state(&original_lex_state);
- zend_prepare_string_for_scanning(str, str_name);
+ zend_prepare_string_for_scanning(&str_zv, filename_str);
+ zend_string_release(filename_str);
BEGIN(INITIAL);
zend_highlight(syntax_highlighter_ini);
if (SCNG(script_filtered)) {
@@ -869,9 +872,7 @@ void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter
SCNG(script_filtered) = NULL;
}
zend_restore_lexical_state(&original_lex_state);
- if (UNEXPECTED(str == &tmp)) {
- zval_ptr_dtor(&tmp);
- }
+ zval_ptr_dtor(&str_zv);
}
ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, const zend_encoding *old_encoding)
@@ -1023,7 +1024,7 @@ static zend_result zend_scan_escape_string(zval *zendlval, char *str, int len, c
/* cache where we started so we can parse after validating */
char *start = s + 1;
size_t len = 0;
- zend_bool valid = 1;
+ bool valid = 1;
unsigned long codepoint;
if (*start != '{') {
@@ -1163,9 +1164,9 @@ static const char *next_newline(const char *str, const char *end, size_t *newlin
return NULL;
}
-static zend_bool strip_multiline_string_indentation(
- zval *zendlval, int indentation, zend_bool using_spaces,
- zend_bool newline_at_start, zend_bool newline_at_end)
+static bool strip_multiline_string_indentation(
+ zval *zendlval, int indentation, bool using_spaces,
+ bool newline_at_start, bool newline_at_end)
{
const char *str = Z_STRVAL_P(zendlval), *end = str + Z_STRLEN_P(zendlval);
char *copy = Z_STRVAL_P(zendlval);
@@ -1372,6 +1373,7 @@ DNUM ({LNUM}?"."{LNUM})|({LNUM}"."{LNUM}?)
EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM})
HNUM "0x"[0-9a-fA-F]+(_[0-9a-fA-F]+)*
BNUM "0b"[01]+(_[01]+)*
+ONUM "0o"[0-7]+(_[0-7]+)*
LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*
WHITESPACE [ \n\r\t]+
TABS_AND_SPACES [ \t]*
@@ -1918,7 +1920,7 @@ NEWLINE ("\r"|"\n"|"\r\n")
/* The +/- 2 skips "0b" */
size_t len = yyleng - 2;
char *end, *bin = yytext + 2;
- zend_bool contains_underscores;
+ bool contains_underscores;
/* Skip any leading 0s */
while (len > 0 && (*bin == '0' || *bin == '_')) {
@@ -1956,11 +1958,61 @@ NEWLINE ("\r"|"\n"|"\r\n")
}
}
+<ST_IN_SCRIPTING>{ONUM} {
+ /* The +/- 2 skips "0o" */
+ size_t len = yyleng - 2;
+ char *end, *octal = yytext + 2;
+ bool contains_underscores = (memchr(octal, '_', len) != NULL);
+
+ /* Skip any leading 0s */
+ while (len > 0 && (*octal == '0' || *octal == '_')) {
+ ++octal;
+ --len;
+ }
+
+ if (len == 0) {
+ ZVAL_LONG(zendlval, 0);
+ RETURN_TOKEN_WITH_VAL(T_LNUMBER);
+ }
+
+ if (contains_underscores) {
+ octal = estrndup(octal, len);
+ strip_underscores(octal, &len);
+ }
+
+ errno = 0;
+
+ ZVAL_LONG(zendlval, ZEND_STRTOL(octal, &end, 8));
+
+ ZEND_ASSERT(end == octal + len);
+
+ if (!errno) {
+ if (contains_underscores) {
+ efree(octal);
+ }
+ RETURN_TOKEN_WITH_VAL(T_LNUMBER);
+ }
+
+ /* Overflow */
+ ZEND_ASSERT(errno == ERANGE);
+ /* Reset errno */
+ errno = 0;
+
+ /* zend_oct_strtod skips leading '0' */
+ ZVAL_DOUBLE(zendlval, zend_oct_strtod(octal, (const char **)&end));
+ ZEND_ASSERT(!errno);
+ ZEND_ASSERT(end == octal + len);
+ if (contains_underscores) {
+ efree(octal);
+ }
+ RETURN_TOKEN_WITH_VAL(T_DNUMBER);
+}
+
<ST_IN_SCRIPTING>{LNUM} {
size_t len = yyleng;
char *end, *lnum = yytext;
- zend_bool is_octal = lnum[0] == '0';
- zend_bool contains_underscores = (memchr(lnum, '_', len) != NULL);
+ bool is_octal = lnum[0] == '0';
+ bool contains_underscores = (memchr(lnum, '_', len) != NULL);
if (contains_underscores) {
lnum = estrndup(lnum, len);
@@ -2023,7 +2075,7 @@ NEWLINE ("\r"|"\n"|"\r\n")
/* The +/- 2 skips "0x" */
size_t len = yyleng - 2;
char *end, *hex = yytext + 2;
- zend_bool contains_underscores;
+ bool contains_underscores;
/* Skip any leading 0s */
while (len > 0 && (*hex == '0' || *hex == '_')) {
@@ -2077,7 +2129,7 @@ string:
RETURN_TOKEN_WITH_VAL(T_NUM_STRING);
}
-<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM} { /* Offset must be treated as a string */
+<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM}|{ONUM} { /* Offset must be treated as a string */
if (yyleng == 1) {
ZVAL_INTERNED_STR(zendlval, ZSTR_CHAR((zend_uchar)*(yytext)));
} else {
@@ -2090,7 +2142,7 @@ string:
const char *end;
size_t len = yyleng;
char *dnum = yytext;
- zend_bool contains_underscores = (memchr(dnum, '_', len) != NULL);
+ bool contains_underscores = (memchr(dnum, '_', len) != NULL);
if (contains_underscores) {
dnum = estrndup(dnum, len);
@@ -2528,7 +2580,7 @@ skip_escape_conversion:
unsigned char *saved_cursor;
int bprefix = (yytext[0] != '<') ? 1 : 0, spacing = 0, indentation = 0;
zend_heredoc_label *heredoc_label = emalloc(sizeof(zend_heredoc_label));
- zend_bool is_heredoc = 1;
+ bool is_heredoc = 1;
CG(zend_lineno)++;
heredoc_label->length = yyleng-bprefix-3-1-(yytext[yyleng-2]=='\r'?1:0);
@@ -2909,7 +2961,7 @@ heredoc_scan_done:
ZVAL_STRINGL(zendlval, yytext, yyleng - newline);
if (!SCNG(heredoc_scan_ahead) && !EG(exception) && PARSER_MODE()) {
- zend_bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r';
+ bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r';
zend_string *copy = Z_STR_P(zendlval);
if (!strip_multiline_string_indentation(
@@ -3010,7 +3062,7 @@ nowdoc_scan_done:
ZVAL_STRINGL(zendlval, yytext, yyleng - newline);
if (!EG(exception) && spacing != -1 && PARSER_MODE()) {
- zend_bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r';
+ bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r';
if (!strip_multiline_string_indentation(
zendlval, indentation, spacing == HEREDOC_USING_SPACES,
newline_at_start, newline != 0)) {
@@ -3050,8 +3102,7 @@ emit_token:
emit_token_with_ident:
if (PARSER_MODE()) {
- elem->ident.offset = SCNG(yy_text) - SCNG(yy_start);
- elem->ident.len = SCNG(yy_leng);
+ elem->ident = SCNG(yy_text);
}
if (SCNG(on_event)) {
SCNG(on_event)(ON_TOKEN, token, start_line, yytext, yyleng, SCNG(on_event_context));
diff --git a/Zend/zend_map_ptr.h b/Zend/zend_map_ptr.h
index c6930473cf..c014f225a3 100644
--- a/Zend/zend_map_ptr.h
+++ b/Zend/zend_map_ptr.h
@@ -37,9 +37,13 @@
type * ZEND_MAP_PTR(name)
# define ZEND_MAP_PTR_GET(ptr) \
(*(ZEND_MAP_PTR(ptr)))
+# define ZEND_MAP_PTR_GET_IMM(ptr) \
+ ZEND_MAP_PTR_GET(ptr)
# define ZEND_MAP_PTR_SET(ptr, val) do { \
(*(ZEND_MAP_PTR(ptr))) = (val); \
} while (0)
+# define ZEND_MAP_PTR_SET_IMM(ptr, val) \
+ ZEND_MAP_PTR_SET(ptr, val)
# define ZEND_MAP_PTR_INIT(ptr, val) do { \
ZEND_MAP_PTR(ptr) = (val); \
} while (0)
@@ -51,6 +55,8 @@
# define ZEND_MAP_PTR_SET_REAL_BASE(base, ptr) do { \
base = (ptr); \
} while (0)
+# define ZEND_MAP_PTR_OFFSET2PTR(ptr) \
+ ((void**)((char*)CG(map_ptr_base) + (uintptr_t)ZEND_MAP_PTR(ptr)))
#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
# define ZEND_MAP_PTR(ptr) \
ptr ## __ptr
@@ -66,6 +72,8 @@
(*(ZEND_MAP_PTR_IS_OFFSET(ptr) ? \
ZEND_MAP_PTR_OFFSET2PTR(ptr) : \
((void**)(ZEND_MAP_PTR(ptr)))))
+# define ZEND_MAP_PTR_GET_IMM(ptr) \
+ (*ZEND_MAP_PTR_OFFSET2PTR(ptr))
# define ZEND_MAP_PTR_SET(ptr, val) do { \
void **__p = (void**)(ZEND_MAP_PTR(ptr)); \
if (ZEND_MAP_PTR_IS_OFFSET(ptr)) { \
@@ -73,6 +81,10 @@
} \
*__p = (val); \
} while (0)
+# define ZEND_MAP_PTR_SET_IMM(ptr, val) do { \
+ void **__p = ZEND_MAP_PTR_OFFSET2PTR(ptr); \
+ *__p = (val); \
+ } while (0)
# define ZEND_MAP_PTR_INIT(ptr, val) do { \
ZEND_MAP_PTR(ptr) = (val); \
} while (0)
diff --git a/Zend/zend_modules.h b/Zend/zend_modules.h
index 30d70bbf53..98957def76 100644
--- a/Zend/zend_modules.h
+++ b/Zend/zend_modules.h
@@ -31,7 +31,7 @@
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module
-#define ZEND_MODULE_API_NO 20200930
+#define ZEND_MODULE_API_NO 20201009
#ifdef ZTS
#define USING_ZTS 1
#else
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index be1631f587..f10e2c58a6 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -63,47 +63,62 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
if (!zobj->properties) {
zend_property_info *prop_info;
zend_class_entry *ce = zobj->ce;
- uint32_t flags = 0;
+ int i;
zobj->properties = zend_new_array(ce->default_properties_count);
if (ce->default_properties_count) {
zend_hash_real_init_mixed(zobj->properties);
- ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
- if (!(prop_info->flags & ZEND_ACC_STATIC)) {
- flags |= prop_info->flags;
+ for (i = 0; i < ce->default_properties_count; i++) {
+ prop_info = ce->properties_info_table[i];
- if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
- HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND;
- }
-
- _zend_hash_append_ind(zobj->properties, prop_info->name,
- OBJ_PROP(zobj, prop_info->offset));
+ if (!prop_info) {
+ continue;
}
- } ZEND_HASH_FOREACH_END();
- if (flags & ZEND_ACC_CHANGED) {
- while (ce->parent && ce->parent->default_properties_count) {
- ce = ce->parent;
- ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
- if (prop_info->ce == ce &&
- !(prop_info->flags & ZEND_ACC_STATIC) &&
- (prop_info->flags & ZEND_ACC_PRIVATE)) {
- zval zv;
-
- if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
- HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND;
- }
-
- ZVAL_INDIRECT(&zv, OBJ_PROP(zobj, prop_info->offset));
- zend_hash_add(zobj->properties, prop_info->name, &zv);
- }
- } ZEND_HASH_FOREACH_END();
+
+ if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
+ HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND;
}
+
+ _zend_hash_append_ind(zobj->properties, prop_info->name,
+ OBJ_PROP(zobj, prop_info->offset));
}
}
}
}
/* }}} */
+ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj) /* {{{ */
+{
+ zend_property_info *prop_info;
+ zend_class_entry *ce = zobj->ce;
+ HashTable *ht;
+ zval* prop;
+ int i;
+
+ ZEND_ASSERT(!zobj->properties);
+ ht = zend_new_array(ce->default_properties_count);
+ if (ce->default_properties_count) {
+ zend_hash_real_init_mixed(ht);
+ for (i = 0; i < ce->default_properties_count; i++) {
+ prop_info = ce->properties_info_table[i];
+
+ if (!prop_info) {
+ continue;
+ }
+
+ prop = OBJ_PROP(zobj, prop_info->offset);
+ if (UNEXPECTED(Z_TYPE_P(prop) == IS_UNDEF)) {
+ continue;
+ }
+
+ Z_TRY_ADDREF_P(prop);
+ _zend_hash_append(ht, prop_info->name, prop);
+ }
+ }
+ return ht;
+}
+/* }}} */
+
ZEND_API HashTable *zend_std_get_properties(zend_object *zobj) /* {{{ */
{
if (!zobj->properties) {
@@ -209,7 +224,7 @@ static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zv
/* }}} */
-static zend_always_inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
+static zend_always_inline bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
{
child_class = child_class->parent;
while (child_class) {
@@ -443,7 +458,7 @@ found:
}
/* }}} */
-ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, zend_bool is_dynamic) /* {{{ */
+ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, bool is_dynamic) /* {{{ */
{
zend_property_info *property_info;
const char *class_name = NULL;
@@ -691,7 +706,7 @@ exit:
}
/* }}} */
-static zend_always_inline zend_bool property_uses_strict_types() {
+static zend_always_inline bool property_uses_strict_types() {
zend_execute_data *execute_data = EG(current_execute_data);
return execute_data
&& execute_data->func
@@ -1263,11 +1278,29 @@ static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_
}
/* }}} */
+static zend_always_inline zend_function *get_static_method_fallback(
+ zend_class_entry *ce, zend_string *function_name)
+{
+ zend_object *object;
+ if (ce->__call &&
+ (object = zend_get_this_object(EG(current_execute_data))) != NULL &&
+ instanceof_function(object->ce, ce)) {
+ /* Call the top-level defined __call().
+ * see: tests/classes/__call_004.phpt */
+
+ ZEND_ASSERT(object->ce->__call);
+ return zend_get_user_call_function(object->ce, function_name);
+ } else if (ce->__callstatic) {
+ return zend_get_user_callstatic_function(ce, function_name);
+ } else {
+ return NULL;
+ }
+}
+
ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name, const zval *key) /* {{{ */
{
zend_function *fbc = NULL;
zend_string *lc_function_name;
- zend_object *object;
zend_class_entry *scope;
if (EXPECTED(key != NULL)) {
@@ -1293,19 +1326,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st
if (UNEXPECTED(!key)) {
zend_string_release_ex(lc_function_name, 0);
}
- if (ce->__call &&
- (object = zend_get_this_object(EG(current_execute_data))) != NULL &&
- instanceof_function(object->ce, ce)) {
- /* Call the top-level defined __call().
- * see: tests/classes/__call_004.phpt */
-
- ZEND_ASSERT(object->ce->__call);
- return zend_get_user_call_function(object->ce, function_name);
- } else if (ce->__callstatic) {
- return zend_get_user_callstatic_function(ce, function_name);
- } else {
- return NULL;
- }
+ return get_static_method_fallback(ce, function_name);
}
} while (0);
@@ -1321,12 +1342,11 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st
if (UNEXPECTED(fbc->common.scope != scope)) {
if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE)
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
- if (ce->__callstatic) {
- fbc = zend_get_user_callstatic_function(ce, function_name);
- } else {
+ zend_function *fallback_fbc = get_static_method_fallback(ce, function_name);
+ if (!fallback_fbc) {
zend_bad_method_call(fbc, function_name, scope);
- fbc = NULL;
}
+ fbc = fallback_fbc;
}
}
}
@@ -1440,7 +1460,7 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p
return zend_std_get_static_property_with_info(ce, property_name, type, &prop_info);
}
-ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */
+ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */
{
zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
return 0;
@@ -1550,6 +1570,7 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
}
if (!zobj1->properties && !zobj2->properties) {
zend_property_info *info;
+ int i;
if (!zobj1->ce->default_properties_count) {
return 0;
@@ -1565,14 +1586,18 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
}
Z_PROTECT_RECURSION_P(o1);
- ZEND_HASH_FOREACH_PTR(&zobj1->ce->properties_info, info) {
- zval *p1 = OBJ_PROP(zobj1, info->offset);
- zval *p2 = OBJ_PROP(zobj2, info->offset);
+ for (i = 0; i < zobj1->ce->default_properties_count; i++) {
+ zval *p1, *p2;
+
+ info = zobj1->ce->properties_info_table[i];
- if (info->flags & ZEND_ACC_STATIC) {
+ if (!info) {
continue;
}
+ p1 = OBJ_PROP(zobj1, info->offset);
+ p2 = OBJ_PROP(zobj2, info->offset);
+
if (Z_TYPE_P(p1) != IS_UNDEF) {
if (Z_TYPE_P(p2) != IS_UNDEF) {
int ret;
@@ -1592,7 +1617,7 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
return 1;
}
}
- } ZEND_HASH_FOREACH_END();
+ }
Z_UNPROTECT_RECURSION_P(o1);
return 0;
@@ -1751,7 +1776,7 @@ ZEND_API int zend_std_cast_object_tostring(zend_object *readobj, zval *writeobj,
}
/* }}} */
-ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */
+ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
{
zval *func;
zend_class_entry *ce = obj->ce;
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index 5134230354..53eef82928 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -36,10 +36,6 @@ struct _zend_property_info;
#define ZEND_ENCODE_DYN_PROP_OFFSET(offset) ((uintptr_t)(-((intptr_t)(offset) + 2)))
-/* The following rule applies to read_property() and read_dimension() implementations:
- If you return a zval which is not otherwise referenced by the extension or the engine's
- symbol table, its reference count should be 0.
-*/
/* Used to fetch property from the object, read-only */
typedef zval *(*zend_object_read_property_t)(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv);
@@ -47,13 +43,9 @@ typedef zval *(*zend_object_read_property_t)(zend_object *object, zend_string *m
typedef zval *(*zend_object_read_dimension_t)(zend_object *object, zval *offset, int type, zval *rv);
-/* The following rule applies to write_property() and write_dimension() implementations:
- If you receive a value zval in write_property/write_dimension, you may only modify it if
- its reference count is 1. Otherwise, you must create a copy of that zval before making
- any changes. You should NOT modify the reference count of the value passed to you.
+/* Used to set property of the object
You must return the final value of the assigned property.
*/
-/* Used to set property of the object */
typedef zval *(*zend_object_write_property_t)(zend_object *object, zend_string *member, zval *value, void **cache_slot);
/* Used to set dimension of the object */
@@ -118,9 +110,29 @@ typedef zend_array *(*zend_object_get_properties_for_t)(zend_object *object, zen
typedef zend_function *(*zend_object_get_method_t)(zend_object **object, zend_string *method, const zval *key);
typedef zend_function *(*zend_object_get_constructor_t)(zend_object *object);
-/* Object maintenance/destruction */
-typedef void (*zend_object_dtor_obj_t)(zend_object *object);
+/* free_obj should release any resources the object holds, without freeing the
+ * object structure itself. The object does not need to be in a valid state after
+ * free_obj finishes running.
+ *
+ * free_obj will always be invoked, even if the object leaks or a fatal error
+ * occurs. However, during shutdown it may be called once the executor is no
+ * longer active, in which case execution of user code may be skipped.
+ */
typedef void (*zend_object_free_obj_t)(zend_object *object);
+
+/* dtor_obj is called before free_obj. The object must remain in a valid state
+ * after dtor_obj finishes running. Unlike free_obj, it is run prior to
+ * deactivation of the executor during shutdown, which allows user code to run.
+ *
+ * This handler is not guaranteed to be called (e.g. on fatal error), and as
+ * such should not be used to release resources or deallocate memory. Furthermore,
+ * releasing resources in this handler can break detection of memory leaks, as
+ * cycles may be broken early.
+ *
+ * dtor_obj should be used *only* to call user destruction hooks, such as __destruct.
+ */
+typedef void (*zend_object_dtor_obj_t)(zend_object *object);
+
typedef zend_object* (*zend_object_clone_obj_t)(zend_object *object);
/* Get class name for display in var_dump and other debugging functions.
@@ -138,7 +150,7 @@ typedef int (*zend_object_cast_t)(zend_object *readobj, zval *retval, int type);
* Returns FAILURE if the object does not have any sense of overloaded dimensions */
typedef int (*zend_object_count_elements_t)(zend_object *object, zend_long *count);
-typedef int (*zend_object_get_closure_t)(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only);
+typedef int (*zend_object_get_closure_t)(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only);
typedef HashTable *(*zend_object_get_gc_t)(zend_object *object, zval **table, int *n);
@@ -191,7 +203,7 @@ ZEND_API void zend_class_init_statics(zend_class_entry *ce);
ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name_strval, const zval *key);
ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend_string *property_name, int type, struct _zend_property_info **prop_info);
ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type);
-ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name);
+ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name);
ZEND_API zend_function *zend_std_get_constructor(zend_object *object);
ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent);
ZEND_API HashTable *zend_std_get_properties(zend_object *object);
@@ -210,16 +222,18 @@ ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset);
ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key);
ZEND_API zend_string *zend_std_get_class_name(const zend_object *zobj);
ZEND_API int zend_std_compare_objects(zval *o1, zval *o2);
-ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only);
+ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only);
ZEND_API void rebuild_object_properties(zend_object *zobj);
+ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj);
+
/* Handler for objects that cannot be meaningfully compared.
* Only objects with the same identity will be considered equal. */
ZEND_API int zend_objects_not_comparable(zval *o1, zval *o2);
ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope);
-ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, zend_bool is_dynamic);
+ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, bool is_dynamic);
ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static);
diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c
index 80359b5e1e..104cda6141 100644
--- a/Zend/zend_objects_API.c
+++ b/Zend/zend_objects_API.c
@@ -79,7 +79,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_stor
}
}
-ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown)
+ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown)
{
zend_object **obj_ptr, **end, *obj;
diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h
index 85ce701cd7..539850f227 100644
--- a/Zend/zend_objects_API.h
+++ b/Zend/zend_objects_API.h
@@ -54,7 +54,7 @@ BEGIN_EXTERN_C()
ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size);
ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects);
ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects);
-ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown);
+ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown);
ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objects);
/* Store API functions */
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 6681ef7b68..4e30b9ff7a 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -77,7 +77,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
op_array->last_live_range = 0;
op_array->static_variables = NULL;
- ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
+ ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
op_array->last_try_catch = 0;
op_array->fn_flags = 0;
@@ -85,6 +85,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
op_array->last_literal = 0;
op_array->literals = NULL;
+ op_array->num_dynamic_func_defs = 0;
+ op_array->dynamic_func_defs = NULL;
+
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
@@ -103,7 +106,7 @@ ZEND_API void destroy_zend_function(zend_function *function)
zend_function_dtor(&tmp);
}
-ZEND_API void zend_type_release(zend_type type, zend_bool persistent) {
+ZEND_API void zend_type_release(zend_type type, bool persistent) {
if (ZEND_TYPE_HAS_LIST(type)) {
zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
@@ -163,7 +166,7 @@ ZEND_API void zend_function_dtor(zval *zv)
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
{
- if (CE_STATIC_MEMBERS(ce)) {
+ if (ZEND_MAP_PTR(ce->static_members_table) && CE_STATIC_MEMBERS(ce)) {
zval *static_members = CE_STATIC_MEMBERS(ce);
zval *p = static_members;
zval *end = p + ce->default_static_members_count;
@@ -255,18 +258,76 @@ static void _destroy_zend_class_traits_info(zend_class_entry *ce)
}
}
+ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
+{
+ zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+
+ if (mutable_data) {
+ HashTable *constants_table;
+ zval *p;
+
+ constants_table = mutable_data->constants_table;
+ if (constants_table && constants_table != &ce->constants_table) {
+ zend_class_constant *c;
+
+ ZEND_HASH_FOREACH_PTR(constants_table, c) {
+ zval_ptr_dtor_nogc(&c->value);
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(constants_table);
+ mutable_data->constants_table = NULL;
+ }
+
+ p = mutable_data->default_properties_table;
+ if (p && p != ce->default_properties_table) {
+ zval *end = p + ce->default_properties_count;
+
+ while (p < end) {
+ zval_ptr_dtor_nogc(p);
+ p++;
+ }
+ mutable_data->default_properties_table = NULL;
+ }
+
+ ZEND_MAP_PTR_SET_IMM(ce->mutable_data, NULL);
+ }
+}
+
ZEND_API void destroy_zend_class(zval *zv)
{
zend_property_info *prop_info;
zend_class_entry *ce = Z_PTR_P(zv);
zend_function *fn;
- if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED)) {
+ if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED|ZEND_ACC_FILE_CACHED)) {
zend_op_array *op_array;
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
}
+
+ if (!(ce->ce_flags & ZEND_ACC_FILE_CACHED)) {
+ if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) {
+ zend_cleanup_mutable_class_data(ce);
+ }
+ } else {
+ zend_class_constant *c;
+ zval *p, *end;
+
+ ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
+ if (c->ce == ce) {
+ zval_ptr_dtor_nogc(&c->value);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ p = ce->default_properties_table;
+ end = p + ce->default_properties_count;
+
+ while (p < end) {
+ zval_ptr_dtor_nogc(p);
+ p++;
+ }
+ }
+
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->type == ZEND_USER_FUNCTION) {
@@ -280,9 +341,37 @@ ZEND_API void destroy_zend_class(zval *zv)
}
switch (ce->type) {
case ZEND_USER_CLASS:
- if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
- zend_string_release_ex(ce->parent_name, 0);
+ if (!(ce->ce_flags & ZEND_ACC_CACHED)) {
+ if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
+ zend_string_release_ex(ce->parent_name, 0);
+ }
+
+ zend_string_release_ex(ce->name, 0);
+ zend_string_release_ex(ce->info.user.filename, 0);
+
+ if (ce->info.user.doc_comment) {
+ zend_string_release_ex(ce->info.user.doc_comment, 0);
+ }
+
+ if (ce->attributes) {
+ zend_hash_release(ce->attributes);
+ }
+
+ if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
+ uint32_t i;
+
+ for (i = 0; i < ce->num_interfaces; i++) {
+ zend_string_release_ex(ce->interface_names[i].name, 0);
+ zend_string_release_ex(ce->interface_names[i].lc_name, 0);
+ }
+ efree(ce->interface_names);
+ }
+
+ if (ce->num_traits > 0) {
+ _destroy_zend_class_traits_info(ce);
+ }
}
+
if (ce->default_properties_table) {
zval *p = ce->default_properties_table;
zval *end = p + ce->default_properties_count;
@@ -325,7 +414,6 @@ ZEND_API void destroy_zend_class(zval *zv)
}
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&ce->properties_info);
- zend_string_release_ex(ce->name, 0);
zend_hash_destroy(&ce->function_table);
if (zend_hash_num_elements(&ce->constants_table)) {
zend_class_constant *c;
@@ -343,29 +431,9 @@ ZEND_API void destroy_zend_class(zval *zv)
} ZEND_HASH_FOREACH_END();
}
zend_hash_destroy(&ce->constants_table);
- if (ce->num_interfaces > 0) {
- if (!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
- uint32_t i;
-
- for (i = 0; i < ce->num_interfaces; i++) {
- zend_string_release_ex(ce->interface_names[i].name, 0);
- zend_string_release_ex(ce->interface_names[i].lc_name, 0);
- }
- }
+ if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
efree(ce->interfaces);
}
- zend_string_release_ex(ce->info.user.filename, 0);
- if (ce->info.user.doc_comment) {
- zend_string_release_ex(ce->info.user.doc_comment, 0);
- }
- if (ce->attributes) {
- zend_hash_release(ce->attributes);
- }
-
- if (ce->num_traits > 0) {
- _destroy_zend_class_traits_info(ce);
- }
-
break;
case ZEND_INTERNAL_CLASS:
if (ce->default_properties_table) {
@@ -446,18 +514,20 @@ void zend_class_add_ref(zval *zv)
}
}
-ZEND_API void destroy_op_array(zend_op_array *op_array)
+ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
{
- uint32_t i;
-
- if (op_array->static_variables) {
+ if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
- if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
- if (GC_DELREF(ht) == 0) {
- zend_array_destroy(ht);
- }
+ if (ht) {
+ zend_array_destroy(ht);
+ ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
}
}
+}
+
+ZEND_API void destroy_op_array(zend_op_array *op_array)
+{
+ uint32_t i;
if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)
&& ZEND_MAP_PTR(op_array->run_time_cache)) {
@@ -534,6 +604,22 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
}
efree(arg_info);
}
+ if (op_array->static_variables) {
+ zend_array_destroy(op_array->static_variables);
+ }
+ if (op_array->num_dynamic_func_defs) {
+ for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
+ /* Closures overwrite static_variables in their copy.
+ * Make sure to destroy them when the prototype function is destroyed. */
+ if (op_array->dynamic_func_defs[i]->static_variables
+ && (op_array->dynamic_func_defs[i]->fn_flags & ZEND_ACC_CLOSURE)) {
+ zend_array_destroy(op_array->dynamic_func_defs[i]->static_variables);
+ op_array->dynamic_func_defs[i]->static_variables = NULL;
+ }
+ destroy_op_array(op_array->dynamic_func_defs[i]);
+ }
+ efree(op_array->dynamic_func_defs);
+ }
}
static void zend_update_extended_stmts(zend_op_array *op_array)
@@ -754,14 +840,14 @@ static void emit_live_range(
emit_live_range_raw(op_array, var_num, kind, start, end);
}
-static zend_bool is_fake_def(zend_op *opline) {
+static bool is_fake_def(zend_op *opline) {
/* These opcodes only modify the result, not create it. */
return opline->opcode == ZEND_ROPE_ADD
|| opline->opcode == ZEND_ADD_ARRAY_ELEMENT
|| opline->opcode == ZEND_ADD_ARRAY_UNPACK;
}
-static zend_bool keeps_op1_alive(zend_op *opline) {
+static bool keeps_op1_alive(zend_op *opline) {
/* These opcodes don't consume their OP1 operand,
* it is later freed by something else. */
if (opline->opcode == ZEND_CASE
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index d1f2d553fc..db53c71317 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -30,12 +30,21 @@
#include "zend_exceptions.h"
#include "zend_closures.h"
+#include <locale.h>
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+
#ifdef __SSE2__
#include <emmintrin.h>
#endif
+#if defined(ZEND_WIN32) && !defined(ZTS) && defined(_MSC_VER)
+/* This performance improvement of tolower() on Windows gives 10-18% on bench.php */
+#define ZEND_USE_TOLOWER_L 1
+#endif
+
#ifdef ZEND_USE_TOLOWER_L
-#include <locale.h>
static _locale_t current_locale = NULL;
/* this is true global! may lead to strange effects on ZTS, but so may setlocale() */
#define zend_tolower(c) _tolower_l(c, current_locale)
@@ -285,7 +294,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o
}
/* }}} */
-static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, zend_bool *failed) /* {{{ */
+static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, bool *failed) /* {{{ */
{
*failed = 0;
switch (Z_TYPE_P(op)) {
@@ -377,7 +386,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze
#define convert_op1_op2_long(op1, op1_lval, op2, op2_lval, result, opcode, sigil) \
do { \
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { \
- zend_bool failed; \
+ bool failed; \
if (Z_ISREF_P(op1)) { \
op1 = Z_REFVAL_P(op1); \
if (Z_TYPE_P(op1) == IS_LONG) { \
@@ -400,7 +409,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze
} while (0); \
do { \
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { \
- zend_bool failed; \
+ bool failed; \
if (Z_ISREF_P(op2)) { \
op2 = Z_REFVAL_P(op2); \
if (Z_TYPE_P(op2) == IS_LONG) { \
@@ -424,14 +433,6 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze
ZEND_API void ZEND_FASTCALL convert_to_long(zval *op) /* {{{ */
{
- if (Z_TYPE_P(op) != IS_LONG) {
- convert_to_long_base(op, 10);
- }
-}
-/* }}} */
-
-ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base) /* {{{ */
-{
zend_long tmp;
try_again:
@@ -456,11 +457,7 @@ try_again:
case IS_STRING:
{
zend_string *str = Z_STR_P(op);
- if (base == 10) {
- ZVAL_LONG(op, zval_get_long(op));
- } else {
- ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base));
- }
+ ZVAL_LONG(op, zval_get_long(op));
zend_string_release_ex(str, 0);
}
break;
@@ -683,7 +680,7 @@ try_again:
}
/* }}} */
-ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op) /* {{{ */
+ZEND_API bool ZEND_FASTCALL _try_convert_to_string(zval *op) /* {{{ */
{
zend_string *str;
@@ -716,6 +713,13 @@ try_again:
case IS_OBJECT:
if (Z_OBJCE_P(op) == zend_ce_closure) {
convert_scalar_to_array(op);
+ } else if (Z_OBJ_P(op)->properties == NULL
+ && Z_OBJ_HT_P(op)->get_properties_for == NULL
+ && Z_OBJ_HT_P(op)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ HashTable *ht = zend_std_build_object_properties_array(Z_OBJ_P(op));
+ OBJ_RELEASE(Z_OBJ_P(op));
+ ZVAL_ARR(op, ht);
} else {
HashTable *obj_ht = zend_get_properties_for(op, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -882,7 +886,7 @@ try_again:
}
/* }}} */
-static zend_always_inline zend_string* __zval_get_string_func(zval *op, zend_bool try) /* {{{ */
+static zend_always_inline zend_string* __zval_get_string_func(zval *op, bool try) /* {{{ */
{
try_again:
switch (Z_TYPE_P(op)) {
@@ -1530,7 +1534,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1,
}
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_OR);
op1_lval = zendi_try_get_long(op1, &failed);
if (UNEXPECTED(failed)) {
@@ -1544,7 +1548,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1,
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_OR);
op2_lval = zendi_try_get_long(op2, &failed);
if (UNEXPECTED(failed)) {
@@ -1612,7 +1616,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1,
}
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_AND);
op1_lval = zendi_try_get_long(op1, &failed);
if (UNEXPECTED(failed)) {
@@ -1626,7 +1630,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1,
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_AND);
op2_lval = zendi_try_get_long(op2, &failed);
if (UNEXPECTED(failed)) {
@@ -1694,7 +1698,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1,
}
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR);
op1_lval = zendi_try_get_long(op1, &failed);
if (UNEXPECTED(failed)) {
@@ -1708,7 +1712,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1,
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
- zend_bool failed;
+ bool failed;
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_XOR);
op2_lval = zendi_try_get_long(op2, &failed);
if (UNEXPECTED(failed)) {
@@ -1910,7 +1914,7 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
}
/* }}} */
-ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, zend_bool case_insensitive) /* {{{ */
+ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool case_insensitive) /* {{{ */
{
zend_string *tmp_str1, *tmp_str2;
zend_string *str1 = zval_get_tmp_string(op1, &tmp_str1);
@@ -2194,7 +2198,7 @@ static int hash_zval_identical_function(zval *z1, zval *z2) /* {{{ */
}
/* }}} */
-ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2) /* {{{ */
{
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
return 0;
@@ -2265,7 +2269,7 @@ ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zv
}
/* }}} */
-ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce) /* {{{ */
+ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce) /* {{{ */
{
uint32_t i;
ZEND_ASSERT(!(class_ce->ce_flags & ZEND_ACC_INTERFACE));
@@ -2283,7 +2287,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_clas
}
/* }}} */
-ZEND_API zend_bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */
+ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */
{
ZEND_ASSERT(instance_ce != ce && "Should have been checked already");
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
@@ -2335,8 +2339,10 @@ static void ZEND_FASTCALL increment_string(zval *str) /* {{{ */
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
Z_TYPE_INFO_P(str) = IS_STRING_EX;
} else if (Z_REFCOUNT_P(str) > 1) {
- Z_DELREF_P(str);
+ /* Only release string after allocation succeeded. */
+ zend_string *orig_str = Z_STR_P(str);
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
+ GC_DELREF(orig_str);
} else {
zend_string_forget_hash_val(Z_STR_P(str));
}
@@ -2547,13 +2553,85 @@ ZEND_API bool ZEND_FASTCALL zend_object_is_true(zval *op) /* {{{ */
}
/* }}} */
-#ifdef ZEND_USE_TOLOWER_L
ZEND_API void zend_update_current_locale(void) /* {{{ */
{
+#ifdef ZEND_USE_TOLOWER_L
+# if defined(ZEND_WIN32) && defined(_MSC_VER)
current_locale = _get_current_locale();
+# else
+ current_locale = uselocale(0);
+# endif
+#endif
+#if defined(ZEND_WIN32) && defined(_MSC_VER)
+ if (MB_CUR_MAX > 1) {
+ unsigned int cp = ___lc_codepage_func();
+ CG(variable_width_locale) = 1;
+ // TODO: EUC-* are also ASCII compatible ???
+ CG(ascii_compatible_locale) =
+ cp == 65001; /* UTF-8 */
+ } else {
+ CG(variable_width_locale) = 0;
+ CG(ascii_compatible_locale) = 1;
+ }
+#elif defined(MB_CUR_MAX)
+ /* Check if current locale uses variable width encoding */
+ if (MB_CUR_MAX > 1) {
+#if HAVE_NL_LANGINFO
+ const char *charmap = nl_langinfo(CODESET);
+#else
+ char buf[16];
+ const char *charmap = NULL;
+ const char *locale = setlocale(LC_CTYPE, NULL);
+
+ if (locale) {
+ const char *dot = strchr(locale, '.');
+ const char *modifier;
+
+ if (dot) {
+ dot++;
+ modifier = strchr(dot, '@');
+ if (!modifier) {
+ charmap = dot;
+ } else if (modifier - dot < sizeof(buf)) {
+ memcpy(buf, dot, modifier - dot);
+ buf[modifier - dot] = '\0';
+ charmap = buf;
+ }
+ }
+ }
+#endif
+ CG(variable_width_locale) = 1;
+ CG(ascii_compatible_locale) = 0;
+
+ if (charmap) {
+ size_t len = strlen(charmap);
+ static const char *ascii_compatible_charmaps[] = {
+ "utf-8",
+ "utf8",
+ // TODO: EUC-* are also ASCII compatible ???
+ NULL
+ };
+ const char **p;
+ /* Check if current locale is ASCII compatible */
+ for (p = ascii_compatible_charmaps; *p; p++) {
+ if (zend_binary_strcasecmp(charmap, len, *p, strlen(*p)) == 0) {
+ CG(ascii_compatible_locale) = 1;
+ break;
+ }
+ }
+ }
+
+ } else {
+ CG(variable_width_locale) = 0;
+ CG(ascii_compatible_locale) = 1;
+ }
+#else
+ /* We can't determine current charset. Assume the worst case */
+ CG(variable_width_locale) = 1;
+ CG(ascii_compatible_locale) = 0;
+#endif
}
/* }}} */
-#endif
static zend_always_inline void zend_str_tolower_impl(char *dest, const char *str, size_t length) /* {{{ */ {
unsigned char *p = (unsigned char*)str;
@@ -2954,15 +3032,6 @@ ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2) /* {{{ */
}
/* }}} */
-ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC) /* {{{ */
-{
- zend_string *str;
-
- str = zend_strpprintf(0, "%.*G", (int) EG(precision), (double)Z_DVAL_P(op));
- ZVAL_NEW_STR(op, str);
-}
-/* }}} */
-
ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num) /* {{{ */
{
if ((zend_ulong)num <= 9) {
diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
index d543b7b03c..8996a3d959 100644
--- a/Zend/zend_operators.h
+++ b/Zend/zend_operators.h
@@ -54,7 +54,7 @@ ZEND_API zend_result ZEND_FASTCALL shift_left_function(zval *result, zval *op1,
ZEND_API zend_result ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *op2);
ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2);
-ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2);
+ZEND_API bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2);
ZEND_API zend_result ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2);
ZEND_API zend_result ZEND_FASTCALL is_identical_function(zval *result, zval *op1, zval *op2);
@@ -63,10 +63,10 @@ ZEND_API zend_result ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1
ZEND_API zend_result ZEND_FASTCALL is_smaller_function(zval *result, zval *op1, zval *op2);
ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, zval *op2);
-ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce);
-ZEND_API zend_bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce);
+ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce);
+ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce);
-static zend_always_inline zend_bool instanceof_function(
+static zend_always_inline bool instanceof_function(
const zend_class_entry *instance_ce, const zend_class_entry *ce) {
return instance_ce == ce || instanceof_function_slow(instance_ce, ce);
}
@@ -262,7 +262,6 @@ ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op);
ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_long(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_double(zval *op);
-ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base);
ZEND_API void ZEND_FASTCALL convert_to_null(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op);
ZEND_API void ZEND_FASTCALL convert_to_array(zval *op);
@@ -322,8 +321,8 @@ static zend_always_inline zend_string *zval_try_get_tmp_string(zval *op, zend_st
/* Like convert_to_string(), but returns whether the conversion succeeded and does not modify the
* zval in-place if it fails. */
-ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op);
-static zend_always_inline zend_bool try_convert_to_string(zval *op) {
+ZEND_API bool ZEND_FASTCALL _try_convert_to_string(zval *op);
+static zend_always_inline bool try_convert_to_string(zval *op) {
if (Z_TYPE_P(op) == IS_STRING) {
return 1;
}
@@ -406,10 +405,10 @@ again:
ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2);
-ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2);
+ZEND_API zend_result ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2);
ZEND_API int ZEND_FASTCALL numeric_compare_function(zval *op1, zval *op2);
-ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, zend_bool case_insensitive);
+ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool case_insensitive);
ZEND_API int ZEND_FASTCALL string_compare_function(zval *op1, zval *op2);
ZEND_API int ZEND_FASTCALL string_case_compare_function(zval *op1, zval *op2);
ZEND_API int ZEND_FASTCALL string_locale_compare_function(zval *op1, zval *op2);
@@ -442,77 +441,16 @@ ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2);
ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len);
ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len);
-ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC);
-
-#define convert_to_ex_master(pzv, lower_type, upper_type) \
- if (Z_TYPE_P(pzv)!=upper_type) { \
- convert_to_##lower_type(pzv); \
- }
-
-#define convert_to_explicit_type(pzv, type) \
- do { \
- switch (type) { \
- case IS_NULL: \
- convert_to_null(pzv); \
- break; \
- case IS_LONG: \
- convert_to_long(pzv); \
- break; \
- case IS_DOUBLE: \
- convert_to_double(pzv); \
- break; \
- case _IS_BOOL: \
- convert_to_boolean(pzv); \
- break; \
- case IS_ARRAY: \
- convert_to_array(pzv); \
- break; \
- case IS_OBJECT: \
- convert_to_object(pzv); \
- break; \
- case IS_STRING: \
- convert_to_string(pzv); \
- break; \
- default: \
- assert(0); \
- break; \
- } \
- } while (0);
-
-#define convert_to_explicit_type_ex(pzv, str_type) \
- if (Z_TYPE_P(pzv) != str_type) { \
- convert_to_explicit_type(pzv, str_type); \
- }
-
-#define convert_to_boolean_ex(pzv) do { \
- if (Z_TYPE_INFO_P(pzv) > IS_TRUE) { \
- convert_to_boolean(pzv); \
- } else if (Z_TYPE_INFO_P(pzv) < IS_FALSE) { \
- ZVAL_FALSE(pzv); \
- } \
- } while (0)
-#define convert_to_long_ex(pzv) convert_to_ex_master(pzv, long, IS_LONG)
-#define convert_to_double_ex(pzv) convert_to_ex_master(pzv, double, IS_DOUBLE)
-#define convert_to_string_ex(pzv) convert_to_ex_master(pzv, string, IS_STRING)
-#define convert_to_array_ex(pzv) convert_to_ex_master(pzv, array, IS_ARRAY)
-#define convert_to_object_ex(pzv) convert_to_ex_master(pzv, object, IS_OBJECT)
-#define convert_to_null_ex(pzv) convert_to_ex_master(pzv, null, IS_NULL)
-
-#define convert_scalar_to_number_ex(pzv) \
- if (Z_TYPE_P(pzv)!=IS_LONG && Z_TYPE_P(pzv)!=IS_DOUBLE) { \
- convert_scalar_to_number(pzv); \
- }
-
-#if defined(ZEND_WIN32) && !defined(ZTS) && defined(_MSC_VER)
-/* This performance improvement of tolower() on Windows gives 10-18% on bench.php */
-#define ZEND_USE_TOLOWER_L 1
-#endif
+#define convert_to_null_ex(zv) convert_to_null(zv)
+#define convert_to_boolean_ex(zv) convert_to_boolean(zv)
+#define convert_to_long_ex(zv) convert_to_long(zv)
+#define convert_to_double_ex(zv) convert_to_double(zv)
+#define convert_to_string_ex(zv) convert_to_string(zv)
+#define convert_to_array_ex(zv) convert_to_array(zv)
+#define convert_to_object_ex(zv) convert_to_object(zv)
+#define convert_scalar_to_number_ex(zv) convert_scalar_to_number(zv)
-#ifdef ZEND_USE_TOLOWER_L
ZEND_API void zend_update_current_locale(void);
-#else
-#define zend_update_current_locale()
-#endif
/* The offset in bytes between the value and type fields of a zval */
#define ZVAL_OFFSETOF_TYPE \
@@ -896,7 +834,7 @@ static zend_always_inline bool fast_equal_check_string(zval *op1, zval *op2)
return zend_compare(op1, op2) == 0;
}
-static zend_always_inline zend_bool fast_is_identical_function(zval *op1, zval *op2)
+static zend_always_inline bool fast_is_identical_function(zval *op1, zval *op2)
{
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
return 0;
@@ -906,7 +844,7 @@ static zend_always_inline zend_bool fast_is_identical_function(zval *op1, zval *
return zend_is_identical(op1, op2);
}
-static zend_always_inline zend_bool fast_is_not_identical_function(zval *op1, zval *op2)
+static zend_always_inline bool fast_is_not_identical_function(zval *op1, zval *op2)
{
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
return 1;
diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h
index f4acc9930e..cda61117ea 100644
--- a/Zend/zend_portability.h
+++ b/Zend/zend_portability.h
@@ -335,7 +335,7 @@ char *alloca();
#if (defined(HAVE_ALLOCA) || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN)
# define ZEND_ALLOCA_MAX_SIZE (32 * 1024)
# define ALLOCA_FLAG(name) \
- zend_bool name;
+ bool name;
# define SET_ALLOCA_FLAG(name) \
name = 1
# define do_alloca_ex(size, limit, use_heap) \
diff --git a/Zend/zend_ptr_stack.c b/Zend/zend_ptr_stack.c
index 2030cc132b..80c77e11d7 100644
--- a/Zend/zend_ptr_stack.c
+++ b/Zend/zend_ptr_stack.c
@@ -21,7 +21,7 @@
#include "zend_ptr_stack.h"
#include <stdarg.h>
-ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, zend_bool persistent)
+ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, bool persistent)
{
stack->top_element = stack->elements = NULL;
stack->top = stack->max = 0;
@@ -96,7 +96,7 @@ ZEND_API void zend_ptr_stack_reverse_apply(zend_ptr_stack *stack, void (*func)(v
}
-ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), zend_bool free_elements)
+ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), bool free_elements)
{
zend_ptr_stack_apply(stack, func);
if (free_elements) {
diff --git a/Zend/zend_ptr_stack.h b/Zend/zend_ptr_stack.h
index 2298e4f211..fd4b59d151 100644
--- a/Zend/zend_ptr_stack.h
+++ b/Zend/zend_ptr_stack.h
@@ -24,7 +24,7 @@ typedef struct _zend_ptr_stack {
int top, max;
void **elements;
void **top_element;
- zend_bool persistent;
+ bool persistent;
} zend_ptr_stack;
@@ -32,13 +32,13 @@ typedef struct _zend_ptr_stack {
BEGIN_EXTERN_C()
ZEND_API void zend_ptr_stack_init(zend_ptr_stack *stack);
-ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, zend_bool persistent);
+ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, bool persistent);
ZEND_API void zend_ptr_stack_n_push(zend_ptr_stack *stack, int count, ...);
ZEND_API void zend_ptr_stack_n_pop(zend_ptr_stack *stack, int count, ...);
ZEND_API void zend_ptr_stack_destroy(zend_ptr_stack *stack);
ZEND_API void zend_ptr_stack_apply(zend_ptr_stack *stack, void (*func)(void *));
ZEND_API void zend_ptr_stack_reverse_apply(zend_ptr_stack *stack, void (*func)(void *));
-ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), zend_bool free_elements);
+ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), bool free_elements);
ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack *stack);
END_EXTERN_C()
diff --git a/Zend/zend_signal.h b/Zend/zend_signal.h
index 08c9de20c7..ed89851fa8 100644
--- a/Zend/zend_signal.h
+++ b/Zend/zend_signal.h
@@ -56,8 +56,8 @@ typedef struct _zend_signal_globals_t {
int blocked; /* 1==TRUE, 0==FALSE */
int running; /* in signal handler execution */
int active; /* internal signal handling is enabled */
- zend_bool check; /* check for replaced handlers on shutdown */
- zend_bool reset; /* reset signal handlers on each request */
+ bool check; /* check for replaced handlers on shutdown */
+ bool reset; /* reset signal handlers on each request */
zend_signal_entry_t handlers[NSIG];
zend_signal_queue_t pstorage[ZEND_SIGNAL_QUEUE_SIZE], *phead, *ptail, *pavail; /* pending queue */
} zend_signal_globals_t;
diff --git a/Zend/zend_smart_str.h b/Zend/zend_smart_str.h
index 9211e54bee..756261e937 100644
--- a/Zend/zend_smart_str.h
+++ b/Zend/zend_smart_str.h
@@ -54,7 +54,7 @@ ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...)
END_EXTERN_C()
-static zend_always_inline size_t smart_str_alloc(smart_str *str, size_t len, zend_bool persistent) {
+static zend_always_inline size_t smart_str_alloc(smart_str *str, size_t len, bool persistent) {
if (UNEXPECTED(!str->s)) {
goto do_smart_str_realloc;
} else {
@@ -71,14 +71,14 @@ do_smart_str_realloc:
return len;
}
-static zend_always_inline char* smart_str_extend_ex(smart_str *dest, size_t len, zend_bool persistent) {
+static zend_always_inline char* smart_str_extend_ex(smart_str *dest, size_t len, bool persistent) {
size_t new_len = smart_str_alloc(dest, len, persistent);
char *ret = ZSTR_VAL(dest->s) + ZSTR_LEN(dest->s);
ZSTR_LEN(dest->s) = new_len;
return ret;
}
-static zend_always_inline void smart_str_free_ex(smart_str *str, zend_bool persistent) {
+static zend_always_inline void smart_str_free_ex(smart_str *str, bool persistent) {
if (str->s) {
zend_string_release_ex(str->s, persistent);
str->s = NULL;
@@ -108,35 +108,35 @@ static zend_always_inline zend_string *smart_str_extract(smart_str *str) {
}
}
-static zend_always_inline void smart_str_appendc_ex(smart_str *dest, char ch, zend_bool persistent) {
+static zend_always_inline void smart_str_appendc_ex(smart_str *dest, char ch, bool persistent) {
size_t new_len = smart_str_alloc(dest, 1, persistent);
ZSTR_VAL(dest->s)[new_len - 1] = ch;
ZSTR_LEN(dest->s) = new_len;
}
-static zend_always_inline void smart_str_appendl_ex(smart_str *dest, const char *str, size_t len, zend_bool persistent) {
+static zend_always_inline void smart_str_appendl_ex(smart_str *dest, const char *str, size_t len, bool persistent) {
size_t new_len = smart_str_alloc(dest, len, persistent);
memcpy(ZSTR_VAL(dest->s) + ZSTR_LEN(dest->s), str, len);
ZSTR_LEN(dest->s) = new_len;
}
-static zend_always_inline void smart_str_append_ex(smart_str *dest, const zend_string *src, zend_bool persistent) {
+static zend_always_inline void smart_str_append_ex(smart_str *dest, const zend_string *src, bool persistent) {
smart_str_appendl_ex(dest, ZSTR_VAL(src), ZSTR_LEN(src), persistent);
}
-static zend_always_inline void smart_str_append_smart_str_ex(smart_str *dest, const smart_str *src, zend_bool persistent) {
+static zend_always_inline void smart_str_append_smart_str_ex(smart_str *dest, const smart_str *src, bool persistent) {
if (src->s && ZSTR_LEN(src->s)) {
smart_str_append_ex(dest, src->s, persistent);
}
}
-static zend_always_inline void smart_str_append_long_ex(smart_str *dest, zend_long num, zend_bool persistent) {
+static zend_always_inline void smart_str_append_long_ex(smart_str *dest, zend_long num, bool persistent) {
char buf[32];
char *result = zend_print_long_to_buf(buf + sizeof(buf) - 1, num);
smart_str_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent);
}
-static zend_always_inline void smart_str_append_unsigned_ex(smart_str *dest, zend_ulong num, zend_bool persistent) {
+static zend_always_inline void smart_str_append_unsigned_ex(smart_str *dest, zend_ulong num, bool persistent) {
char buf[32];
char *result = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, num);
smart_str_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent);
diff --git a/Zend/zend_smart_string.h b/Zend/zend_smart_string.h
index 1f74a63218..d9f484b91e 100644
--- a/Zend/zend_smart_string.h
+++ b/Zend/zend_smart_string.h
@@ -51,7 +51,7 @@
ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len);
ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len);
-static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t len, zend_bool persistent) {
+static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t len, bool persistent) {
if (UNEXPECTED(!str->c) || UNEXPECTED(len >= str->a - str->len)) {
if (persistent) {
_smart_string_alloc_persistent(str, len);
@@ -62,7 +62,7 @@ static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t le
return str->len + len;
}
-static zend_always_inline void smart_string_free_ex(smart_string *str, zend_bool persistent) {
+static zend_always_inline void smart_string_free_ex(smart_string *str, bool persistent) {
if (str->c) {
pefree(str->c, persistent);
str->c = NULL;
@@ -76,25 +76,25 @@ static zend_always_inline void smart_string_0(smart_string *str) {
}
}
-static zend_always_inline void smart_string_appendc_ex(smart_string *dest, char ch, zend_bool persistent) {
+static zend_always_inline void smart_string_appendc_ex(smart_string *dest, char ch, bool persistent) {
dest->len = smart_string_alloc(dest, 1, persistent);
dest->c[dest->len - 1] = ch;
}
-static zend_always_inline void smart_string_appendl_ex(smart_string *dest, const char *str, size_t len, zend_bool persistent) {
+static zend_always_inline void smart_string_appendl_ex(smart_string *dest, const char *str, size_t len, bool persistent) {
size_t new_len = smart_string_alloc(dest, len, persistent);
memcpy(dest->c + dest->len, str, len);
dest->len = new_len;
}
-static zend_always_inline void smart_string_append_long_ex(smart_string *dest, zend_long num, zend_bool persistent) {
+static zend_always_inline void smart_string_append_long_ex(smart_string *dest, zend_long num, bool persistent) {
char buf[32];
char *result = zend_print_long_to_buf(buf + sizeof(buf) - 1, num);
smart_string_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent);
}
-static zend_always_inline void smart_string_append_unsigned_ex(smart_string *dest, zend_ulong num, zend_bool persistent) {
+static zend_always_inline void smart_string_append_unsigned_ex(smart_string *dest, zend_ulong num, bool persistent) {
char buf[32];
char *result = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, num);
smart_string_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent);
diff --git a/Zend/zend_stack.c b/Zend/zend_stack.c
index 1255c941a0..f587452bb7 100644
--- a/Zend/zend_stack.c
+++ b/Zend/zend_stack.c
@@ -141,7 +141,7 @@ ZEND_API void zend_stack_apply_with_argument(zend_stack *stack, int type, int (*
}
}
-ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), zend_bool free_elements)
+ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), bool free_elements)
{
int i;
diff --git a/Zend/zend_stack.h b/Zend/zend_stack.h
index a5aa962226..c0a325b778 100644
--- a/Zend/zend_stack.h
+++ b/Zend/zend_stack.h
@@ -40,7 +40,7 @@ ZEND_API void *zend_stack_base(const zend_stack *stack);
ZEND_API int zend_stack_count(const zend_stack *stack);
ZEND_API void zend_stack_apply(zend_stack *stack, int type, int (*apply_function)(void *element));
ZEND_API void zend_stack_apply_with_argument(zend_stack *stack, int type, int (*apply_function)(void *element, void *arg), void *arg);
-ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), zend_bool free_elements);
+ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), bool free_elements);
END_EXTERN_C()
#define ZEND_STACK_APPLY_TOPDOWN 1
diff --git a/Zend/zend_stream.h b/Zend/zend_stream.h
index 023e2d0555..e97a796730 100644
--- a/Zend/zend_stream.h
+++ b/Zend/zend_stream.h
@@ -58,7 +58,7 @@ typedef struct _zend_file_handle {
zend_stream_type type;
/* free_filename is used by wincache */
/* TODO: Clean up filename vs opened_path mess */
- zend_bool free_filename;
+ bool free_filename;
char *buf;
size_t len;
} zend_file_handle;
diff --git a/Zend/zend_string.c b/Zend/zend_string.c
index 570aeece61..ab074515aa 100644
--- a/Zend/zend_string.c
+++ b/Zend/zend_string.c
@@ -313,7 +313,7 @@ ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_intern
interned_string_init_request_handler = init_handler;
}
-ZEND_API void zend_interned_strings_switch_storage(zend_bool request)
+ZEND_API void zend_interned_strings_switch_storage(bool request)
{
if (request) {
zend_new_interned_string = interned_string_request_handler;
@@ -325,7 +325,7 @@ ZEND_API void zend_interned_strings_switch_storage(zend_bool request)
}
#if defined(__GNUC__) && defined(__i386__)
-ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
+ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
{
char *ptr = ZSTR_VAL(s1);
size_t delta = (char*)s2 - (char*)s1;
@@ -363,7 +363,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_str
}
#ifdef HAVE_VALGRIND
-ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
+ZEND_API bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
{
size_t len = ZSTR_LEN(s1);
char *ptr1 = ZSTR_VAL(s1);
@@ -393,7 +393,7 @@ ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_
#endif
#elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__)
-ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
+ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
{
char *ptr = ZSTR_VAL(s1);
size_t delta = (char*)s2 - (char*)s1;
@@ -431,7 +431,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_str
}
#ifdef HAVE_VALGRIND
-ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
+ZEND_API bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
{
size_t len = ZSTR_LEN(s1);
char *ptr1 = ZSTR_VAL(s1);
diff --git a/Zend/zend_string.h b/Zend/zend_string.h
index 96f1a6f4a0..88a2fddfb3 100644
--- a/Zend/zend_string.h
+++ b/Zend/zend_string.h
@@ -47,7 +47,7 @@ ZEND_API void zend_interned_strings_dtor(void);
ZEND_API void zend_interned_strings_activate(void);
ZEND_API void zend_interned_strings_deactivate(void);
ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler);
-ZEND_API void zend_interned_strings_switch_storage(zend_bool request);
+ZEND_API void zend_interned_strings_switch_storage(bool request);
ZEND_API extern zend_string *zend_empty_string;
ZEND_API extern zend_string *zend_one_char_string[256];
@@ -332,21 +332,21 @@ static zend_always_inline void zend_string_release_ex(zend_string *s, bool persi
#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__)))
BEGIN_EXTERN_C()
-ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2);
+ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2);
END_EXTERN_C()
#else
-static zend_always_inline zend_bool zend_string_equal_val(zend_string *s1, zend_string *s2)
+static zend_always_inline bool zend_string_equal_val(zend_string *s1, zend_string *s2)
{
return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
}
#endif
-static zend_always_inline zend_bool zend_string_equal_content(zend_string *s1, zend_string *s2)
+static zend_always_inline bool zend_string_equal_content(zend_string *s1, zend_string *s2)
{
return ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2);
}
-static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_string *s2)
+static zend_always_inline bool zend_string_equals(zend_string *s1, zend_string *s2)
{
return s1 == s2 || zend_string_equal_content(s1, s2);
}
@@ -551,6 +551,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_FALSE, "false") \
_(ZEND_STR_NULL_LOWERCASE, "null") \
_(ZEND_STR_MIXED, "mixed") \
+ _(ZEND_STR_SLEEP, "__sleep") \
+ _(ZEND_STR_WAKEUP, "__wakeup") \
typedef enum _zend_known_string_id {
diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c
index e88fbd001f..9173a15031 100644
--- a/Zend/zend_strtod.c
+++ b/Zend/zend_strtod.c
@@ -4467,9 +4467,6 @@ ZEND_API double zend_oct_strtod(const char *str, const char **endptr)
return 0.0;
}
- /* skip leading zero */
- s++;
-
while ((c = *s++)) {
if (c < '0' || c > '7') {
/* break and return the current value if the number is not well-formed
diff --git a/Zend/zend_ts_hash.c b/Zend/zend_ts_hash.c
index d4e972d0e3..9bbf42c66d 100644
--- a/Zend/zend_ts_hash.c
+++ b/Zend/zend_ts_hash.c
@@ -57,7 +57,7 @@ static void end_write(TsHashTable *ht)
}
/* delegates */
-ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
+ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
{
#ifdef ZTS
ht->mx_reader = tsrm_mutex_alloc();
@@ -271,7 +271,7 @@ ZEND_API void zend_ts_hash_sort(TsHashTable *ht, sort_func_t sort_func, bucket_c
end_write(ht);
}
-ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, zend_bool ordered)
+ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, bool ordered)
{
int retval;
diff --git a/Zend/zend_ts_hash.h b/Zend/zend_ts_hash.h
index d1b41b1d6f..12d3c65eb1 100644
--- a/Zend/zend_ts_hash.h
+++ b/Zend/zend_ts_hash.h
@@ -35,7 +35,7 @@ BEGIN_EXTERN_C()
#define TS_HASH(table) (&(table->hash))
/* startup/shutdown */
-ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
+ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent);
ZEND_API void zend_ts_hash_destroy(TsHashTable *ht);
ZEND_API void zend_ts_hash_clean(TsHashTable *ht);
@@ -69,7 +69,7 @@ ZEND_API void zend_ts_hash_copy_to_hash(HashTable *target, TsHashTable *source,
ZEND_API void zend_ts_hash_merge(TsHashTable *target, TsHashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite);
ZEND_API void zend_ts_hash_merge_ex(TsHashTable *target, TsHashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam);
ZEND_API void zend_ts_hash_sort(TsHashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
-ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, zend_bool ordered);
+ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, bool ordered);
ZEND_API zval *zend_ts_hash_minmax(TsHashTable *ht, bucket_compare_func_t compar, int flag);
ZEND_API int zend_ts_hash_num_elements(TsHashTable *ht);
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 82cf20f1b5..dbe2df23de 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -130,7 +130,7 @@ typedef struct {
* are only supported since C++20). */
void *ptr;
uint32_t type_mask;
- /* TODO: We could use the extra 32-bit of padding on 64-bit systems. */
+ uint32_t ce_cache__ptr; /* map_ptr offset */
} zend_type;
typedef struct {
@@ -138,13 +138,15 @@ typedef struct {
zend_type types[1];
} zend_type_list;
-#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 24
-#define _ZEND_TYPE_MASK ((1u << 24) - 1)
+#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25
+#define _ZEND_TYPE_MASK ((1u << 25) - 1)
/* Only one of these bits may be set. */
-#define _ZEND_TYPE_NAME_BIT (1u << 23)
-#define _ZEND_TYPE_CE_BIT (1u << 22)
-#define _ZEND_TYPE_LIST_BIT (1u << 21)
+#define _ZEND_TYPE_NAME_BIT (1u << 24)
+#define _ZEND_TYPE_CE_BIT (1u << 23)
+#define _ZEND_TYPE_LIST_BIT (1u << 22)
#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CE_BIT|_ZEND_TYPE_NAME_BIT)
+/* CE cached in map_ptr area */
+#define _ZEND_TYPE_CACHE_BIT (1u << 21)
/* Whether the type list is arena allocated */
#define _ZEND_TYPE_ARENA_BIT (1u << 20)
/* Type mask excluding the flags above. */
@@ -167,6 +169,9 @@ typedef struct {
#define ZEND_TYPE_HAS_LIST(t) \
((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0)
+#define ZEND_TYPE_HAS_CE_CACHE(t) \
+ ((((t).type_mask) & _ZEND_TYPE_CACHE_BIT) != 0)
+
#define ZEND_TYPE_USES_ARENA(t) \
((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0)
@@ -185,6 +190,13 @@ typedef struct {
#define ZEND_TYPE_LIST(t) \
((zend_type_list *) (t).ptr)
+#define ZEND_TYPE_CE_CACHE(t) \
+ (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache))
+
+#define ZEND_TYPE_SET_CE_CACHE(t, ce) do { \
+ *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache)) = ce; \
+ } while (0)
+
#define ZEND_TYPE_LIST_SIZE(num_types) \
(sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type))
@@ -254,10 +266,10 @@ typedef struct {
(((t).type_mask & _ZEND_TYPE_NULLABLE_BIT) != 0)
#define ZEND_TYPE_INIT_NONE(extra_flags) \
- { NULL, (extra_flags) }
+ { NULL, (extra_flags), 0 }
#define ZEND_TYPE_INIT_MASK(_type_mask) \
- { NULL, (_type_mask) }
+ { NULL, (_type_mask), 0 }
#define ZEND_TYPE_INIT_CODE(code, allow_null, extra_flags) \
ZEND_TYPE_INIT_MASK(((code) == _IS_BOOL ? MAY_BE_BOOL : ((code) == IS_MIXED ? MAY_BE_ANY : (1 << (code)))) \
@@ -265,10 +277,10 @@ typedef struct {
#define ZEND_TYPE_INIT_PTR(ptr, type_kind, allow_null, extra_flags) \
{ (void *) (ptr), \
- (type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags) }
+ (type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags), 0 }
#define ZEND_TYPE_INIT_PTR_MASK(ptr, type_mask) \
- { (void *) (ptr), (type_mask) }
+ { (void *) (ptr), (type_mask), 0 }
#define ZEND_TYPE_INIT_CE(_ce, allow_null, extra_flags) \
ZEND_TYPE_INIT_PTR(_ce, _ZEND_TYPE_CE_BIT, allow_null, extra_flags)
@@ -613,6 +625,7 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define GC_ADDREF_EX(p, rc) zend_gc_addref_ex(&(p)->gc, rc)
#define GC_DELREF_EX(p, rc) zend_gc_delref_ex(&(p)->gc, rc)
#define GC_TRY_ADDREF(p) zend_gc_try_addref(&(p)->gc)
+#define GC_TRY_DELREF(p) zend_gc_try_delref(&(p)->gc)
#define GC_TYPE_MASK 0x0000000f
#define GC_FLAGS_MASK 0x000003f0
@@ -1130,7 +1143,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#endif
#if ZEND_RC_DEBUG
-extern ZEND_API zend_bool zend_rc_debug;
+extern ZEND_API bool zend_rc_debug;
# define ZEND_RC_MOD_CHECK(p) do { \
if (zend_rc_debug && zval_gc_type((p)->u.type_info) != IS_OBJECT) { \
ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \
@@ -1168,6 +1181,13 @@ static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) {
}
}
+static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) {
+ if (!(p->u.type_info & GC_IMMUTABLE)) {
+ ZEND_RC_MOD_CHECK(p);
+ --p->refcount;
+ }
+}
+
static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) {
ZEND_ASSERT(p->refcount > 0);
ZEND_RC_MOD_CHECK(p);
@@ -1339,34 +1359,27 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
zend_string *_str = Z_STR_P(_zv); \
ZEND_ASSERT(Z_REFCOUNTED_P(_zv)); \
ZEND_ASSERT(!ZSTR_IS_INTERNED(_str)); \
- Z_DELREF_P(_zv); \
ZVAL_NEW_STR(_zv, zend_string_init( \
ZSTR_VAL(_str), ZSTR_LEN(_str), 0)); \
+ GC_DELREF(_str); \
} \
} while (0)
#define SEPARATE_ARRAY(zv) do { \
- zval *_zv = (zv); \
- zend_array *_arr = Z_ARR_P(_zv); \
- if (UNEXPECTED(GC_REFCOUNT(_arr) > 1)) { \
- if (Z_REFCOUNTED_P(_zv)) { \
- GC_DELREF(_arr); \
- } \
- ZVAL_ARR(_zv, zend_array_dup(_arr)); \
- } \
- } while (0)
-
-#define SEPARATE_ZVAL_IF_NOT_REF(zv) do { \
zval *__zv = (zv); \
- if (Z_TYPE_P(__zv) == IS_ARRAY) { \
- SEPARATE_ARRAY(__zv); \
+ zend_array *_arr = Z_ARR_P(__zv); \
+ if (UNEXPECTED(GC_REFCOUNT(_arr) > 1)) { \
+ ZVAL_ARR(__zv, zend_array_dup(_arr)); \
+ GC_TRY_DELREF(_arr); \
} \
} while (0)
#define SEPARATE_ZVAL_NOREF(zv) do { \
zval *_zv = (zv); \
ZEND_ASSERT(Z_TYPE_P(_zv) != IS_REFERENCE); \
- SEPARATE_ZVAL_IF_NOT_REF(_zv); \
+ if (Z_TYPE_P(_zv) == IS_ARRAY) { \
+ SEPARATE_ARRAY(_zv); \
+ } \
} while (0)
#define SEPARATE_ZVAL(zv) do { \
@@ -1384,13 +1397,8 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
break; \
} \
} \
- SEPARATE_ZVAL_IF_NOT_REF(_zv); \
- } while (0)
-
-#define SEPARATE_ARG_IF_REF(varptr) do { \
- ZVAL_DEREF(varptr); \
- if (Z_REFCOUNTED_P(varptr)) { \
- Z_ADDREF_P(varptr); \
+ if (Z_TYPE_P(_zv) == IS_ARRAY) { \
+ SEPARATE_ARRAY(_zv); \
} \
} while (0)
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 75062824f2..09dce52b68 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -446,7 +446,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(16, ZEND_IS_IDENTICAL, CONST|TMP|VAR|CV, CONST|T
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
@@ -461,7 +461,7 @@ ZEND_VM_HANDLER(196, ZEND_CASE_STRICT, TMP|VAR, CONST|TMP|VAR|CV)
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
@@ -475,7 +475,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(17, ZEND_IS_NOT_IDENTICAL, CONST|TMP|VAR|CV, CON
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
@@ -1727,8 +1727,9 @@ ZEND_VM_C_LABEL(fetch_this):
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
retval = &EG(uninitialized_zval);
@@ -1746,8 +1747,9 @@ ZEND_VM_C_LABEL(fetch_this):
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
retval = &EG(uninitialized_zval);
@@ -2822,6 +2824,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
ZEND_VM_LEAVE();
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
zend_detach_symbol_table(execute_data);
+ zend_destroy_static_vars(&EX(func)->op_array);
destroy_op_array(&EX(func)->op_array);
efree_size(EX(func), sizeof(zend_op_array));
#ifdef ZEND_PREFER_RELOAD
@@ -3892,7 +3895,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@@ -4010,7 +4013,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@@ -4114,7 +4117,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@@ -4225,7 +4228,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -5002,7 +5005,7 @@ ZEND_VM_C_LABEL(send_again):
HashTable *ht = Z_ARRVAL_P(args);
zval *arg, *top;
zend_string *name;
- zend_bool have_named_params = 0;
+ bool have_named_params = 0;
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht));
@@ -5073,7 +5076,7 @@ ZEND_VM_C_LABEL(send_again):
} else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(args);
zend_object_iterator *iter;
- zend_bool have_named_params = 0;
+ bool have_named_params = 0;
if (!ce || !ce->get_iterator) {
zend_type_error("Only arrays and Traversables can be unpacked");
@@ -5090,26 +5093,27 @@ ZEND_VM_C_LABEL(send_again):
HANDLE_EXCEPTION();
}
- if (iter->funcs->rewind) {
- iter->funcs->rewind(iter);
+ const zend_object_iterator_funcs *funcs = iter->funcs;
+ if (funcs->rewind) {
+ funcs->rewind(iter);
}
- for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
+ for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- arg = iter->funcs->get_current_data(iter);
+ arg = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zend_string *name = NULL;
- if (iter->funcs->get_current_key) {
+ if (funcs->get_current_key) {
zval key;
- iter->funcs->get_current_key(iter, &key);
+ funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@@ -5171,7 +5175,7 @@ ZEND_VM_C_LABEL(send_again):
ZEND_CALL_NUM_ARGS(EX(call))++;
}
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
}
zend_iterator_dtor(iter);
@@ -5235,7 +5239,7 @@ ZEND_VM_C_LABEL(send_array):
arg_num = 1;
param = ZEND_CALL_ARG(EX(call), 1);
ZEND_HASH_FOREACH_VAL(ht, arg) {
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
if (skip > 0) {
skip--;
continue;
@@ -5271,7 +5275,7 @@ ZEND_VM_C_LABEL(send_array):
FREE_OP2();
} else {
zend_string *name;
- zend_bool have_named_params;
+ bool have_named_params;
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
arg_num = 1;
param = ZEND_CALL_ARG(EX(call), 1);
@@ -5292,7 +5296,7 @@ ZEND_VM_C_LABEL(send_array):
HANDLE_EXCEPTION();
}
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
@@ -5819,7 +5823,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
}
}
- zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
+ zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@@ -5950,9 +5954,11 @@ ZEND_VM_HANDLER(147, ZEND_ADD_ARRAY_UNPACK, ANY, ANY)
{
USE_OPLINE
zval *op1;
+ HashTable *result_ht;
SAVE_OPLINE();
op1 = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ result_ht = Z_ARRVAL_P(EX_VAR(opline->result.var));
ZEND_VM_C_LABEL(add_unpack_again):
if (EXPECTED(Z_TYPE_P(op1) == IS_ARRAY)) {
@@ -5961,16 +5967,14 @@ ZEND_VM_C_LABEL(add_unpack_again):
zend_string *key;
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
+ val = Z_REFVAL_P(val);
+ }
+ Z_TRY_ADDREF_P(val);
if (key) {
- zend_throw_error(NULL, "Cannot unpack array with string keys");
- FREE_OP1();
- HANDLE_EXCEPTION();
+ zend_hash_update(result_ht, key, val);
} else {
- if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
- val = Z_REFVAL_P(val);
- }
- Z_TRY_ADDREF_P(val);
- if (!zend_hash_next_index_insert(Z_ARRVAL_P(EX_VAR(opline->result.var)), val)) {
+ if (!zend_hash_next_index_insert(result_ht, val)) {
zend_cannot_add_element();
zval_ptr_dtor_nogc(val);
break;
@@ -5995,48 +5999,59 @@ ZEND_VM_C_LABEL(add_unpack_again):
HANDLE_EXCEPTION();
}
- if (iter->funcs->rewind) {
- iter->funcs->rewind(iter);
+ const zend_object_iterator_funcs *funcs = iter->funcs;
+ if (funcs->rewind) {
+ funcs->rewind(iter);
}
- for (; iter->funcs->valid(iter) == SUCCESS; ) {
+ for (; funcs->valid(iter) == SUCCESS; ) {
zval *val;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- val = iter->funcs->get_current_data(iter);
+ val = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- if (iter->funcs->get_current_key) {
- zval key;
- iter->funcs->get_current_key(iter, &key);
+ zval key;
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- if (UNEXPECTED(Z_TYPE(key) != IS_LONG)) {
+ if (UNEXPECTED(Z_TYPE(key) != IS_LONG && Z_TYPE(key) != IS_STRING)) {
zend_throw_error(NULL,
- (Z_TYPE(key) == IS_STRING) ?
- "Cannot unpack Traversable with string keys" :
- "Cannot unpack Traversable with non-integer keys");
+ "Keys must be of type int|string during array unpacking");
zval_ptr_dtor(&key);
break;
}
+ } else {
+ ZVAL_UNDEF(&key);
}
ZVAL_DEREF(val);
Z_TRY_ADDREF_P(val);
- if (!zend_hash_next_index_insert(Z_ARRVAL_P(EX_VAR(opline->result.var)), val)) {
- zend_cannot_add_element();
- zval_ptr_dtor_nogc(val);
+ zend_ulong num_key;
+ if (Z_TYPE(key) == IS_STRING && !ZEND_HANDLE_NUMERIC(Z_STR(key), num_key)) {
+ zend_hash_update(result_ht, Z_STR(key), val);
+ zval_ptr_dtor_str(&key);
+ } else {
+ if (!zend_hash_next_index_insert(result_ht, val)) {
+ zend_cannot_add_element();
+ zval_ptr_dtor_nogc(val);
+ break;
+ }
}
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
+ if (UNEXPECTED(EG(exception))) {
+ break;
+ }
}
zend_iterator_dtor(iter);
@@ -6124,6 +6139,11 @@ ZEND_VM_COLD_CONST_HANDLER(51, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
} else {
ZVAL_EMPTY_ARRAY(result);
}
+ } else if (Z_OBJ_P(expr)->properties == NULL
+ && Z_OBJ_HT_P(expr)->get_properties_for == NULL
+ && Z_OBJ_HT_P(expr)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -6217,6 +6237,7 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER
zend_vm_stack_free_call_frame(call);
}
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -6372,11 +6393,8 @@ ZEND_VM_C_LABEL(offset_again):
}
}
ZEND_VM_C_LABEL(str_index_dim):
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
ZEND_VM_C_LABEL(num_index_dim):
@@ -6397,6 +6415,7 @@ ZEND_VM_C_LABEL(num_index_dim):
hval = 1;
ZEND_VM_C_GOTO(num_index_dim);
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
ZEND_VM_C_GOTO(num_index_dim);
} else if (OP2_TYPE == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -6528,7 +6547,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR)
FREE_OP1_IF_VAR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
FREE_OP1();
if (UNEXPECTED(EG(exception))) {
@@ -6621,7 +6640,7 @@ ZEND_VM_COLD_CONST_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
if (OP1_TYPE == IS_VAR) {
FREE_OP1_VAR_PTR();
@@ -6675,16 +6694,9 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -6745,15 +6757,16 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
}
}
} else {
+ const zend_object_iterator_funcs *funcs = iter->funcs;
if (EXPECTED(++iter->index > 0)) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
- if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
+ if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@@ -6762,7 +6775,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
ZEND_VM_C_GOTO(fe_fetch_r_exit);
}
}
- value = iter->funcs->get_current_data(iter);
+ value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -6772,8 +6785,8 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
ZEND_VM_C_GOTO(fe_fetch_r_exit);
}
if (RETURN_VALUE_USED(opline)) {
- if (iter->funcs->get_current_key) {
- iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -6827,16 +6840,9 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -6905,15 +6911,16 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
}
}
} else {
+ const zend_object_iterator_funcs *funcs = iter->funcs;
if (++iter->index > 0) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
- if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
+ if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@@ -6922,7 +6929,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
ZEND_VM_C_GOTO(fe_fetch_w_exit);
}
}
- value = iter->funcs->get_current_data(iter);
+ value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -6932,8 +6939,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
ZEND_VM_C_GOTO(fe_fetch_w_exit);
}
if (RETURN_VALUE_USED(opline)) {
- if (iter->funcs->get_current_key) {
- iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -7095,7 +7102,7 @@ ZEND_VM_C_LABEL(isset_again):
ZEND_VM_C_GOTO(num_index_prop);
}
}
- value = zend_hash_find_ex_ind(ht, str, OP2_TYPE == IS_CONST);
+ value = zend_hash_find_ex(ht, str, OP2_TYPE == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
ZEND_VM_C_LABEL(num_index_prop):
@@ -7203,7 +7210,7 @@ ZEND_VM_HANDLER(194, ZEND_ARRAY_KEY_EXISTS, CV|TMPVAR|CONST, CV|TMPVAR|CONST)
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -7530,7 +7537,8 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST)
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
- if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
+ ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname));
+ if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
@@ -7571,7 +7579,8 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
- if (zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
+ ce = zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
+ if (!ce) {
HANDLE_EXCEPTION();
}
}
@@ -7581,12 +7590,14 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY)
+ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, NUM)
{
+ zend_function *func;
USE_OPLINE
SAVE_OPLINE();
- do_bind_function(RT_CONSTANT(opline, opline->op1));
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
+ do_bind_function(func, RT_CONSTANT(opline, opline->op1));
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -7609,7 +7620,7 @@ ZEND_VM_HANDLER(138, ZEND_INSTANCEOF, TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR, C
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
@@ -7851,23 +7862,14 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED, CACHE_SLOT)
+ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
{
USE_OPLINE
zend_function *func;
- zval *zfunc;
zval *object;
zend_class_entry *called_scope;
- func = CACHED_PTR(opline->extended_value);
- if (UNEXPECTED(func == NULL)) {
- zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
- ZEND_ASSERT(zfunc != NULL);
- func = Z_FUNC_P(zfunc);
- ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
- CACHE_PTR(opline->extended_value, func);
- }
-
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
if (Z_TYPE(EX(This)) == IS_OBJECT) {
called_scope = Z_OBJCE(EX(This));
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
@@ -8288,10 +8290,12 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY)
value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) {
ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value));
- FREE_OP1();
+ if (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(value);
+ }
ZEND_VM_NEXT_OPCODE();
} else {
- zend_bool strict;
+ bool strict;
if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) {
value = Z_REFVAL_P(value);
@@ -8312,8 +8316,18 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY)
zend_string *str;
zval tmp;
+ if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) {
+ zend_error(E_DEPRECATED,
+ "strlen(): Passing null to parameter #1 ($string) of type string is deprecated");
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ ZVAL_LONG(EX_VAR(opline->result.var), 0);
+ break;
+ }
+
ZVAL_COPY(&tmp, value);
- if (zend_parse_arg_str_weak(&tmp, &str)) {
+ if (zend_parse_arg_str_weak(&tmp, &str, 1)) {
ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str));
zval_ptr_dtor(&tmp);
break;
@@ -8545,7 +8559,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
if (ret == NULL) {
@@ -8645,16 +8659,10 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
if (!ht) {
- ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
ht = zend_array_dup(EX(func)->op_array.static_variables);
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
- } else if (GC_REFCOUNT(ht) > 1) {
- if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
- GC_DELREF(ht);
- }
- ht = zend_array_dup(ht);
- ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
}
+ ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));
@@ -8703,6 +8711,16 @@ ZEND_VM_HOT_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
}
}
+ZEND_VM_HANDLER(200, ZEND_FETCH_GLOBALS, UNUSED, UNUSED)
+{
+ USE_OPLINE
+
+ /* For symbol tables we need to deal with exactly the same problems as for property tables. */
+ ZVAL_ARR(EX_VAR(opline->result.var),
+ zend_proptable_to_symtable(&EG(symbol_table), /* always_duplicate */ 1));
+ ZEND_VM_NEXT_OPCODE();
+}
+
ZEND_VM_HANDLER(186, ZEND_ISSET_ISEMPTY_THIS, UNUSED, UNUSED)
{
USE_OPLINE
@@ -8879,33 +8897,69 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(189, ZEND_IN_ARRAY, CONST|TMP|VAR|CV, CONST, NUM
HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
zval *result;
- SAVE_OPLINE();
- op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST);
- } else if (opline->extended_value) {
+ if (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(op1);
+ }
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+
+ if (opline->extended_value) {
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
result = zend_hash_index_find(ht, Z_LVAL_P(op1));
- } else {
- result = NULL;
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ SAVE_OPLINE();
+ if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ FREE_OP1();
+ ZEND_VM_SMART_BRANCH(result, 0);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ result = zend_hash_index_find(ht, Z_LVAL_P(op1));
+ FREE_OP1();
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ } else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ ZVAL_UNDEFINED_OP1();
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ ZVAL_UNDEFINED_OP1();
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
result = zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
+ ZEND_VM_SMART_BRANCH(result, 0);
} else {
zend_string *key;
- zval key_tmp, *val;
+ zval key_tmp;
- result = NULL;
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ FREE_OP1();
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ }
+
+ SAVE_OPLINE();
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- result = val;
- break;
+ FREE_OP1();
+ ZEND_VM_SMART_BRANCH(1, 1);
}
} ZEND_HASH_FOREACH_END();
}
FREE_OP1();
- ZEND_VM_SMART_BRANCH(result, 1);
+ ZEND_VM_SMART_BRANCH(0, 1);
}
ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
@@ -8919,7 +8973,7 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
while (1) {
if (Z_TYPE_P(op1) == IS_ARRAY) {
- count = zend_array_count(Z_ARRVAL_P(op1));
+ count = zend_hash_num_elements(Z_ARRVAL_P(op1));
break;
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
zend_object *zobj = Z_OBJ_P(op1);
@@ -9302,7 +9356,7 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op1_type == IS_CV && (op->op2_t
/* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
@@ -9315,7 +9369,7 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_IDENTICAL, op->op1_type == IS_CV && (op->o
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
@@ -9639,16 +9693,9 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_FE_FETCH_R, op->op2_type == IS_CV && (op1_inf
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 9d7eae0311..385941e595 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1146,6 +1146,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper
ZEND_VM_LEAVE();
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
zend_detach_symbol_table(execute_data);
+ zend_destroy_static_vars(&EX(func)->op_array);
destroy_op_array(&EX(func)->op_array);
efree_size(EX(func), sizeof(zend_op_array));
#ifdef ZEND_PREFER_RELOAD
@@ -1225,7 +1226,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 0 ? EX_VAR(opline->result.var) : &retval;
@@ -1286,7 +1287,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 1 ? EX_VAR(opline->result.var) : &retval;
@@ -1451,7 +1452,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 0 ? EX_VAR(opline->result.var) : &retval;
@@ -1545,7 +1546,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 1 ? EX_VAR(opline->result.var) : &retval;
@@ -1640,7 +1641,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@@ -1744,7 +1745,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 0 ? EX_VAR(opline->result.var) : &retval;
@@ -1852,7 +1853,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = 1 ? EX_VAR(opline->result.var) : &retval;
@@ -1960,7 +1961,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@@ -2115,7 +2116,7 @@ send_again:
HashTable *ht = Z_ARRVAL_P(args);
zval *arg, *top;
zend_string *name;
- zend_bool have_named_params = 0;
+ bool have_named_params = 0;
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht));
@@ -2186,7 +2187,7 @@ send_again:
} else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(args);
zend_object_iterator *iter;
- zend_bool have_named_params = 0;
+ bool have_named_params = 0;
if (!ce || !ce->get_iterator) {
zend_type_error("Only arrays and Traversables can be unpacked");
@@ -2203,26 +2204,27 @@ send_again:
HANDLE_EXCEPTION();
}
- if (iter->funcs->rewind) {
- iter->funcs->rewind(iter);
+ const zend_object_iterator_funcs *funcs = iter->funcs;
+ if (funcs->rewind) {
+ funcs->rewind(iter);
}
- for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
+ for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- arg = iter->funcs->get_current_data(iter);
+ arg = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zend_string *name = NULL;
- if (iter->funcs->get_current_key) {
+ if (funcs->get_current_key) {
zval key;
- iter->funcs->get_current_key(iter, &key);
+ funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@@ -2284,7 +2286,7 @@ send_again:
ZEND_CALL_NUM_ARGS(EX(call))++;
}
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
}
zend_iterator_dtor(iter);
@@ -2348,7 +2350,7 @@ send_array:
arg_num = 1;
param = ZEND_CALL_ARG(EX(call), 1);
ZEND_HASH_FOREACH_VAL(ht, arg) {
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
if (skip > 0) {
skip--;
continue;
@@ -2384,7 +2386,7 @@ send_array:
FREE_OP(opline->op2_type, opline->op2.var);
} else {
zend_string *name;
- zend_bool have_named_params;
+ bool have_named_params;
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
arg_num = 1;
param = ZEND_CALL_ARG(EX(call), 1);
@@ -2405,7 +2407,7 @@ send_array:
HANDLE_EXCEPTION();
}
- zend_bool must_wrap = 0;
+ bool must_wrap = 0;
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
@@ -2499,9 +2501,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER(
{
USE_OPLINE
zval *op1;
+ HashTable *result_ht;
SAVE_OPLINE();
op1 = get_zval_ptr(opline->op1_type, opline->op1, BP_VAR_R);
+ result_ht = Z_ARRVAL_P(EX_VAR(opline->result.var));
add_unpack_again:
if (EXPECTED(Z_TYPE_P(op1) == IS_ARRAY)) {
@@ -2510,16 +2514,14 @@ add_unpack_again:
zend_string *key;
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
+ val = Z_REFVAL_P(val);
+ }
+ Z_TRY_ADDREF_P(val);
if (key) {
- zend_throw_error(NULL, "Cannot unpack array with string keys");
- FREE_OP(opline->op1_type, opline->op1.var);
- HANDLE_EXCEPTION();
+ zend_hash_update(result_ht, key, val);
} else {
- if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
- val = Z_REFVAL_P(val);
- }
- Z_TRY_ADDREF_P(val);
- if (!zend_hash_next_index_insert(Z_ARRVAL_P(EX_VAR(opline->result.var)), val)) {
+ if (!zend_hash_next_index_insert(result_ht, val)) {
zend_cannot_add_element();
zval_ptr_dtor_nogc(val);
break;
@@ -2544,48 +2546,59 @@ add_unpack_again:
HANDLE_EXCEPTION();
}
- if (iter->funcs->rewind) {
- iter->funcs->rewind(iter);
+ const zend_object_iterator_funcs *funcs = iter->funcs;
+ if (funcs->rewind) {
+ funcs->rewind(iter);
}
- for (; iter->funcs->valid(iter) == SUCCESS; ) {
+ for (; funcs->valid(iter) == SUCCESS; ) {
zval *val;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- val = iter->funcs->get_current_data(iter);
+ val = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- if (iter->funcs->get_current_key) {
- zval key;
- iter->funcs->get_current_key(iter, &key);
+ zval key;
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
- if (UNEXPECTED(Z_TYPE(key) != IS_LONG)) {
+ if (UNEXPECTED(Z_TYPE(key) != IS_LONG && Z_TYPE(key) != IS_STRING)) {
zend_throw_error(NULL,
- (Z_TYPE(key) == IS_STRING) ?
- "Cannot unpack Traversable with string keys" :
- "Cannot unpack Traversable with non-integer keys");
+ "Keys must be of type int|string during array unpacking");
zval_ptr_dtor(&key);
break;
}
+ } else {
+ ZVAL_UNDEF(&key);
}
ZVAL_DEREF(val);
Z_TRY_ADDREF_P(val);
- if (!zend_hash_next_index_insert(Z_ARRVAL_P(EX_VAR(opline->result.var)), val)) {
- zend_cannot_add_element();
- zval_ptr_dtor_nogc(val);
+ zend_ulong num_key;
+ if (Z_TYPE(key) == IS_STRING && !ZEND_HANDLE_NUMERIC(Z_STR(key), num_key)) {
+ zend_hash_update(result_ht, Z_STR(key), val);
+ zval_ptr_dtor_str(&key);
+ } else {
+ if (!zend_hash_next_index_insert(result_ht, val)) {
+ zend_cannot_add_element();
+ zval_ptr_dtor_nogc(val);
+ break;
+ }
}
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
+ if (UNEXPECTED(EG(exception))) {
+ break;
+ }
}
zend_iterator_dtor(iter);
@@ -2803,7 +2816,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
ce = Z_CE_P(zv);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
SAVE_OPLINE();
- if (zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) {
+ ce = zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
+ if (!ce) {
HANDLE_EXCEPTION();
}
}
@@ -2815,10 +2829,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
+ zend_function *func;
USE_OPLINE
SAVE_OPLINE();
- do_bind_function(RT_CONSTANT(opline, opline->op1));
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
+ do_bind_function(func, RT_CONSTANT(opline, opline->op1));
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -3167,7 +3183,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
if (ret == NULL) {
@@ -3303,7 +3319,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
EG(current_execute_data) = call;
#if ZEND_DEBUG
- zend_bool should_throw = zend_internal_call_should_throw(fbc, call);
+ bool should_throw = zend_internal_call_should_throw(fbc, call);
#endif
if (ret == NULL) {
@@ -4627,6 +4643,11 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H
} else {
ZVAL_EMPTY_ARRAY(result);
}
+ } else if (Z_OBJ_P(expr)->properties == NULL
+ && Z_OBJ_HT_P(expr)->get_properties_for == NULL
+ && Z_OBJ_HT_P(expr)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -4719,6 +4740,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN
zend_vm_stack_free_call_frame(call);
}
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -4788,6 +4810,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_
zend_vm_stack_free_call_frame(call);
}
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -4851,7 +4874,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
@@ -4943,7 +4966,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
if (IS_CONST == IS_VAR) {
@@ -5139,6 +5162,32 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_CONST_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_function *func;
+ zval *object;
+ zend_class_entry *called_scope;
+
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
+ (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
+ object = NULL;
+ } else {
+ object = &EX(This);
+ }
+ } else {
+ called_scope = Z_CE(EX(This));
+ object = NULL;
+ }
+ zend_create_closure(EX_VAR(opline->result.var), func,
+ EX(func)->op_array.scope, called_scope, object);
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -5251,10 +5300,12 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST
value = RT_CONSTANT(opline, opline->op1);
if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) {
ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value));
-
+ if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(value);
+ }
ZEND_VM_NEXT_OPCODE();
} else {
- zend_bool strict;
+ bool strict;
if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) {
value = Z_REFVAL_P(value);
@@ -5275,8 +5326,18 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST
zend_string *str;
zval tmp;
+ if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) {
+ zend_error(E_DEPRECATED,
+ "strlen(): Passing null to parameter #1 ($string) of type string is deprecated");
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ ZVAL_LONG(EX_VAR(opline->result.var), 0);
+ break;
+ }
+
ZVAL_COPY(&tmp, value);
- if (zend_parse_arg_str_weak(&tmp, &str)) {
+ if (zend_parse_arg_str_weak(&tmp, &str, 1)) {
ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str));
zval_ptr_dtor(&tmp);
break;
@@ -5624,7 +5685,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = RT_CONSTANT(opline, opline->op1);
@@ -5639,7 +5700,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = RT_CONSTANT(opline, opline->op1);
@@ -6838,7 +6899,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
}
}
- zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
+ zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@@ -7013,7 +7074,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -7121,7 +7182,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_CO
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -7165,7 +7226,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_DELAYED_SPEC_CON
if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
} else {
- if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) {
+ ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname));
+ if (!ce) {
/* Reload bucket pointer, the hash table may have been reallocated */
zv = zend_hash_find(EG(class_table), Z_STR_P(lcname));
zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1));
@@ -7441,33 +7503,69 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_CON
HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
zval *result;
- SAVE_OPLINE();
op1 = RT_CONSTANT(opline, opline->op1);
if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
result = zend_hash_find_ex(ht, Z_STR_P(op1), IS_CONST == IS_CONST);
- } else if (opline->extended_value) {
+ if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(op1);
+ }
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+
+ if (opline->extended_value) {
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
result = zend_hash_index_find(ht, Z_LVAL_P(op1));
- } else {
- result = NULL;
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ SAVE_OPLINE();
+ if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ result = zend_hash_index_find(ht, Z_LVAL_P(op1));
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ } else if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ ZVAL_UNDEFINED_OP1();
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ ZVAL_UNDEFINED_OP1();
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
result = zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
+ ZEND_VM_SMART_BRANCH(result, 0);
} else {
zend_string *key;
- zval key_tmp, *val;
+ zval key_tmp;
- result = NULL;
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ }
+
+ SAVE_OPLINE();
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- result = val;
- break;
+
+ ZEND_VM_SMART_BRANCH(1, 1);
}
} ZEND_HASH_FOREACH_END();
}
- ZEND_VM_SMART_BRANCH(result, 1);
+ ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -9169,7 +9267,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
+ value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -9277,7 +9375,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_TM
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -9473,8 +9571,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
retval = &EG(uninitialized_zval);
@@ -9492,8 +9591,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
retval = &EG(uninitialized_zval);
@@ -9763,7 +9863,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -10143,41 +10243,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zend_function *func;
- zval *zfunc;
- zval *object;
- zend_class_entry *called_scope;
-
- func = CACHED_PTR(opline->extended_value);
- if (UNEXPECTED(func == NULL)) {
- zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
- ZEND_ASSERT(zfunc != NULL);
- func = Z_FUNC_P(zfunc);
- ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
- CACHE_PTR(opline->extended_value, func);
- }
-
- if (Z_TYPE(EX(This)) == IS_OBJECT) {
- called_scope = Z_OBJCE(EX(This));
- if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
- (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
- object = NULL;
- } else {
- object = &EX(This);
- }
- } else {
- called_scope = Z_CE(EX(This));
- object = NULL;
- }
- zend_create_closure(EX_VAR(opline->result.var), func,
- EX(func)->op_array.scope, called_scope, object);
-
- ZEND_VM_NEXT_OPCODE();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -10323,7 +10388,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_
while (1) {
if (Z_TYPE_P(op1) == IS_ARRAY) {
- count = zend_array_count(Z_ARRVAL_P(op1));
+ count = zend_hash_num_elements(Z_ARRVAL_P(op1));
break;
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
zend_object *zobj = Z_OBJ_P(op1);
@@ -11551,7 +11616,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -11659,7 +11724,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_CV
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -14292,6 +14357,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
zend_vm_stack_free_call_frame(call);
}
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -14421,10 +14487,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN
value = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) {
ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value));
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ if ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(value);
+ }
ZEND_VM_NEXT_OPCODE();
} else {
- zend_bool strict;
+ bool strict;
if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) {
value = Z_REFVAL_P(value);
@@ -14445,8 +14513,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN
zend_string *str;
zval tmp;
+ if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) {
+ zend_error(E_DEPRECATED,
+ "strlen(): Passing null to parameter #1 ($string) of type string is deprecated");
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ ZVAL_LONG(EX_VAR(opline->result.var), 0);
+ break;
+ }
+
ZVAL_COPY(&tmp, value);
- if (zend_parse_arg_str_weak(&tmp, &str)) {
+ if (zend_parse_arg_str_weak(&tmp, &str, 1)) {
ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str));
zval_ptr_dtor(&tmp);
break;
@@ -15697,7 +15775,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -15805,7 +15883,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_C
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -15836,7 +15914,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_H
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
@@ -17089,7 +17167,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
+ value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -17197,7 +17275,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_T
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -17229,7 +17307,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HAN
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
@@ -17314,8 +17392,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
retval = &EG(uninitialized_zval);
@@ -17333,8 +17412,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
retval = &EG(uninitialized_zval);
@@ -17510,7 +17590,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
@@ -17562,7 +17642,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL
while (1) {
if (Z_TYPE_P(op1) == IS_ARRAY) {
- count = zend_array_count(Z_ARRVAL_P(op1));
+ count = zend_hash_num_elements(Z_ARRVAL_P(op1));
break;
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
zend_object *zobj = Z_OBJ_P(op1);
@@ -18399,7 +18479,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -18507,7 +18587,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_C
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -18784,6 +18864,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
} else {
ZVAL_EMPTY_ARRAY(result);
}
+ } else if (Z_OBJ_P(expr)->properties == NULL
+ && Z_OBJ_HT_P(expr)->get_properties_for == NULL
+ && Z_OBJ_HT_P(expr)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -18871,7 +18956,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
if (UNEXPECTED(EG(exception))) {
@@ -18964,7 +19049,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
if (IS_TMP_VAR == IS_VAR) {
@@ -19133,7 +19218,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HA
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -19148,7 +19233,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CONST_HAN
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -19162,7 +19247,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONS
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -19584,33 +19669,69 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_TMP_CONST_HANDLE
HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
zval *result;
- SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
result = zend_hash_find_ex(ht, Z_STR_P(op1), IS_TMP_VAR == IS_CONST);
- } else if (opline->extended_value) {
+ if (IS_TMP_VAR & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(op1);
+ }
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+
+ if (opline->extended_value) {
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
result = zend_hash_index_find(ht, Z_LVAL_P(op1));
- } else {
- result = NULL;
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ SAVE_OPLINE();
+ if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ result = zend_hash_index_find(ht, Z_LVAL_P(op1));
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ } else if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ ZVAL_UNDEFINED_OP1();
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ ZVAL_UNDEFINED_OP1();
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
result = zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
+ ZEND_VM_SMART_BRANCH(result, 0);
} else {
zend_string *key;
- zval key_tmp, *val;
+ zval key_tmp;
- result = NULL;
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ }
+
+ SAVE_OPLINE();
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- result = val;
- break;
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(1, 1);
}
} ZEND_HASH_FOREACH_END();
}
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
- ZEND_VM_SMART_BRANCH(result, 1);
+ ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -19985,7 +20106,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HAND
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -20000,7 +20121,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_TMP_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -20014,7 +20135,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -20029,7 +20150,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_VAR_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -20117,7 +20238,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -20462,7 +20583,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CV_HANDLE
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -21358,6 +21479,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
} else {
ZVAL_EMPTY_ARRAY(result);
}
+ } else if (Z_OBJ_P(expr)->properties == NULL
+ && Z_OBJ_HT_P(expr)->get_properties_for == NULL
+ && Z_OBJ_HT_P(expr)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -21446,7 +21572,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
if (UNEXPECTED(EG(exception))) {
@@ -21539,7 +21665,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
if (IS_VAR == IS_VAR) {
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
@@ -21593,16 +21719,9 @@ fe_fetch_r_exit:
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -21663,15 +21782,16 @@ fe_fetch_r_exit:
}
}
} else {
+ const zend_object_iterator_funcs *funcs = iter->funcs;
if (EXPECTED(++iter->index > 0)) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
- if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
+ if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@@ -21680,7 +21800,7 @@ fe_fetch_r_exit:
goto fe_fetch_r_exit;
}
}
- value = iter->funcs->get_current_data(iter);
+ value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -21690,8 +21810,8 @@ fe_fetch_r_exit:
goto fe_fetch_r_exit;
}
if (RETURN_VALUE_USED(opline)) {
- if (iter->funcs->get_current_key) {
- iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -21745,16 +21865,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -21823,15 +21936,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
}
}
} else {
+ const zend_object_iterator_funcs *funcs = iter->funcs;
if (++iter->index > 0) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
- iter->funcs->move_forward(iter);
+ funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
- if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
+ if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@@ -21840,7 +21954,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
goto fe_fetch_w_exit;
}
}
- value = iter->funcs->get_current_data(iter);
+ value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -21850,8 +21964,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
goto fe_fetch_w_exit;
}
if (RETURN_VALUE_USED(opline)) {
- if (iter->funcs->get_current_key) {
- iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
+ if (funcs->get_current_key) {
+ funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -22044,7 +22158,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HA
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -22059,7 +22173,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CONST_HAN
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -22073,7 +22187,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONS
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -24194,7 +24308,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
}
}
- zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
+ zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@@ -24370,11 +24484,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -24395,6 +24506,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -24606,33 +24718,69 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_VAR_CONST_HANDLE
HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
zval *result;
- SAVE_OPLINE();
- op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
+ op1 = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
result = zend_hash_find_ex(ht, Z_STR_P(op1), IS_VAR == IS_CONST);
- } else if (opline->extended_value) {
+ if (IS_VAR & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(op1);
+ }
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+
+ if (opline->extended_value) {
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
result = zend_hash_index_find(ht, Z_LVAL_P(op1));
- } else {
- result = NULL;
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ SAVE_OPLINE();
+ if ((IS_VAR & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ result = zend_hash_index_find(ht, Z_LVAL_P(op1));
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ } else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ ZVAL_UNDEFINED_OP1();
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ ZVAL_UNDEFINED_OP1();
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
result = zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
+ ZEND_VM_SMART_BRANCH(result, 0);
} else {
zend_string *key;
- zval key_tmp, *val;
+ zval key_tmp;
- result = NULL;
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if ((IS_VAR & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ }
+
+ SAVE_OPLINE();
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- result = val;
- break;
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ ZEND_VM_SMART_BRANCH(1, 1);
}
} ZEND_HASH_FOREACH_END();
}
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
- ZEND_VM_SMART_BRANCH(result, 1);
+ ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -26523,11 +26671,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -26548,6 +26693,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -26759,7 +26905,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HAND
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -26774,7 +26920,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_TMP_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -26788,7 +26934,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -26843,7 +26989,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HAND
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -26858,7 +27004,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_VAR_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -26872,7 +27018,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -27732,7 +27878,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -28570,7 +28716,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CV_HANDLE
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
@@ -30537,11 +30683,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -30562,6 +30705,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -30790,16 +30934,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SIMPLE_
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -30842,16 +30979,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SIMPLE_
pos++;
value = &p->val;
value_type = Z_TYPE_INFO_P(value);
+ ZEND_ASSERT(value_type != IS_INDIRECT);
if (EXPECTED(value_type != IS_UNDEF)) {
- if (UNEXPECTED(value_type == IS_INDIRECT)) {
- value = Z_INDIRECT_P(value);
- value_type = Z_TYPE_INFO_P(value);
- if (EXPECTED(value_type != IS_UNDEF)) {
- break;
- }
- } else {
- break;
- }
+ break;
}
p++;
}
@@ -32637,7 +32767,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
}
}
- zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
+ zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1);
if (EXPECTED(zv != NULL)) {
c = Z_PTR_P(zv);
scope = EX(func)->op_array.scope;
@@ -34932,7 +35062,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -35247,6 +35377,16 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_THIS_SPEC_UN
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ /* For symbol tables we need to deal with exactly the same problems as for property tables. */
+ ZVAL_ARR(EX_VAR(opline->result.var),
+ zend_proptable_to_symtable(&EG(symbol_table), /* always_duplicate */ 1));
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -38019,6 +38159,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
} else {
ZVAL_EMPTY_ARRAY(result);
}
+ } else if (Z_OBJ_P(expr)->properties == NULL
+ && Z_OBJ_HT_P(expr)->get_properties_for == NULL
+ && Z_OBJ_HT_P(expr)->get_properties == zend_std_get_properties) {
+ /* Optimized version without rebulding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
@@ -38111,6 +38256,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE
zend_vm_stack_free_call_frame(call);
}
+ zend_destroy_static_vars(new_op_array);
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -38174,7 +38320,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
@@ -38266,7 +38412,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
+ bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);
if (IS_CV == IS_VAR) {
@@ -38530,10 +38676,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP
value = EX_VAR(opline->op1.var);
if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) {
ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value));
-
+ if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(value);
+ }
ZEND_VM_NEXT_OPCODE();
} else {
- zend_bool strict;
+ bool strict;
if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) {
value = Z_REFVAL_P(value);
@@ -38554,8 +38702,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP
zend_string *str;
zval tmp;
+ if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) {
+ zend_error(E_DEPRECATED,
+ "strlen(): Passing null to parameter #1 ($string) of type string is deprecated");
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ ZVAL_LONG(EX_VAR(opline->result.var), 0);
+ break;
+ }
+
ZVAL_COPY(&tmp, value);
- if (zend_parse_arg_str_weak(&tmp, &str)) {
+ if (zend_parse_arg_str_weak(&tmp, &str, 1)) {
ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str));
zval_ptr_dtor(&tmp);
break;
@@ -38938,7 +39096,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CONST_HAN
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -38953,7 +39111,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CONST
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -41808,11 +41966,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -41833,6 +41988,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -41940,7 +42096,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -42048,7 +42204,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CONST
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -42079,7 +42235,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_CONST_HANDL
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = EX_VAR(opline->op1.var);
@@ -42331,33 +42487,69 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_CV_CONST_HANDLER
HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
zval *result;
- SAVE_OPLINE();
- op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
+ op1 = EX_VAR(opline->op1.var);
if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
result = zend_hash_find_ex(ht, Z_STR_P(op1), IS_CV == IS_CONST);
- } else if (opline->extended_value) {
+ if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
+ zval_ptr_dtor_str(op1);
+ }
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+
+ if (opline->extended_value) {
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
result = zend_hash_index_find(ht, Z_LVAL_P(op1));
- } else {
- result = NULL;
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ SAVE_OPLINE();
+ if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
+ result = zend_hash_index_find(ht, Z_LVAL_P(op1));
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ ZVAL_UNDEFINED_OP1();
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ ZVAL_UNDEFINED_OP1();
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
result = zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1);
+ ZEND_VM_SMART_BRANCH(result, 0);
} else {
zend_string *key;
- zval key_tmp, *val;
+ zval key_tmp;
- result = NULL;
- ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) {
+ if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(op1) == IS_REFERENCE) {
+ op1 = Z_REFVAL_P(op1);
+ if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
+ result = zend_hash_find_ex(ht, Z_STR_P(op1), 0);
+
+ ZEND_VM_SMART_BRANCH(result, 0);
+ }
+ }
+
+ SAVE_OPLINE();
+ ZEND_HASH_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- result = val;
- break;
+
+ ZEND_VM_SMART_BRANCH(1, 1);
}
} ZEND_HASH_FOREACH_END();
}
- ZEND_VM_SMART_BRANCH(result, 1);
+ ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42367,7 +42559,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_C
/* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = EX_VAR(opline->op1.var);
op2 = RT_CONSTANT(opline, opline->op2);
@@ -42380,7 +42572,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = EX_VAR(opline->op1.var);
op2 = RT_CONSTANT(opline, opline->op2);
@@ -45254,11 +45446,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -45279,6 +45468,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -45388,7 +45578,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
+ value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -45496,7 +45686,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVA
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -45652,7 +45842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -45667,7 +45857,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_H
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -45722,7 +45912,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_VAR_HANDL
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -45737,7 +45927,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_H
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -45827,7 +46017,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = EX_VAR(opline->op1.var);
@@ -45996,8 +46186,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
} else {
retval = &EG(uninitialized_zval);
@@ -46015,8 +46206,9 @@ fetch_this:
} else if (type == BP_VAR_IS) {
retval = &EG(uninitialized_zval);
} else {
- zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name));
- if (type == BP_VAR_RW) {
+ zend_error(E_WARNING, "Undefined %svariable $%s",
+ (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name));
+ if (type == BP_VAR_RW && !EG(exception)) {
ZVAL_NULL(retval);
} else {
retval = &EG(uninitialized_zval);
@@ -46634,7 +46826,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -47119,7 +47311,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_UNUSED_HAND
{
USE_OPLINE
zval *expr;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
expr = EX_VAR(opline->op1.var);
@@ -47294,16 +47486,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN
ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
if (!ht) {
- ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
ht = zend_array_dup(EX(func)->op_array.static_variables);
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
- } else if (GC_REFCOUNT(ht) > 1) {
- if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
- GC_DELREF(ht);
- }
- ht = zend_array_dup(ht);
- ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
}
+ ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));
@@ -47394,7 +47580,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z
while (1) {
if (Z_TYPE_P(op1) == IS_ARRAY) {
- count = zend_array_count(Z_ARRVAL_P(op1));
+ count = zend_hash_num_elements(Z_ARRVAL_P(op1));
break;
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
zend_object *zobj = Z_OBJ_P(op1);
@@ -47619,7 +47805,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CV_HANDLE
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -47634,7 +47820,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CV_HA
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -50377,11 +50563,8 @@ offset_again:
}
}
str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
+ ZEND_ASSERT(ht != &EG(symbol_table));
+ zend_hash_del(ht, key);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_dim:
@@ -50402,6 +50585,7 @@ num_index_dim:
hval = 1;
goto num_index_dim;
} else if (Z_TYPE_P(offset) == IS_RESOURCE) {
+ zend_use_resource_as_offset(offset);
hval = Z_RES_HANDLE_P(offset);
goto num_index_dim;
} else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
@@ -50509,7 +50693,7 @@ isset_again:
goto num_index_prop;
}
}
- value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST);
+ value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST);
} else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
hval = Z_LVAL_P(offset);
num_index_prop:
@@ -50617,7 +50801,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_HA
zval *key, *subject;
HashTable *ht;
- zend_bool result;
+ bool result;
SAVE_OPLINE();
@@ -50774,7 +50958,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_C
/* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = EX_VAR(opline->op1.var);
op2 = EX_VAR(opline->op2.var);
@@ -50787,7 +50971,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_
{
USE_OPLINE
zval *op1, *op2;
- zend_bool result;
+ bool result;
op1 = EX_VAR(opline->op1.var);
op2 = EX_VAR(opline->op2.var);
@@ -53148,7 +53332,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL,
(void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL,
- (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_LABEL,
+ (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_LABEL,
(void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL,
(void*)&&ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_LABEL,
@@ -53405,6 +53589,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_TMPVARCV_LABEL,
(void*)&&ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_LABEL,
+ (void*)&&ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
(void*)&&ZEND_JMP_FORWARD_SPEC_LABEL,
(void*)&&ZEND_NULL_LABEL,
@@ -54458,6 +54643,7 @@ zend_leave_helper_SPEC_LABEL:
ZEND_VM_LEAVE();
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
zend_detach_symbol_table(execute_data);
+ zend_destroy_static_vars(&EX(func)->op_array);
destroy_op_array(&EX(func)->op_array);
efree_size(EX(func), sizeof(zend_op_array));
#ifdef ZEND_PREFER_RELOAD
@@ -54966,6 +55152,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_DECLARE_CLASS_SPEC_CONST)
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
+ HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST):
+ VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST)
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+ HYBRID_BREAK();
HYBRID_CASE(ZEND_YIELD_FROM_SPEC_CONST):
VM_TRACE(ZEND_YIELD_FROM_SPEC_CONST)
ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -55442,10 +55632,6 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED)
ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
- HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED):
- VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED)
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
- HYBRID_BREAK();
HYBRID_CASE(ZEND_YIELD_SPEC_CONST_UNUSED):
VM_TRACE(ZEND_YIELD_SPEC_CONST_UNUSED)
ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -57604,6 +57790,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED)
ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
+ HYBRID_CASE(ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED):
+ VM_TRACE(ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED)
+ ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+ HYBRID_BREAK();
HYBRID_CASE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED):
VM_TRACE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED)
ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -61171,7 +61361,7 @@ void zend_vm_init(void)
ZEND_NULL_HANDLER,
ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER,
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER,
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER,
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER,
ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_HANDLER,
@@ -61428,6 +61618,7 @@ void zend_vm_init(void)
ZEND_NULL_HANDLER,
ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER,
ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
ZEND_JMP_FORWARD_SPEC_HANDLER,
ZEND_NULL_HANDLER,
@@ -62534,7 +62725,8 @@ void zend_vm_init(void)
2536 | SPEC_RULE_OP1,
2541 | SPEC_RULE_OP1,
2546,
- 3450
+ 2547,
+ 3451
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
@@ -62707,7 +62899,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2549 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2550 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -62715,7 +62907,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2574 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2575 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -62723,7 +62915,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2599 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2600 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -62734,17 +62926,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2624 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2625 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2649 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2650 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2674 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2675 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@@ -62755,17 +62947,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2699 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2700 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2724 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2725 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2749 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2750 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_IDENTICAL:
@@ -62776,14 +62968,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2774 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2849 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2850 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
- spec = 3074 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 3075 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_IDENTICAL:
@@ -62794,14 +62986,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2924 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2925 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2999 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 3000 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
- spec = 3079 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 3080 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
@@ -62812,12 +63004,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2774 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2849 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2850 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
@@ -62828,12 +63020,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2924 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2925 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2999 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 3000 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
@@ -62841,12 +63033,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3084 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3085 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3159 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3160 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@@ -62854,74 +63046,74 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3234 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3235 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3309 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3310 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_LONG) {
- spec = 3396 | SPEC_RULE_OP1;
+ spec = 3397 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_DOUBLE) {
- spec = 3401 | SPEC_RULE_OP1;
+ spec = 3402 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
- spec = 3406 | SPEC_RULE_OP1;
+ spec = 3407 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3384 | SPEC_RULE_RETVAL;
+ spec = 3385 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
- spec = 3386 | SPEC_RULE_RETVAL;
+ spec = 3387 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3388 | SPEC_RULE_RETVAL;
+ spec = 3389 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
- spec = 3390 | SPEC_RULE_RETVAL;
+ spec = 3391 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3392;
- } else if (op1_info == MAY_BE_LONG) {
spec = 3393;
+ } else if (op1_info == MAY_BE_LONG) {
+ spec = 3394;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3394;
- } else if (op1_info == MAY_BE_LONG) {
spec = 3395;
+ } else if (op1_info == MAY_BE_LONG) {
+ spec = 3396;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
- spec = 2548;
+ spec = 2549;
}
break;
case ZEND_RECV:
if (op->op2.num == MAY_BE_ANY) {
- spec = 2547;
+ spec = 2548;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
- spec = 3446;
+ spec = 3447;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
- spec = 3441 | SPEC_RULE_OP1;
+ spec = 3442 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
- spec = 3448 | SPEC_RULE_RETVAL;
+ spec = 3449 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
@@ -62929,17 +63121,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3411 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 3412 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
- spec = 3447;
+ spec = 3448;
}
break;
case ZEND_SEND_VAR:
if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
- spec = 3436 | SPEC_RULE_OP1;
+ spec = 3437 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR:
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index 0795822f96..c7a0df936d 100755
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -138,10 +138,10 @@ $vm_ext_decode = array(
);
$vm_kind_name = array(
- ZEND_VM_KIND_CALL => "ZEND_VM_KIND_CALL",
- ZEND_VM_KIND_SWITCH => "ZEND_VM_KIND_SWITCH",
- ZEND_VM_KIND_GOTO => "ZEND_VM_KIND_GOTO",
- ZEND_VM_KIND_HYBRID => "ZEND_VM_KIND_HYBRID",
+ ZEND_VM_KIND_CALL => "ZEND_VM_KIND_CALL",
+ ZEND_VM_KIND_SWITCH => "ZEND_VM_KIND_SWITCH",
+ ZEND_VM_KIND_GOTO => "ZEND_VM_KIND_GOTO",
+ ZEND_VM_KIND_HYBRID => "ZEND_VM_KIND_HYBRID",
);
$op_types = array(
@@ -150,7 +150,7 @@ $op_types = array(
"TMP",
"VAR",
"UNUSED",
- "CV"
+ "CV",
);
$op_types_ex = array(
@@ -590,9 +590,9 @@ function is_hot_helper($name) {
if (isset($helpers[$name]["hot"])) {
return $helpers[$name]["hot"];
- } else {
- return false;
}
+
+ return false;
}
// Returns name of specialized helper
@@ -637,7 +637,8 @@ function helper_name($name, $spec, $op1, $op2, $extra_spec) {
$extra = extra_spec_name(array_intersect_key($extra_spec, $helpers[$name]["spec"]));
}
}
- return $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].$extra;
+
+ return $name . ($spec ? "_SPEC" : "") . $prefix[$op1] . $prefix[$op2] . $extra;
}
function opcode_name($name, $spec, $op1, $op2, $extra_spec) {
@@ -688,7 +689,8 @@ function opcode_name($name, $spec, $op1, $op2, $extra_spec) {
$extra = extra_spec_name(array_intersect_key($extra_spec, $opcode["spec"]));
}
}
- return $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].$extra;
+
+ return $name . ($spec ? "_SPEC" : "") . $prefix[$op1] . $prefix[$op2] . $extra;
}
// Formats condition, protecting it by parentheses when needed.
@@ -701,7 +703,7 @@ function format_condition($condition) {
return $condition;
}
- return "(".$condition.")";
+ return "(" . $condition . ")";
}
// Generates code for opcode handler or helper
@@ -811,7 +813,7 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null)
$code = "{\n\tfprintf(stderr, \"$name\\n\");\n" . substr($code, 1);
}
// Updating code according to selected threading model
- switch($kind) {
+ switch ($kind) {
case ZEND_VM_KIND_HYBRID:
$code = preg_replace_callback(
array(
@@ -1071,7 +1073,7 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno,
// Generate opcode handler's entry point according to selected threading model
$additional_func = false;
$spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
- switch($kind) {
+ switch ($kind) {
case ZEND_VM_KIND_HYBRID:
if (is_inline_hybrid_handler($name, $opcode["hot"], $op1, $op2, $extra_spec)) {
$out = fopen('php://memory', 'w+');
@@ -1164,7 +1166,7 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno,
$spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
// Generate helper's entry point according to selected threading model
- switch($kind) {
+ switch ($kind) {
case ZEND_VM_KIND_HYBRID:
out($f, $spec_name . "_LABEL:\n");
break;
@@ -1226,7 +1228,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
// Emit labels for specialized executor
// For each opcode in opcode number order
- foreach($opcodes as $num => $dsc) {
+ foreach ($opcodes as $num => $dsc) {
if (isset($dsc['alias'])) {
$specs[$num] = $specs[$opnames[$dsc['alias']]];
continue;
@@ -1276,7 +1278,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
$foreach_op1 = function($do) use ($dsc, $op_types) {
return function($_, $op2) use ($do, $dsc, $op_types) {
// For each op1.op_type except ANY
- foreach($op_types as $op1) {
+ foreach ($op_types as $op1) {
if ($op1 != "ANY") {
if (!isset($dsc["op1"][$op1])) {
if ($op1 == "TMP" || $op1 == "VAR") {
@@ -1302,7 +1304,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
$foreach_op2 = function($do) use ($dsc, $op_types) {
return function($op1, $_) use ($do, $dsc, $op_types) {
// For each op2.op_type except ANY
- foreach($op_types as $op2) {
+ foreach ($op_types as $op2) {
if ($op2 != "ANY") {
if (!isset($dsc["op2"][$op2])) {
if ($op2 == "TMP" || $op2 == "VAR") {
@@ -1328,7 +1330,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
$foreach_op_data = function($do) use ($dsc, $op_types) {
return function($op1, $op2, $extra_spec = array()) use ($do, $dsc, $op_types) {
// For each op_data.op_type except ANY
- foreach($op_types as $op_data) {
+ foreach ($op_types as $op_data) {
if ($op_data != "ANY") {
if (!isset($dsc["spec"]["OP_DATA"][$op_data])) {
if ($op_data == "TMP" || $op_data == "VAR") {
@@ -1448,7 +1450,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
// Emit labels for unspecialized executor
// For each opcode in opcode number order
- foreach($opcodes as $num => $dsc) {
+ foreach ($opcodes as $num => $dsc) {
while ($next != $num) {
// If some opcode numbers are not used then fill hole with pointers
// to handler of undefined opcode
@@ -1529,7 +1531,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
$l = fopen(__DIR__ . "/zend_vm_handlers.h", "w+") or die("ERROR: Cannot create zend_vm_handlers.h\n");
out($l, "#define VM_HANDLERS(_) \\\n");
foreach ($list as $n => $name) {
- if (!is_null($name)) {
+ if (null !== $name) {
out($l, "\t_($n, $name) \\\n");
}
}
@@ -1644,7 +1646,7 @@ function extra_spec_handler($dsc) {
if (isset($specs["OP_DATA"])) {
$op_data_specs = $specs["OP_DATA"];
$specs["OP_DATA"] = array();
- foreach($op_types_ex as $op_data) {
+ foreach ($op_types_ex as $op_data) {
if (isset($dsc["spec"]["OP_DATA"][$op_data])) {
$specs["OP_DATA"][] = $op_data;
}
@@ -1694,10 +1696,10 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array())
// Produce specialized executor
$op1t = $op_types_ex;
// for each op1.op_type
- foreach($op1t as $op1) {
+ foreach ($op1t as $op1) {
$op2t = $op_types_ex;
// for each op2.op_type
- foreach($op2t as $op2) {
+ foreach ($op2t as $op2) {
// for each handlers in helpers in original order
foreach ($list as $lineno => $dsc) {
if (isset($dsc["handler"])) {
@@ -2255,7 +2257,7 @@ function parse_operand_spec($def, $lineno, $str, &$flags) {
$flags = 0;
$a = explode("|",$str);
- foreach($a as $val) {
+ foreach ($a as $val) {
if (isset($vm_op_decode[$val])) {
$flags |= $vm_op_decode[$val];
} else {
@@ -2276,7 +2278,7 @@ function parse_ext_spec($def, $lineno, $str) {
$flags = 0;
$a = explode("|",$str);
- foreach($a as $val) {
+ foreach ($a as $val) {
if (isset($vm_ext_decode[$val])) {
$flags |= $vm_ext_decode[$val];
} else {
@@ -2291,7 +2293,7 @@ function parse_spec_rules($def, $lineno, $str) {
$ret = array();
$a = explode(",", $str);
- foreach($a as $rule) {
+ foreach ($a as $rule) {
$n = strpos($rule, "=");
if ($n !== false) {
$id = trim(substr($rule, 0, $n));
@@ -2336,6 +2338,62 @@ function parse_spec_rules($def, $lineno, $str) {
return $ret;
}
+function gen_vm_opcodes_header(
+ array $opcodes, int $max_opcode, int $max_opcode_len, array $vm_op_flags
+): string {
+ $str = HEADER_TEXT;
+ $str .= "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n";
+ $str .= "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n";
+ $str .= "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n";
+ $str .= "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n";
+ $str .= "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n";
+ $str .= "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n";
+ $str .= "#define ZEND_VM_KIND_HYBRID\t" . ZEND_VM_KIND_HYBRID . "\n";
+ if ($GLOBALS["vm_kind_name"][ZEND_VM_KIND] === "ZEND_VM_KIND_HYBRID") {
+ $str .= "/* HYBRID requires support for computed GOTO and global register variables*/\n";
+ $str .= "#if (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS))\n";
+ $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n";
+ $str .= "#else\n";
+ $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n";
+ $str .= "#endif\n";
+ } else {
+ $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n";
+ }
+ $str .= "\n";
+ $str .= "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) && !defined(__SANITIZE_ADDRESS__)\n";
+ $str .= "# if ((defined(i386) && !defined(__PIC__)) || defined(__x86_64__) || defined(_M_X64))\n";
+ $str .= "# define ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 16\n";
+ $str .= "# endif\n";
+ $str .= "#endif\n";
+ $str .= "\n";
+ foreach ($vm_op_flags as $name => $val) {
+ $str .= sprintf("#define %-24s 0x%08x\n", $name, $val);
+ }
+ $str .= "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n";
+ $str .= "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n";
+ $str .= "\n";
+ $str .= "BEGIN_EXTERN_C()\n\n";
+ $str .= "ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode);\n";
+ $str .= "ZEND_API uint32_t ZEND_FASTCALL zend_get_opcode_flags(zend_uchar opcode);\n\n";
+ $str .= "END_EXTERN_C()\n\n";
+
+ $code_len = strlen((string) $max_opcode);
+ foreach ($opcodes as $code => $dsc) {
+ $code = str_pad((string)$code, $code_len, " ", STR_PAD_LEFT);
+ $op = str_pad($dsc["op"], $max_opcode_len);
+ if ($code <= $max_opcode) {
+ $str .= "#define $op $code\n";
+ }
+ }
+
+ $code = str_pad((string)$max_opcode, $code_len, " ", STR_PAD_LEFT);
+ $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len);
+ $str .= "\n#define $op $code\n";
+
+ $str .= "\n#endif\n";
+ return $str;
+}
+
function gen_vm($def, $skel) {
global $definition_file, $skeleton_file, $executor_file,
$op_types, $list, $opcodes, $helpers, $params, $opnames,
@@ -2446,8 +2504,8 @@ function gen_vm($def, $skel) {
$opcodes[$orig_code]['type_spec'][$code] = $condition;
}
$op = $m[4];
- $op1 = parse_operand_spec($def, $lineno, $m[5], $flags1);
- $op2 = parse_operand_spec($def, $lineno, $m[6], $flags2);
+ $op1 = parse_operand_spec($def, $lineno, $m[5], $flags1);
+ $op2 = parse_operand_spec($def, $lineno, $m[6], $flags2);
$flags = $flags1 | ($flags2 << 8);
if (!empty($m[8])) {
$flags |= parse_ext_spec($def, $lineno, $m[8]);
@@ -2494,10 +2552,11 @@ function gen_vm($def, $skel) {
}
// Store parameters
- if (ZEND_VM_KIND == ZEND_VM_KIND_GOTO
+ if ((ZEND_VM_KIND == ZEND_VM_KIND_GOTO
|| ZEND_VM_KIND == ZEND_VM_KIND_SWITCH
- || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && $hot)) {
- foreach (explode(",", $param) as $p) {
+ || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && $hot))
+ && $param) {
+ foreach (explode(",", $param ) as $p) {
$p = trim($p);
if ($p !== "") {
$params[$p] = 1;
@@ -2574,60 +2633,8 @@ function gen_vm($def, $skel) {
}
// Generate opcode #defines (zend_vm_opcodes.h)
- $code_len = strlen((string)$max_opcode);
- $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n");
-
- // Insert header
- out($f, HEADER_TEXT);
- fputs($f, "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n");
- fputs($f, "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n");
- fputs($f, "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n");
- fputs($f, "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n");
- fputs($f, "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n");
- fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n");
- fputs($f, "#define ZEND_VM_KIND_HYBRID\t" . ZEND_VM_KIND_HYBRID . "\n");
- if ($GLOBALS["vm_kind_name"][ZEND_VM_KIND] === "ZEND_VM_KIND_HYBRID") {
- fputs($f, "/* HYBRID requires support for computed GOTO and global register variables*/\n");
- fputs($f, "#if (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS))\n");
- fputs($f, "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n");
- fputs($f, "#else\n");
- fputs($f, "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n");
- fputs($f, "#endif\n");
- } else {
- fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n");
- }
- fputs($f, "\n");
- fputs($f, "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) && !defined(__SANITIZE_ADDRESS__)\n");
- fputs($f, "# if ((defined(i386) && !defined(__PIC__)) || defined(__x86_64__) || defined(_M_X64))\n");
- fputs($f, "# define ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 16\n");
- fputs($f, "# endif\n");
- fputs($f, "#endif\n");
- fputs($f, "\n");
- foreach($vm_op_flags as $name => $val) {
- fprintf($f, "#define %-24s 0x%08x\n", $name, $val);
- }
- fputs($f, "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n");
- fputs($f, "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n");
- fputs($f, "\n");
- fputs($f, "BEGIN_EXTERN_C()\n\n");
- fputs($f, "ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode);\n");
- fputs($f, "ZEND_API uint32_t ZEND_FASTCALL zend_get_opcode_flags(zend_uchar opcode);\n\n");
- fputs($f, "END_EXTERN_C()\n\n");
-
- foreach ($opcodes as $code => $dsc) {
- $code = str_pad((string)$code,$code_len," ",STR_PAD_LEFT);
- $op = str_pad($dsc["op"],$max_opcode_len);
- if ($code <= $max_opcode) {
- fputs($f,"#define $op $code\n");
- }
- }
-
- $code = str_pad((string)$max_opcode,$code_len," ",STR_PAD_LEFT);
- $op = str_pad("ZEND_VM_LAST_OPCODE",$max_opcode_len);
- fputs($f,"\n#define $op $code\n");
-
- fputs($f, "\n#endif\n");
- fclose($f);
+ $str = gen_vm_opcodes_header($opcodes, $max_opcode, $max_opcode_len, $vm_op_flags);
+ write_file_if_changed(__DIR__ . "/zend_vm_opcodes.h", $str);
echo "zend_vm_opcodes.h generated successfully.\n";
// zend_vm_opcodes.c
@@ -2847,7 +2854,7 @@ function gen_vm($def, $skel) {
out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n");
if (isset($used_extra_spec["TYPE"])) {
out($f, "\tswitch (opcode) {\n");
- foreach($opcodes as $code => $dsc) {
+ foreach ($opcodes as $code => $dsc) {
if (isset($dsc['type_spec'])) {
$orig_op = $dsc['op'];
out($f, "\t\tcase $orig_op:\n");
@@ -2857,7 +2864,7 @@ function gen_vm($def, $skel) {
out($f, "\t\t\t}\n");
}
$first = true;
- foreach($dsc['type_spec'] as $code => $condition) {
+ foreach ($dsc['type_spec'] as $code => $condition) {
$condition = format_condition($condition);
if ($first) {
out($f, "\t\t\tif $condition {\n");
@@ -2885,7 +2892,7 @@ function gen_vm($def, $skel) {
}
}
$has_commutative = false;
- foreach($opcodes as $code => $dsc) {
+ foreach ($opcodes as $code => $dsc) {
if (!isset($dsc['is_type_spec']) &&
!isset($dsc['type_spec']) &&
isset($dsc["spec"]["COMMUTATIVE"])) {
@@ -2980,6 +2987,18 @@ function gen_vm($def, $skel) {
echo "zend_vm_execute.h generated successfully.\n";
}
+function write_file_if_changed(string $filename, string $contents) {
+ if (file_exists($filename)) {
+ $orig_contents = file_get_contents($filename);
+ if ($orig_contents === $contents) {
+ // Unchanged, no need to write.
+ return;
+ }
+ }
+
+ file_put_contents($filename, $contents);
+}
+
function usage() {
echo("\nUsage: php zend_vm_gen.php [options]\n".
"\nOptions:".
diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h
index a2e85dde9b..2fbc04273d 100644
--- a/Zend/zend_vm_handlers.h
+++ b/Zend/zend_vm_handlers.h
@@ -1158,7 +1158,7 @@
_(2286, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \
_(2288, ZEND_MAKE_REF_SPEC_CV_UNUSED) \
_(2289, ZEND_DECLARE_FUNCTION_SPEC) \
- _(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED) \
+ _(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) \
_(2291, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \
_(2292, ZEND_DECLARE_CLASS_SPEC_CONST) \
_(2293, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST) \
@@ -1352,498 +1352,499 @@
_(2543, ZEND_JMP_NULL_SPEC_TMPVARCV) \
_(2545, ZEND_JMP_NULL_SPEC_TMPVARCV) \
_(2546, ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED) \
- _(2547, ZEND_RECV_NOTYPE_SPEC) \
- _(2548, ZEND_JMP_FORWARD_SPEC) \
- _(2554, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2555, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2547, ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED) \
+ _(2548, ZEND_RECV_NOTYPE_SPEC) \
+ _(2549, ZEND_JMP_FORWARD_SPEC) \
+ _(2555, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2556, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2558, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2559, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2560, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2557, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2559, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2560, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2561, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2563, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2569, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2570, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2562, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2564, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2570, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2571, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2579, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2580, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2580, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2581, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2583, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2584, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2585, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2582, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2584, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2585, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2586, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2588, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2594, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2595, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2587, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2589, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2595, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2596, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2604, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2605, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2605, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2606, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2608, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2609, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2610, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2607, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2609, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2610, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2611, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2613, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2619, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2620, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2612, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2614, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2620, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2621, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2625, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
_(2626, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
- _(2628, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
- _(2629, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2630, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2627, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2629, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2630, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2631, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2633, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2634, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2635, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2632, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2634, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2635, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2636, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2638, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2645, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2637, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2639, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2645, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2650, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
_(2651, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
- _(2653, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
- _(2654, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2655, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2652, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2654, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2655, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2656, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2658, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2659, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2660, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2657, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2659, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2660, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2661, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2663, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2669, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2670, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2662, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2664, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2670, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2671, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2675, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
_(2676, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(2678, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(2679, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2680, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2677, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2679, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2680, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2681, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2683, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2684, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2685, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2682, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2684, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2685, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2686, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2688, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2694, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2695, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2687, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2689, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2695, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2696, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2704, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2705, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2705, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2706, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2708, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2709, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2710, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2707, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2709, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2710, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2711, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2713, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2719, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2720, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2712, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2714, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2720, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2721, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2729, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2730, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2730, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2731, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2733, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2734, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2735, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2732, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2734, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2735, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2736, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2738, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2744, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2745, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2737, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2739, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2745, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2746, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2754, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2755, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2755, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2756, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2758, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2759, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2760, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2757, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2759, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2760, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2761, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2763, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2769, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2770, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2762, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2764, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2770, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2771, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2789, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2790, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2791, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2792, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2793, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2794, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2795, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2796, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2797, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2801, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2802, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2803, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2804, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2805, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2806, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2816, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2864, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2865, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2866, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2867, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2868, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2869, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2870, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2871, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2872, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2876, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2877, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2878, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2879, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2880, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2881, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2891, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2939, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2940, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2941, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2942, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2943, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2944, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2945, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2946, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2947, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2951, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2952, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2953, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2954, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2955, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2956, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2966, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3014, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3015, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3016, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3017, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3018, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3019, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3020, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3021, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3022, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3026, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3027, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3028, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3029, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3030, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3031, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3041, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3074, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
- _(3078, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \
- _(3079, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
- _(3083, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \
- _(3087, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3088, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3089, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3090, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3091, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3092, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3096, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3097, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3098, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3099, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3100, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3101, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3102, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3103, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3104, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3105, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3106, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3107, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3111, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3112, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3113, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3114, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3115, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3116, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3117, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3118, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3119, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3120, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3121, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3122, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3162, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3163, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3164, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3165, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3166, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3167, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3171, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3172, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3173, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3174, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3175, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3176, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3177, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3178, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3179, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3180, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3181, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3182, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3193, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3194, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3237, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3238, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3239, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3240, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3241, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3242, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3246, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3247, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3248, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3249, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3250, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3251, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3252, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3253, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3254, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3255, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3256, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3257, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3268, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3269, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3312, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3313, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3314, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3315, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3316, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3317, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3321, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3322, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3323, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3324, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3325, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3326, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3327, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3328, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3329, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3330, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3331, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3332, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3343, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3344, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3384, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
- _(3385, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
- _(3386, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \
- _(3387, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \
- _(3388, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
- _(3389, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
- _(3390, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \
- _(3391, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \
- _(3392, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \
- _(3393, ZEND_POST_INC_LONG_SPEC_CV) \
- _(3394, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \
- _(3395, ZEND_POST_DEC_LONG_SPEC_CV) \
- _(3396, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \
- _(3397, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2790, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2791, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2792, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2793, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2794, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2795, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2796, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2797, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2798, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2802, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2803, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2804, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2805, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2806, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2849, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2865, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2866, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2867, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2868, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2869, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2870, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2871, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2872, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2873, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2877, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2878, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2879, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2880, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2881, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2924, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2940, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2941, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2942, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2943, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2944, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2945, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2946, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2947, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2948, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2952, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2953, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2954, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2955, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2956, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2999, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3015, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3016, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3017, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3018, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3019, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3020, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3021, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3022, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3023, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3027, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3028, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3029, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3030, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3031, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3074, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3075, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
+ _(3079, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \
+ _(3080, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
+ _(3084, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \
+ _(3088, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3089, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3090, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3091, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3092, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3093, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3097, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3098, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3099, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3100, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3101, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3102, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3103, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3104, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3105, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3106, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3107, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3108, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3112, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3113, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3114, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3115, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3116, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3117, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3118, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3119, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3120, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3121, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3122, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3163, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3164, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3165, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3166, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3167, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3168, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3172, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3173, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3174, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3175, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3176, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3177, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3178, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3179, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3180, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3181, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3182, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3183, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3193, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3194, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3238, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3239, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3240, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3241, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3242, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3243, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3247, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3248, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3249, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3250, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3251, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3252, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3253, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3254, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3255, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3256, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3257, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3258, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3268, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3269, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3313, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3314, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3315, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3316, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3317, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3318, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3322, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3323, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3324, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3325, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3326, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3327, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3328, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3329, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3330, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3331, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3332, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3333, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3343, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3344, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3385, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
+ _(3386, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
+ _(3387, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \
+ _(3388, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \
+ _(3389, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
+ _(3390, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
+ _(3391, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \
+ _(3392, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \
+ _(3393, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \
+ _(3394, ZEND_POST_INC_LONG_SPEC_CV) \
+ _(3395, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \
+ _(3396, ZEND_POST_DEC_LONG_SPEC_CV) \
+ _(3397, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \
_(3398, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
- _(3400, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
- _(3401, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \
- _(3402, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3399, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(3401, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(3402, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \
_(3403, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
- _(3405, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
- _(3406, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \
- _(3407, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
+ _(3404, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3406, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3407, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \
_(3408, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
- _(3410, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
- _(3412, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3409, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
+ _(3411, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
_(3413, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
- _(3415, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
- _(3416, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
- _(3417, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3414, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3416, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3417, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
_(3418, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3420, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3421, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
- _(3422, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3419, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3421, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3422, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
_(3423, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3425, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3431, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \
- _(3432, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3424, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3426, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3432, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \
_(3433, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
- _(3435, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
- _(3438, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
- _(3440, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
- _(3443, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
- _(3445, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
- _(3446, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
- _(3447, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
- _(3448, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
- _(3449, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
- _(3449+1, ZEND_NULL)
+ _(3434, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3439, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
+ _(3441, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
+ _(3444, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
+ _(3446, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
+ _(3447, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
+ _(3448, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
+ _(3449, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
+ _(3450, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
+ _(3450+1, ZEND_NULL)
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index 56a31aa19a..f25dc2e12d 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -22,7 +22,7 @@
#include <zend.h>
#include <zend_vm_opcodes.h>
-static const char *zend_vm_opcodes_names[200] = {
+static const char *zend_vm_opcodes_names[201] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@@ -223,9 +223,10 @@ static const char *zend_vm_opcodes_names[200] = {
"ZEND_MATCH_ERROR",
"ZEND_JMP_NULL",
"ZEND_CHECK_UNDEF_ARGS",
+ "ZEND_FETCH_GLOBALS",
};
-static uint32_t zend_vm_opcodes_flags[200] = {
+static uint32_t zend_vm_opcodes_flags[201] = {
0x00000000,
0x00000b0b,
0x00000b0b,
@@ -367,8 +368,8 @@ static uint32_t zend_vm_opcodes_flags[200] = {
0x00047305,
0x00000000,
0x00000101,
- 0x00000000,
- 0x00040103,
+ 0x00001000,
+ 0x00001003,
0x00000303,
0x00000003,
0x00000303,
@@ -426,6 +427,7 @@ static uint32_t zend_vm_opcodes_flags[200] = {
0x0000010b,
0x0000200b,
0x00000101,
+ 0x00000101,
};
ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 653e320b85..e7e40c8a85 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -282,7 +282,8 @@ END_EXTERN_C()
#define ZEND_MATCH_ERROR 197
#define ZEND_JMP_NULL 198
#define ZEND_CHECK_UNDEF_ARGS 199
+#define ZEND_FETCH_GLOBALS 200
-#define ZEND_VM_LAST_OPCODE 199
+#define ZEND_VM_LAST_OPCODE 200
#endif
diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c
index 04c5043210..13ab6c3bb8 100644
--- a/Zend/zend_weakrefs.c
+++ b/Zend/zend_weakrefs.c
@@ -181,8 +181,8 @@ static zend_object* zend_weakref_new(zend_class_entry *ce) {
return &wr->std;
}
-static zend_always_inline zend_bool zend_weakref_find(zval *referent, zval *return_value) {
- void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), (zend_ulong) Z_OBJ_P(referent));
+static zend_always_inline bool zend_weakref_find(zend_object *referent, zval *return_value) {
+ void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), (zend_ulong) referent);
if (!tagged_ptr) {
return 0;
}
@@ -209,13 +209,13 @@ found_weakref:
return 0;
}
-static zend_always_inline void zend_weakref_create(zval *referent, zval *return_value) {
+static zend_always_inline void zend_weakref_create(zend_object *referent, zval *return_value) {
zend_weakref *wr;
object_init_ex(return_value, zend_ce_weakref);
wr = zend_weakref_fetch(return_value);
- wr->referent = Z_OBJ_P(referent);
+ wr->referent = referent;
zend_weakref_register(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF));
}
@@ -245,10 +245,10 @@ ZEND_COLD ZEND_METHOD(WeakReference, __construct)
ZEND_METHOD(WeakReference, create)
{
- zval *referent;
+ zend_object *referent;
ZEND_PARSE_PARAMETERS_START(1,1)
- Z_PARAM_OBJECT(referent)
+ Z_PARAM_OBJ(referent)
ZEND_PARSE_PARAMETERS_END();
if (zend_weakref_find(referent, return_value)) {
@@ -402,12 +402,12 @@ static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop
zend_ulong obj_addr;
zval *val;
ZEND_HASH_FOREACH_NUM_KEY_VAL(&wm->ht, obj_addr, val) {
+ zend_object *obj = (zend_object*)obj_addr;
zval pair;
- zval obj_zv;
array_init(&pair);
- ZVAL_OBJ_COPY(&obj_zv, (zend_object *) obj_addr);
- add_assoc_zval(&pair, "key", &obj_zv);
+ GC_ADDREF(obj);
+ add_assoc_object(&pair, "key", obj);
Z_TRY_ADDREF_P(val);
add_assoc_zval(&pair, "value", val);
@@ -597,11 +597,7 @@ ZEND_METHOD(WeakMap, getIterator)
void zend_register_weakref_ce(void) /* {{{ */
{
- zend_class_entry ce;
-
- INIT_CLASS_ENTRY(ce, "WeakReference", class_WeakReference_methods);
- zend_ce_weakref = zend_register_internal_class(&ce);
- zend_ce_weakref->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+ zend_ce_weakref = register_class_WeakReference();
zend_ce_weakref->create_object = zend_weakref_new;
zend_ce_weakref->serialize = zend_class_serialize_deny;
@@ -613,11 +609,7 @@ void zend_register_weakref_ce(void) /* {{{ */
zend_weakref_handlers.free_obj = zend_weakref_free;
zend_weakref_handlers.clone_obj = NULL;
- INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods);
- zend_ce_weakmap = zend_register_internal_class(&ce);
- zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
- zend_class_implements(
- zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
+ zend_ce_weakmap = register_class_WeakMap(zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
zend_ce_weakmap->create_object = zend_weakmap_create_object;
zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator;
diff --git a/Zend/zend_weakrefs.stub.php b/Zend/zend_weakrefs.stub.php
index c341e0a463..e8c0c3c92d 100644
--- a/Zend/zend_weakrefs.stub.php
+++ b/Zend/zend_weakrefs.stub.php
@@ -1,7 +1,8 @@
<?php
-/** @generate-function-entries */
+/** @generate-class-entries */
+/** @strict-properties */
final class WeakReference
{
public function __construct() {}
@@ -11,6 +12,7 @@ final class WeakReference
public function get(): ?object {}
}
+/** @strict-properties */
final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
{
/**
diff --git a/Zend/zend_weakrefs_arginfo.h b/Zend/zend_weakrefs_arginfo.h
index da953a9800..d737faebe5 100644
--- a/Zend/zend_weakrefs_arginfo.h
+++ b/Zend/zend_weakrefs_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0569bc7e10a1ec15a3a9eec481da27b647eb1d1d */
+ * Stub hash: 97fff017125955a3def85d9ed5a31746de7b808a */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_WeakReference___construct, 0, 0, 0)
ZEND_END_ARG_INFO()
@@ -63,3 +63,26 @@ static const zend_function_entry class_WeakMap_methods[] = {
ZEND_ME(WeakMap, getIterator, arginfo_class_WeakMap_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
+
+static zend_class_entry *register_class_WeakReference(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "WeakReference", class_WeakReference_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+
+ return class_entry;
+}
+
+static zend_class_entry *register_class_WeakMap(zend_class_entry *class_entry_ArrayAccess, zend_class_entry *class_entry_Countable, zend_class_entry *class_entry_IteratorAggregate)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods);
+ class_entry = zend_register_internal_class_ex(&ce, NULL);
+ class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
+ zend_class_implements(class_entry, 3, class_entry_ArrayAccess, class_entry_Countable, class_entry_IteratorAggregate);
+
+ return class_entry;
+}