mirror of
https://github.com/bdnugget/php-src.git
synced 2026-11-12 03:30:50 +00:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8a73dd55 | ||
|
|
ceefc0947a | ||
|
|
2928e412e6 | ||
|
|
770571bce9 | ||
|
|
6cdf477254 | ||
|
|
5784b8f8e6 | ||
|
|
ee573eba67 | ||
|
|
724e98498a | ||
|
|
62062dcd0e | ||
|
|
dfe6fdaa72 | ||
|
|
8d09c84bd3 | ||
|
|
955303ddc8 | ||
|
|
2a0e037c0d | ||
|
|
9876b2c2cf | ||
|
|
5ea2b70645 | ||
|
|
29711264a8 | ||
|
|
dbf30365aa | ||
|
|
9cb8cb4797 | ||
|
|
8a884d628b |
No files matched your search
@@ -100,6 +100,10 @@ PHP NEWS
|
||||
- GD:
|
||||
. Fixed bug #61221 (imagegammacorrect function loses alpha channel). (cmb)
|
||||
|
||||
- JSON
|
||||
. Fixed bug #68546 (json_decode() Fatal error: Cannot access property
|
||||
started with '\0'). (Jakub Zelenka)
|
||||
|
||||
- mysqlnd:
|
||||
. Fixed Bug #69796 (mysqli_stmt::fetch doesn't assign null values to
|
||||
bound variables). (Laruence)
|
||||
@@ -269,8 +273,6 @@ PHP NEWS
|
||||
(JSON extension includes a problematic license statement). (Jakub Zelenka)
|
||||
. Fixed bug #68938 (json_decode() decodes empty string without error).
|
||||
(jeremy at bat-country dot us)
|
||||
. Fixed bug #68546 (json_decode() Fatal error: Cannot access property
|
||||
started with '\0'). (Jakub Zelenka)
|
||||
|
||||
- LDAP
|
||||
. Fixed bug #47222 (Implement LDAP_OPT_DIAGNOSTIC_MESSAGE). (Andreas Heigl)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
--TEST--
|
||||
jump 15: goto from loop (forward)
|
||||
--FILE--
|
||||
<?php
|
||||
$ar = array("1","2","3");
|
||||
foreach ($ar as $val) {
|
||||
switch ($val) {
|
||||
case "1":
|
||||
echo "1: ok\n";
|
||||
break;
|
||||
case "2":
|
||||
echo "2: ok\n";
|
||||
goto L1;
|
||||
case "3":
|
||||
echo "bug\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo "bug\n";
|
||||
L1:
|
||||
try {
|
||||
echo "3: ok\n";
|
||||
} finally {
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1: ok
|
||||
2: ok
|
||||
3: ok
|
||||
@@ -1,23 +0,0 @@
|
||||
--TEST--
|
||||
Temporary leak on exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function ops() {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
try {
|
||||
$x = 2;
|
||||
$y = new stdClass;
|
||||
while ($x-- && new stdClass) {
|
||||
$r = [$x] + ($y ? ((array) $x) + [2] : ops());
|
||||
$y = (array) $y;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
==DONE==
|
||||
@@ -1,32 +0,0 @@
|
||||
--TEST--
|
||||
Temporary leak on rope (encapsed string)
|
||||
--FILE--
|
||||
<?php
|
||||
class Obj {
|
||||
function __get($x) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
$x = new Obj;
|
||||
$y = 0;
|
||||
|
||||
try {
|
||||
$r = "$y|$x->x|";
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$r = "$x->x|$y|";
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$r = "$y|$y|$x->x";
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
==DONE==
|
||||
@@ -1,19 +0,0 @@
|
||||
--TEST--
|
||||
Fundamental memory leak test on temporaries
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function ops() {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
try{
|
||||
$x = 1;
|
||||
$r = [$x] + ops();
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
==DONE==
|
||||
@@ -1,44 +0,0 @@
|
||||
--TEST--
|
||||
Temporary leak with switch
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function ops() {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
$a = [new stdClass, new stdClass];
|
||||
switch ($a[0]) {
|
||||
case false:
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
$x = 2;
|
||||
$y = new stdClass;
|
||||
while ($x-- && new stdClass) {
|
||||
$r = [$x] + ($y ? ((array) $x) + [2] : ops());
|
||||
$y = (array) $y;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($a[0]) {
|
||||
case false:
|
||||
break;
|
||||
default:
|
||||
$x = 2;
|
||||
$y = new stdClass;
|
||||
while ($x-- && new stdClass) {
|
||||
$r = [$x] + ($y ? ((array) $x) + [2] : ops());
|
||||
$y = (array) $y;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
==DONE==
|
||||
@@ -1,48 +0,0 @@
|
||||
--TEST--
|
||||
Temporary leak with foreach
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function ops() {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
$a = [new stdClass, new stdClass];
|
||||
foreach ([$a, [new stdClass]] as $b) {
|
||||
switch ($b[0]) {
|
||||
case false:
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
$x = 2;
|
||||
$y = new stdClass;
|
||||
while ($x-- && new stdClass) {
|
||||
$r = [$x] + ($y ? ((array) $x) + [2] : ops());
|
||||
$y = (array) $y;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ([$a, [new stdClass]] as $b) {
|
||||
try {
|
||||
switch ($b[0]) {
|
||||
case false:
|
||||
break;
|
||||
default:
|
||||
$x = 2;
|
||||
$y = new stdClass;
|
||||
while ($x-- && new stdClass) {
|
||||
$r = [$x] + ($y ? ((array) $x) + [2] : ops());
|
||||
$y = (array) $y;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
==DONE==
|
||||
@@ -1045,6 +1045,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
|
||||
zval orig_user_error_handler;
|
||||
zend_bool in_compilation;
|
||||
zend_class_entry *saved_class_entry;
|
||||
zend_stack loop_var_stack;
|
||||
zend_stack delayed_oplines_stack;
|
||||
zend_array *symbol_table;
|
||||
|
||||
@@ -1211,6 +1212,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
|
||||
if (in_compilation) {
|
||||
saved_class_entry = CG(active_class_entry);
|
||||
CG(active_class_entry) = NULL;
|
||||
SAVE_STACK(loop_var_stack);
|
||||
SAVE_STACK(delayed_oplines_stack);
|
||||
CG(in_compilation) = 0;
|
||||
}
|
||||
@@ -1229,6 +1231,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
|
||||
|
||||
if (in_compilation) {
|
||||
CG(active_class_entry) = saved_class_entry;
|
||||
RESTORE_STACK(loop_var_stack);
|
||||
RESTORE_STACK(delayed_oplines_stack);
|
||||
CG(in_compilation) = 1;
|
||||
}
|
||||
|
||||
+122
-123
@@ -32,7 +32,6 @@
|
||||
#include "zend_multibyte.h"
|
||||
#include "zend_language_scanner.h"
|
||||
#include "zend_inheritance.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
#define SET_NODE(target, src) do { \
|
||||
target ## _type = (src)->op_type; \
|
||||
@@ -211,21 +210,16 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
|
||||
CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
|
||||
CG(context).vars_size = 0;
|
||||
CG(context).literals_size = 0;
|
||||
CG(context).current_brk_cont = -1;
|
||||
CG(context).backpatch_count = 0;
|
||||
CG(context).in_finally = 0;
|
||||
CG(context).fast_call_var = -1;
|
||||
CG(context).current_brk_cont = -1;
|
||||
CG(context).last_brk_cont = 0;
|
||||
CG(context).brk_cont_array = NULL;
|
||||
CG(context).labels = NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
|
||||
{
|
||||
if (CG(context).brk_cont_array) {
|
||||
efree(CG(context).brk_cont_array);
|
||||
}
|
||||
if (CG(context).labels) {
|
||||
zend_hash_destroy(CG(context).labels);
|
||||
FREE_HASHTABLE(CG(context).labels);
|
||||
@@ -289,6 +283,7 @@ void zend_file_context_end(zend_file_context *prev_context) /* {{{ */
|
||||
|
||||
void zend_init_compiler_data_structures(void) /* {{{ */
|
||||
{
|
||||
zend_stack_init(&CG(loop_var_stack), sizeof(znode));
|
||||
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
|
||||
CG(active_class_entry) = NULL;
|
||||
CG(in_compilation) = 0;
|
||||
@@ -321,6 +316,7 @@ void init_compiler(void) /* {{{ */
|
||||
|
||||
void shutdown_compiler(void) /* {{{ */
|
||||
{
|
||||
zend_stack_destroy(&CG(loop_var_stack));
|
||||
zend_stack_destroy(&CG(delayed_oplines_stack));
|
||||
zend_hash_destroy(&CG(filenames_table));
|
||||
zend_hash_destroy(&CG(const_filenames));
|
||||
@@ -566,13 +562,17 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
|
||||
zend_brk_cont_element *brk_cont_element;
|
||||
int parent = CG(context).current_brk_cont;
|
||||
|
||||
CG(context).current_brk_cont = CG(context).last_brk_cont;
|
||||
CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
|
||||
brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
|
||||
brk_cont_element->parent = parent;
|
||||
if (loop_var && (loop_var->op_type & (IS_TMP_VAR|IS_VAR))) {
|
||||
brk_cont_element->loop_var = *loop_var;
|
||||
|
||||
if (loop_var) {
|
||||
zend_stack_push(&CG(loop_var_stack), loop_var);
|
||||
brk_cont_element->start = get_next_op_number(CG(active_op_array));
|
||||
} else {
|
||||
brk_cont_element->loop_var.op_type = IS_UNUSED;
|
||||
/* The start field is used to free temporary variables in case of exceptions.
|
||||
* We won't try to free something of we don't have loop variable. */
|
||||
brk_cont_element->start = -1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@@ -580,10 +580,14 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
|
||||
static inline void zend_end_loop(int cont_addr) /* {{{ */
|
||||
{
|
||||
zend_brk_cont_element *brk_cont_element
|
||||
= &CG(context).brk_cont_array[CG(context).current_brk_cont];
|
||||
= &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
|
||||
brk_cont_element->cont = cont_addr;
|
||||
brk_cont_element->brk = get_next_op_number(CG(active_op_array));
|
||||
CG(context).current_brk_cont = brk_cont_element->parent;
|
||||
|
||||
if (brk_cont_element->start >= 0) {
|
||||
zend_stack_del_top(&CG(loop_var_stack));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -870,17 +874,81 @@ static void str_dtor(zval *zv) /* {{{ */ {
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */
|
||||
{
|
||||
zend_label *dest;
|
||||
int current, distance;
|
||||
zval *label;
|
||||
|
||||
if (pass2) {
|
||||
label = RT_CONSTANT(op_array, opline->op2);
|
||||
} else {
|
||||
label = CT_CONSTANT_EX(op_array, opline->op2.constant);
|
||||
}
|
||||
if (CG(context).labels == NULL ||
|
||||
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
|
||||
|
||||
if (pass2) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = opline->lineno;
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
|
||||
} else {
|
||||
/* Label is not defined. Delay to pass 2. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
opline->op1.opline_num = dest->opline_num;
|
||||
zval_dtor(label);
|
||||
ZVAL_NULL(label);
|
||||
|
||||
/* Check that we are not moving into loop or switch */
|
||||
current = opline->extended_value;
|
||||
for (distance = 0; current != dest->brk_cont; distance++) {
|
||||
if (current == -1) {
|
||||
if (pass2) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = opline->lineno;
|
||||
}
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
|
||||
}
|
||||
current = op_array->brk_cont_array[current].parent;
|
||||
}
|
||||
|
||||
if (distance == 0) {
|
||||
/* Nothing to break out of, optimize to ZEND_JMP */
|
||||
opline->opcode = ZEND_JMP;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
} else {
|
||||
/* Set real break distance */
|
||||
ZVAL_LONG(label, distance);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_bool zend_is_call(zend_ast *ast);
|
||||
|
||||
static void generate_free_loop_var(znode *var) /* {{{ */
|
||||
static int generate_free_loop_var(znode *var) /* {{{ */
|
||||
{
|
||||
if (var->op_type != IS_UNUSED) {
|
||||
zend_op *opline = get_next_op(CG(active_op_array));
|
||||
switch (var->op_type) {
|
||||
case IS_UNUSED:
|
||||
/* Stack separator on function boundary, stop applying */
|
||||
return 1;
|
||||
case IS_VAR:
|
||||
case IS_TMP_VAR:
|
||||
{
|
||||
zend_op *opline = get_next_op(CG(active_op_array));
|
||||
|
||||
opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
|
||||
SET_NODE(opline->op1, var);
|
||||
SET_UNUSED(opline->op2);
|
||||
opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
|
||||
SET_NODE(opline->op1, var);
|
||||
SET_UNUSED(opline->op2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -3443,12 +3511,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
|
||||
|
||||
static void zend_free_foreach_and_switch_variables(void) /* {{{ */
|
||||
{
|
||||
int array_offset = CG(context).current_brk_cont;
|
||||
while (array_offset != -1) {
|
||||
zend_brk_cont_element *brk_cont = &CG(context).brk_cont_array[array_offset];
|
||||
generate_free_loop_var(&brk_cont->loop_var);
|
||||
array_offset = brk_cont->parent;
|
||||
}
|
||||
zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -3550,6 +3613,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
|
||||
} else {
|
||||
int array_offset = CG(context).current_brk_cont;
|
||||
zend_long nest_level = depth;
|
||||
znode *loop_var = zend_stack_top(&CG(loop_var_stack));
|
||||
|
||||
do {
|
||||
if (array_offset == -1) {
|
||||
@@ -3558,11 +3622,12 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
|
||||
depth, depth == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
if (nest_level > 1) {
|
||||
generate_free_loop_var(&CG(context).brk_cont_array[array_offset].loop_var);
|
||||
if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) {
|
||||
generate_free_loop_var(loop_var);
|
||||
loop_var--;
|
||||
}
|
||||
|
||||
array_offset = CG(context).brk_cont_array[array_offset].parent;
|
||||
array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
|
||||
} while (--nest_level > 0);
|
||||
}
|
||||
opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
|
||||
@@ -3571,108 +3636,16 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline) /* {{{ */
|
||||
{
|
||||
zend_label *dest;
|
||||
int current, distance, free_vars;
|
||||
zval *label;
|
||||
|
||||
if (pass2_opline) {
|
||||
label = RT_CONSTANT(op_array, pass2_opline->op2);
|
||||
} else {
|
||||
label = &label_node->u.constant;
|
||||
}
|
||||
if (CG(context).labels == NULL ||
|
||||
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
|
||||
|
||||
if (pass2_opline) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = pass2_opline->lineno;
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
|
||||
} else {
|
||||
/* Label is not defined. Delay to pass 2. */
|
||||
zend_op *opline;
|
||||
|
||||
current = CG(context).current_brk_cont;
|
||||
while (current != -1) {
|
||||
if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
|
||||
zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, label_node);
|
||||
opline->extended_value = CG(context).current_brk_cont;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
zval_dtor(label);
|
||||
ZVAL_NULL(label);
|
||||
|
||||
/* Check that we are not moving into loop or switch */
|
||||
if (pass2_opline) {
|
||||
current = pass2_opline->extended_value;
|
||||
} else {
|
||||
current = CG(context).current_brk_cont;
|
||||
}
|
||||
for (distance = 0, free_vars = 0; current != dest->brk_cont; distance++) {
|
||||
if (current == -1) {
|
||||
if (pass2_opline) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = pass2_opline->lineno;
|
||||
}
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
|
||||
}
|
||||
if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
|
||||
if (pass2_opline) {
|
||||
free_vars++;
|
||||
} else {
|
||||
generate_free_loop_var(&CG(context).brk_cont_array[current].loop_var);
|
||||
}
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
|
||||
if (pass2_opline) {
|
||||
if (free_vars) {
|
||||
current = pass2_opline->extended_value;
|
||||
while (current != dest->brk_cont) {
|
||||
if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
|
||||
zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
|
||||
|
||||
(pass2_opline - free_vars)->opcode = brk_opline->opcode;
|
||||
(pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
|
||||
if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
|
||||
(pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
|
||||
} else {
|
||||
(pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
|
||||
ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
|
||||
}
|
||||
free_vars--;
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
}
|
||||
pass2_opline->opcode = ZEND_JMP;
|
||||
pass2_opline->op1.opline_num = dest->opline_num;
|
||||
SET_UNUSED(pass2_opline->op2);
|
||||
pass2_opline->extended_value = 0;
|
||||
} else {
|
||||
zend_op *opline = zend_emit_op(NULL, ZEND_JMP, NULL, NULL);
|
||||
opline->op1.opline_num = dest->opline_num;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_goto(zend_ast *ast) /* {{{ */
|
||||
{
|
||||
zend_ast *label_ast = ast->child[0];
|
||||
znode label_node;
|
||||
zend_op *opline;
|
||||
|
||||
zend_compile_expr(&label_node, label_ast);
|
||||
zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
|
||||
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
|
||||
opline->extended_value = CG(context).current_brk_cont;
|
||||
zend_resolve_goto_label(CG(active_op_array), opline, 0);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -4768,6 +4741,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
|
||||
opline_ext->lineno = decl->start_lineno;
|
||||
}
|
||||
|
||||
{
|
||||
/* Push a separator to the loop variable stack */
|
||||
znode dummy_var;
|
||||
dummy_var.op_type = IS_UNUSED;
|
||||
|
||||
zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
|
||||
}
|
||||
|
||||
zend_compile_params(params_ast, return_type_ast);
|
||||
if (uses_ast) {
|
||||
zend_compile_closure_uses(uses_ast);
|
||||
@@ -4785,6 +4766,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
|
||||
pass_two(CG(active_op_array));
|
||||
zend_oparray_context_end(&orig_oparray_context);
|
||||
|
||||
/* Pop the loop variable stack separator */
|
||||
zend_stack_del_top(&CG(loop_var_stack));
|
||||
|
||||
CG(active_op_array) = orig_op_array;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -6301,7 +6285,10 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
|
||||
{
|
||||
zend_ast *expr_ast = ast->child[0];
|
||||
znode silence_node;
|
||||
uint32_t begin_opline_num, end_opline_num;
|
||||
zend_brk_cont_element *brk_cont_element;
|
||||
|
||||
begin_opline_num = get_next_op_number(CG(active_op_array));
|
||||
zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
|
||||
|
||||
if (expr_ast->kind == ZEND_AST_VAR) {
|
||||
@@ -6312,7 +6299,15 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
|
||||
zend_compile_expr(result, expr_ast);
|
||||
}
|
||||
|
||||
end_opline_num = get_next_op_number(CG(active_op_array));
|
||||
zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
|
||||
|
||||
/* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
|
||||
* EG(error_reporting) value on exception */
|
||||
brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
|
||||
brk_cont_element->start = begin_opline_num;
|
||||
brk_cont_element->cont = brk_cont_element->brk = end_opline_num;
|
||||
brk_cont_element->parent = -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -6640,6 +6635,10 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
|
||||
GET_NODE(result, opline->result);
|
||||
} else {
|
||||
uint32_t var;
|
||||
zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array));
|
||||
info->start = rope_init_lineno;
|
||||
info->parent = CG(context).current_brk_cont;
|
||||
info->cont = info->brk = opline - CG(active_op_array)->opcodes;
|
||||
|
||||
init_opline->extended_value = j;
|
||||
opline->opcode = ZEND_ROPE_END;
|
||||
|
||||
+27
-35
@@ -109,36 +109,15 @@ typedef struct _zend_declarables {
|
||||
zend_long ticks;
|
||||
} zend_declarables;
|
||||
|
||||
typedef struct _zend_brk_cont_element {
|
||||
int cont;
|
||||
int brk;
|
||||
int parent;
|
||||
znode loop_var;
|
||||
} zend_brk_cont_element;
|
||||
|
||||
typedef struct _zend_label {
|
||||
int brk_cont;
|
||||
uint32_t opline_num;
|
||||
} zend_label;
|
||||
|
||||
typedef struct _zend_try_catch_element {
|
||||
uint32_t try_op;
|
||||
uint32_t catch_op; /* ketchup! */
|
||||
uint32_t finally_op;
|
||||
uint32_t finally_end;
|
||||
} zend_try_catch_element;
|
||||
|
||||
/* Compilation context that is different for each op array. */
|
||||
typedef struct _zend_oparray_context {
|
||||
uint32_t opcodes_size;
|
||||
int vars_size;
|
||||
int literals_size;
|
||||
int current_brk_cont;
|
||||
int backpatch_count;
|
||||
int in_finally;
|
||||
uint32_t fast_call_var;
|
||||
int current_brk_cont;
|
||||
int last_brk_cont;
|
||||
zend_brk_cont_element *brk_cont_array;
|
||||
HashTable *labels;
|
||||
} zend_oparray_context;
|
||||
|
||||
@@ -184,6 +163,26 @@ struct _zend_op {
|
||||
zend_uchar result_type;
|
||||
};
|
||||
|
||||
|
||||
typedef struct _zend_brk_cont_element {
|
||||
int start;
|
||||
int cont;
|
||||
int brk;
|
||||
int parent;
|
||||
} zend_brk_cont_element;
|
||||
|
||||
typedef struct _zend_label {
|
||||
int brk_cont;
|
||||
uint32_t opline_num;
|
||||
} zend_label;
|
||||
|
||||
typedef struct _zend_try_catch_element {
|
||||
uint32_t try_op;
|
||||
uint32_t catch_op; /* ketchup! */
|
||||
uint32_t finally_op;
|
||||
uint32_t finally_end;
|
||||
} zend_try_catch_element;
|
||||
|
||||
/* method flags (types) */
|
||||
#define ZEND_ACC_STATIC 0x01
|
||||
#define ZEND_ACC_ABSTRACT 0x02
|
||||
@@ -329,11 +328,6 @@ typedef struct _zend_internal_function_info {
|
||||
zend_bool _is_variadic;
|
||||
} zend_internal_function_info;
|
||||
|
||||
#define ZEND_LIVE_ROPE 1
|
||||
#define ZEND_LIVE_SILENCE 2
|
||||
#define ZEND_LIVE_LOOP 3
|
||||
#define ZEND_LIVE_MASK 3
|
||||
|
||||
struct _zend_op_array {
|
||||
/* Common elements */
|
||||
zend_uchar type;
|
||||
@@ -357,12 +351,10 @@ struct _zend_op_array {
|
||||
int last_var;
|
||||
uint32_t T;
|
||||
zend_string **vars;
|
||||
uint32_t *T_liveliness;
|
||||
|
||||
void **run_time_cache;
|
||||
int cache_size;
|
||||
|
||||
int last_brk_cont;
|
||||
int last_try_catch;
|
||||
zend_brk_cont_element *brk_cont_array;
|
||||
zend_try_catch_element *try_catch_array;
|
||||
|
||||
/* static variables support */
|
||||
@@ -377,6 +369,9 @@ struct _zend_op_array {
|
||||
int last_literal;
|
||||
zval *literals;
|
||||
|
||||
int cache_size;
|
||||
void **run_time_cache;
|
||||
|
||||
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
|
||||
};
|
||||
|
||||
@@ -717,7 +712,7 @@ void zend_do_extended_fcall_end(void);
|
||||
|
||||
void zend_verify_namespace(void);
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline);
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2);
|
||||
|
||||
ZEND_API void function_add_ref(zend_function *function);
|
||||
|
||||
@@ -732,8 +727,6 @@ 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_generate_var_liveliness_info(zend_op_array *op_array);
|
||||
ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array);
|
||||
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
|
||||
ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce);
|
||||
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
|
||||
@@ -958,7 +951,6 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
|
||||
#define ZEND_ARRAY_SIZE_SHIFT 2
|
||||
|
||||
/* Pseudo-opcodes that are used only temporarily during compilation */
|
||||
#define ZEND_GOTO 253
|
||||
#define ZEND_BRK 254
|
||||
#define ZEND_CONT 255
|
||||
|
||||
|
||||
+71
-60
@@ -1939,6 +1939,31 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
|
||||
}
|
||||
}
|
||||
|
||||
static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
|
||||
{
|
||||
zend_brk_cont_element *jmp_to;
|
||||
|
||||
do {
|
||||
ZEND_ASSERT(array_offset != -1);
|
||||
jmp_to = &op_array->brk_cont_array[array_offset];
|
||||
if (nest_levels > 1 && jmp_to->start >= 0) {
|
||||
zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
|
||||
|
||||
if (brk_opline->opcode == ZEND_FREE) {
|
||||
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
|
||||
} else if (brk_opline->opcode == ZEND_FE_FREE) {
|
||||
zval *var = EX_VAR(brk_opline->op1.var);
|
||||
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(var));
|
||||
}
|
||||
zval_ptr_dtor_nogc(var);
|
||||
}
|
||||
}
|
||||
array_offset = jmp_to->parent;
|
||||
} while (--nest_levels > 0);
|
||||
return jmp_to;
|
||||
}
|
||||
|
||||
#if ZEND_INTENSIVE_DEBUGGING
|
||||
|
||||
#define CHECK_SYMBOL_TABLES() \
|
||||
@@ -2359,66 +2384,7 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut
|
||||
|
||||
static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
|
||||
{
|
||||
if (EX(func)->op_array.T_liveliness
|
||||
&& op_num < EX(func)->op_array.last
|
||||
&& EX(func)->op_array.T_liveliness[op_num] != (uint32_t)-1) {
|
||||
uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num];
|
||||
uint32_t *catch_off = NULL;
|
||||
uint32_t var = *off;
|
||||
|
||||
if (catch_op_num && EX(func)->op_array.T_liveliness[catch_op_num] != (uint32_t)-1) {
|
||||
catch_off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[catch_op_num];
|
||||
}
|
||||
|
||||
do {
|
||||
/* we should be safe to assume that all temporaries at catch_op_num will be present at op_num too, in same order */
|
||||
if (catch_off && *catch_off == var) {
|
||||
catch_off++;
|
||||
var = *(++off);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_ROPE) {
|
||||
/* free incomplete rope */
|
||||
zend_string **rope;
|
||||
zend_op *last;
|
||||
|
||||
var = var & ~ZEND_LIVE_ROPE;
|
||||
rope = (zend_string **) EX_VAR(var);
|
||||
last = EX(func)->op_array.opcodes + op_num;
|
||||
while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
|
||||
|| last->result.var != var) {
|
||||
ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
|
||||
last--;
|
||||
}
|
||||
if (last->opcode == ZEND_ROPE_INIT) {
|
||||
zend_string_release(*rope);
|
||||
} else {
|
||||
int j = last->extended_value;
|
||||
do {
|
||||
zend_string_release(rope[j]);
|
||||
} while (j--);
|
||||
}
|
||||
} else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_SILENCE) {
|
||||
/* restore previous error_reporting value */
|
||||
var = var & ~ZEND_LIVE_SILENCE;
|
||||
if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) {
|
||||
EG(error_reporting) = Z_LVAL_P(EX_VAR(var));
|
||||
}
|
||||
} else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_LOOP) {
|
||||
/* free loop variables */
|
||||
var = var & ~ZEND_LIVE_LOOP;
|
||||
if (Z_TYPE_P(EX_VAR(var)) != IS_ARRAY && Z_FE_ITER_P(EX_VAR(var)) != (uint32_t) -1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(EX_VAR(var)));
|
||||
}
|
||||
zval_ptr_dtor_nogc(EX_VAR(var));
|
||||
} else {
|
||||
zval_ptr_dtor_nogc(EX_VAR(var));
|
||||
}
|
||||
var = *(++off);
|
||||
} while (var != (uint32_t)-1);
|
||||
}
|
||||
|
||||
int i;
|
||||
if (UNEXPECTED(EX(call))) {
|
||||
zend_execute_data *call = EX(call);
|
||||
zend_op *opline = EX(func)->op_array.opcodes + op_num;
|
||||
@@ -2532,6 +2498,51 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
|
||||
call = EX(call);
|
||||
} while (call);
|
||||
}
|
||||
|
||||
for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
|
||||
const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
|
||||
if (brk_cont->start < 0) {
|
||||
continue;
|
||||
} else if (brk_cont->start > op_num) {
|
||||
/* further blocks will not be relevant... */
|
||||
break;
|
||||
} else if (op_num < brk_cont->brk) {
|
||||
if (!catch_op_num || catch_op_num >= brk_cont->brk) {
|
||||
zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
|
||||
|
||||
if (brk_opline->opcode == ZEND_FREE) {
|
||||
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
|
||||
} else if (brk_opline->opcode == ZEND_FE_FREE) {
|
||||
zval *var = EX_VAR(brk_opline->op1.var);
|
||||
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(var));
|
||||
}
|
||||
zval_ptr_dtor_nogc(var);
|
||||
} else if (brk_opline->opcode == ZEND_ROPE_END) {
|
||||
zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var);
|
||||
zend_op *last = EX(func)->op_array.opcodes + op_num;
|
||||
while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
|
||||
|| last->result.var != brk_opline->op1.var) {
|
||||
ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
|
||||
last--;
|
||||
}
|
||||
if (last->opcode == ZEND_ROPE_INIT) {
|
||||
zend_string_release(*rope);
|
||||
} else {
|
||||
int j = last->extended_value;
|
||||
do {
|
||||
zend_string_release(rope[j]);
|
||||
} while (j--);
|
||||
}
|
||||
} else if (brk_opline->opcode == ZEND_END_SILENCE) {
|
||||
/* restore previous error_reporting value */
|
||||
if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
|
||||
EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ typedef struct _zend_ini_entry zend_ini_entry;
|
||||
|
||||
|
||||
struct _zend_compiler_globals {
|
||||
zend_stack loop_var_stack;
|
||||
|
||||
zend_class_entry *active_class_entry;
|
||||
|
||||
zend_string *compiled_filename;
|
||||
|
||||
+15
-222
@@ -65,7 +65,6 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
|
||||
op_array->vars = NULL;
|
||||
|
||||
op_array->T = 0;
|
||||
op_array->T_liveliness = NULL;
|
||||
|
||||
op_array->function_name = NULL;
|
||||
op_array->filename = zend_get_compiled_filename();
|
||||
@@ -78,7 +77,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
|
||||
op_array->scope = NULL;
|
||||
op_array->prototype = NULL;
|
||||
|
||||
op_array->brk_cont_array = NULL;
|
||||
op_array->try_catch_array = NULL;
|
||||
op_array->last_brk_cont = 0;
|
||||
|
||||
op_array->static_variables = NULL;
|
||||
op_array->last_try_catch = 0;
|
||||
@@ -382,12 +383,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
|
||||
if (op_array->doc_comment) {
|
||||
zend_string_release(op_array->doc_comment);
|
||||
}
|
||||
if (op_array->brk_cont_array) {
|
||||
efree(op_array->brk_cont_array);
|
||||
}
|
||||
if (op_array->try_catch_array) {
|
||||
efree(op_array->try_catch_array);
|
||||
}
|
||||
if (op_array->T_liveliness) {
|
||||
efree(op_array->T_liveliness);
|
||||
}
|
||||
if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
|
||||
zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
|
||||
}
|
||||
@@ -446,9 +447,9 @@ int get_next_op_number(zend_op_array *op_array)
|
||||
|
||||
zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array)
|
||||
{
|
||||
CG(context).last_brk_cont++;
|
||||
CG(context).brk_cont_array = erealloc(CG(context).brk_cont_array, sizeof(zend_brk_cont_element)*CG(context).last_brk_cont);
|
||||
return &CG(context).brk_cont_array[CG(context).last_brk_cont-1];
|
||||
op_array->last_brk_cont++;
|
||||
op_array->brk_cont_array = erealloc(op_array->brk_cont_array, sizeof(zend_brk_cont_element)*op_array->last_brk_cont);
|
||||
return &op_array->brk_cont_array[op_array->last_brk_cont-1];
|
||||
}
|
||||
|
||||
static void zend_update_extended_info(zend_op_array *op_array)
|
||||
@@ -575,7 +576,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num,
|
||||
fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var;
|
||||
|
||||
/* generate a FAST_CALL to finally block */
|
||||
start_op = get_next_op_number(op_array);
|
||||
start_op = get_next_op_number(op_array);
|
||||
|
||||
opline = get_next_op(op_array);
|
||||
opline->opcode = ZEND_FAST_CALL;
|
||||
@@ -671,7 +672,7 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
|
||||
int array_offset = opline->op1.num;
|
||||
zend_brk_cont_element *jmp_to;
|
||||
do {
|
||||
jmp_to = &CG(context).brk_cont_array[array_offset];
|
||||
jmp_to = &op_array->brk_cont_array[array_offset];
|
||||
if (nest_levels > 1) {
|
||||
array_offset = jmp_to->parent;
|
||||
}
|
||||
@@ -699,8 +700,11 @@ static void zend_resolve_finally_calls(zend_op_array *op_array)
|
||||
break;
|
||||
case ZEND_GOTO:
|
||||
if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) {
|
||||
uint32_t num = opline->op2.constant;
|
||||
|
||||
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
|
||||
zend_resolve_goto_label(op_array, NULL, opline);
|
||||
zend_resolve_goto_label(op_array, opline, 1);
|
||||
opline->op2.constant = num;
|
||||
}
|
||||
/* break omitted intentionally */
|
||||
case ZEND_JMP:
|
||||
@@ -747,9 +751,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
|
||||
op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
|
||||
CG(context).literals_size = op_array->last_literal;
|
||||
}
|
||||
|
||||
zend_generate_var_liveliness_info(op_array);
|
||||
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
while (opline < end) {
|
||||
@@ -786,7 +787,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
|
||||
break;
|
||||
case ZEND_GOTO:
|
||||
if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) {
|
||||
zend_resolve_goto_label(op_array, NULL, opline);
|
||||
zend_resolve_goto_label(op_array, opline, 1);
|
||||
}
|
||||
/* break omitted intentionally */
|
||||
case ZEND_JMP:
|
||||
@@ -839,214 +840,6 @@ int pass_two_wrapper(zval *el)
|
||||
return pass_two((zend_op_array *) Z_PTR_P(el));
|
||||
}
|
||||
|
||||
/* The following liveliness analyzing algorithm assumes that
|
||||
* 1) temporary variables are defined before use
|
||||
* 2) they have linear live-ranges without "holes"
|
||||
* 3) Opcodes never use and define the same temorary variables
|
||||
*/
|
||||
typedef struct _op_var_info {
|
||||
struct _op_var_info *next;
|
||||
uint32_t var;
|
||||
} op_var_info;
|
||||
|
||||
static zend_always_inline uint32_t liveliness_kill_var(zend_op_array *op_array, zend_op *cur_op, uint32_t var, uint32_t *Tstart, op_var_info **opTs)
|
||||
{
|
||||
uint32_t start = Tstart[var];
|
||||
uint32_t end = cur_op - op_array->opcodes;
|
||||
uint32_t count = 0;
|
||||
uint32_t var_offset, j;
|
||||
|
||||
Tstart[var] = -1;
|
||||
if (cur_op->opcode == ZEND_OP_DATA) {
|
||||
end--;
|
||||
}
|
||||
start++;
|
||||
if (op_array->opcodes[start].opcode == ZEND_OP_DATA
|
||||
|| op_array->opcodes[start].opcode == ZEND_FE_FETCH_R
|
||||
|| op_array->opcodes[start].opcode == ZEND_FE_FETCH_RW) {
|
||||
start++;
|
||||
}
|
||||
if (start < end) {
|
||||
op_var_info *new_opTs;
|
||||
|
||||
var_offset = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var);
|
||||
if (op_array->opcodes[end].opcode == ZEND_ROPE_END) {
|
||||
var_offset |= ZEND_LIVE_ROPE;
|
||||
} else if (op_array->opcodes[end].opcode == ZEND_END_SILENCE) {
|
||||
var_offset |= ZEND_LIVE_SILENCE;
|
||||
} else if (op_array->opcodes[end].opcode == ZEND_FE_FREE) {
|
||||
var_offset |= ZEND_LIVE_LOOP;
|
||||
}
|
||||
|
||||
if (opTs[start]) {
|
||||
if (start > 0 && opTs[start-1] == opTs[start]) {
|
||||
op_var_info *opT = opTs[start];
|
||||
do {
|
||||
count++;
|
||||
opT = opT->next;
|
||||
} while (opT);
|
||||
count += 2;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
count += 2;
|
||||
}
|
||||
|
||||
new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
|
||||
new_opTs->next = opTs[start];
|
||||
new_opTs->var = var_offset;
|
||||
opTs[start] = new_opTs;
|
||||
|
||||
for (j = start + 1; j < end; j++) {
|
||||
if (opTs[j-1]->next == opTs[j]) {
|
||||
opTs[j] = opTs[j-1];
|
||||
} else {
|
||||
if (opTs[j]) {
|
||||
count++;
|
||||
} else {
|
||||
count += 2;
|
||||
}
|
||||
new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
|
||||
new_opTs->next = opTs[j];
|
||||
new_opTs->var = var_offset;
|
||||
opTs[j] = new_opTs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static zend_always_inline uint32_t *generate_var_liveliness_info_ex(zend_op_array *op_array, zend_bool done_pass_two)
|
||||
{
|
||||
zend_op *opline, *end;
|
||||
uint32_t var, i, op_live_total = 0;
|
||||
uint32_t *info, info_off = op_array->last + 1;
|
||||
void *checkpoint = zend_arena_checkpoint(CG(arena));
|
||||
uint32_t *Tstart = zend_arena_alloc(&CG(arena), sizeof(uint32_t) * op_array->T);
|
||||
op_var_info **opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info *) * op_array->last);
|
||||
|
||||
memset(Tstart, -1, sizeof(uint32_t) * op_array->T);
|
||||
memset(opTs, 0, sizeof(op_var_info *) * op_array->last);
|
||||
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
do {
|
||||
if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
|
||||
&& !((opline)->result_type & EXT_TYPE_UNUSED)
|
||||
/* the following opcodes are used in inline branching
|
||||
* (and anyway always bool, so no need to free) and may
|
||||
* not be defined depending on the taken branch */
|
||||
&& opline->opcode != ZEND_BOOL
|
||||
&& opline->opcode != ZEND_JMPZ_EX
|
||||
&& opline->opcode != ZEND_JMPNZ_EX
|
||||
/* these two consecutive ops appear on ternary,
|
||||
* the result of true branch is undefined for false branch */
|
||||
&& (opline->opcode != ZEND_QM_ASSIGN || (opline + 1)->opcode != ZEND_JMP)
|
||||
/* exception for opcache, it might nowhere use the temporary
|
||||
* (anyway bool, so no need to free) */
|
||||
&& opline->opcode != ZEND_CASE
|
||||
/* the following opcodes reuse TMP created before */
|
||||
&& opline->opcode != ZEND_ROPE_ADD
|
||||
&& opline->opcode != ZEND_ADD_ARRAY_ELEMENT
|
||||
/* passes fast_call */
|
||||
&& opline->opcode != ZEND_FAST_CALL
|
||||
/* the following opcodes pass class_entry */
|
||||
&& opline->opcode != ZEND_FETCH_CLASS
|
||||
&& opline->opcode != ZEND_DECLARE_CLASS
|
||||
&& opline->opcode != ZEND_DECLARE_INHERITED_CLASS
|
||||
&& opline->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED
|
||||
&& opline->opcode != ZEND_DECLARE_ANON_CLASS
|
||||
&& opline->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) {
|
||||
if (done_pass_two) {
|
||||
var = EX_VAR_TO_NUM(opline->result.var) - op_array->last_var;
|
||||
} else {
|
||||
var = opline->result.var;
|
||||
}
|
||||
/* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */
|
||||
if (opline->opcode == ZEND_NEW) {
|
||||
Tstart[var] = opline->op2.opline_num - 1;
|
||||
} else {
|
||||
Tstart[var] = opline - op_array->opcodes;
|
||||
}
|
||||
}
|
||||
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
|
||||
if (done_pass_two) {
|
||||
var = EX_VAR_TO_NUM(opline->op1.var) - op_array->last_var;
|
||||
} else {
|
||||
var = opline->op1.var;
|
||||
}
|
||||
if (Tstart[var] != (uint32_t)-1
|
||||
/* the following opcodes don't free TMP */
|
||||
&& opline->opcode != ZEND_ROPE_ADD
|
||||
&& opline->opcode != ZEND_FETCH_LIST
|
||||
&& opline->opcode != ZEND_CASE
|
||||
&& opline->opcode != ZEND_FE_FETCH_R
|
||||
&& opline->opcode != ZEND_FE_FETCH_RW) {
|
||||
op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
|
||||
}
|
||||
}
|
||||
if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
|
||||
if (done_pass_two) {
|
||||
var = EX_VAR_TO_NUM(opline->op2.var) - op_array->last_var;
|
||||
} else {
|
||||
var = opline->op2.var;
|
||||
}
|
||||
if (Tstart[var] != (uint32_t)-1) {
|
||||
op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
|
||||
}
|
||||
}
|
||||
} while (++opline != end);
|
||||
|
||||
#if ZEND_DEBUG
|
||||
/* Check that all TMP variable live-ranges are closed */
|
||||
for (i = 0; i < op_array->T; i++) {
|
||||
ZEND_ASSERT(Tstart[i] == (uint32_t)-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!op_live_total) {
|
||||
info = NULL;
|
||||
} else {
|
||||
info = emalloc((op_array->last + 1 + op_live_total) * sizeof(uint32_t));
|
||||
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
if (!opTs[i]) {
|
||||
info[i] = (uint32_t)-1;
|
||||
} else if (i > 0 && opTs[i-1] == opTs[i]) {
|
||||
info[i] = info[i-1];
|
||||
} else {
|
||||
op_var_info *opT = opTs[i];
|
||||
info[i] = info_off;
|
||||
while (opT) {
|
||||
info[info_off++] = opT->var;
|
||||
opT = opT->next;
|
||||
}
|
||||
info[info_off++] = (uint32_t)-1;
|
||||
}
|
||||
}
|
||||
info[op_array->last] = info_off;
|
||||
ZEND_ASSERT(info_off == op_array->last + 1 + op_live_total);
|
||||
}
|
||||
|
||||
zend_arena_release(&CG(arena), checkpoint);
|
||||
return info;
|
||||
}
|
||||
|
||||
ZEND_API void zend_generate_var_liveliness_info(zend_op_array *op_array)
|
||||
{
|
||||
op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 0);
|
||||
}
|
||||
|
||||
ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array)
|
||||
{
|
||||
if (op_array->T_liveliness) {
|
||||
efree(op_array->T_liveliness);
|
||||
}
|
||||
op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 1);
|
||||
}
|
||||
|
||||
int print_class(zend_class_entry *class_entry)
|
||||
{
|
||||
printf("Class %s:\n", ZSTR_VAL(class_entry->name));
|
||||
|
||||
@@ -4866,6 +4866,31 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_brk_cont_element *el;
|
||||
|
||||
SAVE_OPLINE();
|
||||
el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
|
||||
&EX(func)->op_array, execute_data);
|
||||
|
||||
if (el->start >= 0) {
|
||||
zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
|
||||
|
||||
if (brk_opline->opcode == ZEND_FREE) {
|
||||
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
|
||||
} else if (brk_opline->opcode == ZEND_FE_FREE) {
|
||||
zval *var = EX_VAR(brk_opline->op1.var);
|
||||
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(var));
|
||||
}
|
||||
zval_ptr_dtor_nogc(var);
|
||||
}
|
||||
}
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
+30
-5
@@ -2225,6 +2225,31 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_brk_cont_element *el;
|
||||
|
||||
SAVE_OPLINE();
|
||||
el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
|
||||
&EX(func)->op_array, execute_data);
|
||||
|
||||
if (el->start >= 0) {
|
||||
zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
|
||||
|
||||
if (brk_opline->opcode == ZEND_FREE) {
|
||||
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
|
||||
} else if (brk_opline->opcode == ZEND_FE_FREE) {
|
||||
zval *var = EX_VAR(brk_opline->op1.var);
|
||||
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
|
||||
zend_hash_iterator_del(Z_FE_ITER_P(var));
|
||||
}
|
||||
zval_ptr_dtor_nogc(var);
|
||||
}
|
||||
}
|
||||
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
|
||||
}
|
||||
|
||||
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
@@ -48397,27 +48422,27 @@ void zend_init_opcodes_handlers(void)
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_GOTO_SPEC_CONST_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_GOTO_SPEC_CONST_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_GOTO_SPEC_CONST_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_GOTO_SPEC_CONST_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_GOTO_SPEC_CONST_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
|
||||
@@ -122,7 +122,7 @@ const char *zend_vm_opcodes_map[173] = {
|
||||
"ZEND_FETCH_OBJ_UNSET",
|
||||
"ZEND_FETCH_LIST",
|
||||
"ZEND_FETCH_CONSTANT",
|
||||
NULL,
|
||||
"ZEND_GOTO",
|
||||
"ZEND_EXT_STMT",
|
||||
"ZEND_EXT_FCALL_BEGIN",
|
||||
"ZEND_EXT_FCALL_END",
|
||||
|
||||
@@ -130,6 +130,7 @@ END_EXTERN_C()
|
||||
#define ZEND_FETCH_OBJ_UNSET 97
|
||||
#define ZEND_FETCH_LIST 98
|
||||
#define ZEND_FETCH_CONSTANT 99
|
||||
#define ZEND_GOTO 100
|
||||
#define ZEND_EXT_STMT 101
|
||||
#define ZEND_EXT_FCALL_BEGIN 102
|
||||
#define ZEND_EXT_FCALL_END 103
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ int zend_sprintf(char *buffer, const char *format, ...);
|
||||
PHP_MAJOR_VERSION=7
|
||||
PHP_MINOR_VERSION=0
|
||||
PHP_RELEASE_VERSION=0
|
||||
PHP_EXTRA_VERSION="-dev"
|
||||
PHP_EXTRA_VERSION="beta1"
|
||||
PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION"
|
||||
PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION`
|
||||
|
||||
|
||||
@@ -123,6 +123,10 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
|
||||
blocks[0].start_opline_no = 0;
|
||||
while (opline < end) {
|
||||
switch((unsigned)opline->opcode) {
|
||||
case ZEND_GOTO:
|
||||
/* would not optimize GOTOs - we cannot really know where it jumps,
|
||||
* so these optimizations are too dangerous */
|
||||
return 0;
|
||||
case ZEND_FAST_CALL:
|
||||
START_BLOCK_OP(ZEND_OP1(opline).opline_num);
|
||||
if (opline->extended_value) {
|
||||
@@ -193,6 +197,65 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
|
||||
blocks[op_array->try_catch_array[i].try_op].protected = 1;
|
||||
}
|
||||
}
|
||||
/* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
|
||||
* but, we have to keep brk_cont_array to avoid memory leaks during
|
||||
* exception handling */
|
||||
if (op_array->last_brk_cont) {
|
||||
int i, j;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i< op_array->last_brk_cont; i++) {
|
||||
if (op_array->brk_cont_array[i].start >= 0 &&
|
||||
(op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
|
||||
int parent = op_array->brk_cont_array[i].parent;
|
||||
|
||||
while (parent >= 0 &&
|
||||
op_array->brk_cont_array[parent].start < 0 &&
|
||||
(op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
|
||||
op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
|
||||
parent = op_array->brk_cont_array[parent].parent;
|
||||
}
|
||||
op_array->brk_cont_array[i].parent = parent;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (j) {
|
||||
cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
|
||||
cfg->loop_cont = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
|
||||
cfg->loop_brk = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
|
||||
j = 0;
|
||||
for (i = 0; i< op_array->last_brk_cont; i++) {
|
||||
if (op_array->brk_cont_array[i].start >= 0 &&
|
||||
(op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
|
||||
op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
|
||||
if (i != j) {
|
||||
op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
|
||||
}
|
||||
cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
|
||||
cfg->loop_cont[j] = &blocks[op_array->brk_cont_array[j].cont];
|
||||
cfg->loop_brk[j] = &blocks[op_array->brk_cont_array[j].brk];
|
||||
START_BLOCK_OP(op_array->brk_cont_array[j].start);
|
||||
START_BLOCK_OP(op_array->brk_cont_array[j].cont);
|
||||
START_BLOCK_OP(op_array->brk_cont_array[j].brk);
|
||||
blocks[op_array->brk_cont_array[j].start].protected = 1;
|
||||
blocks[op_array->brk_cont_array[j].brk].protected = 1;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
op_array->last_brk_cont = j;
|
||||
} else {
|
||||
efree(op_array->brk_cont_array);
|
||||
op_array->brk_cont_array = NULL;
|
||||
op_array->last_brk_cont = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build CFG (Control Flow Graph) */
|
||||
cur_block = blocks;
|
||||
@@ -460,6 +523,16 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
|
||||
/* Walk thorough all paths */
|
||||
zend_access_path(start, ctx);
|
||||
|
||||
/* Add brk/cont paths */
|
||||
if (op_array->last_brk_cont) {
|
||||
int i;
|
||||
for (i=0; i< op_array->last_brk_cont; i++) {
|
||||
zend_access_path(cfg->loop_start[i], ctx);
|
||||
zend_access_path(cfg->loop_cont[i], ctx);
|
||||
zend_access_path(cfg->loop_brk[i], ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add exception paths */
|
||||
if (op_array->last_try_catch) {
|
||||
int i;
|
||||
@@ -1123,6 +1196,16 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
|
||||
op_array->last_try_catch = j;
|
||||
}
|
||||
|
||||
/* adjust loop jump targets */
|
||||
if (op_array->last_brk_cont) {
|
||||
int i;
|
||||
for (i = 0; i< op_array->last_brk_cont; i++) {
|
||||
op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
|
||||
op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
|
||||
op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust jump targets */
|
||||
for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
|
||||
if (!cur_block->access) {
|
||||
|
||||
@@ -44,6 +44,14 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
|
||||
end = op_array->opcodes + op_array->last;
|
||||
for (opline = op_array->opcodes; opline < end; opline++) {
|
||||
|
||||
/* GOTO target is unresolved yet. We can't optimize. */
|
||||
if (opline->opcode == ZEND_GOTO &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
|
||||
/* TODO: in general we can avoid this restriction */
|
||||
FREE_ALLOCA(shiftlist);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Kill JMP-over-NOP-s */
|
||||
if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
|
||||
/* check if there are only NOPs under the branch */
|
||||
@@ -77,6 +85,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
|
||||
for (opline = op_array->opcodes; opline<end; opline++) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@@ -108,6 +117,13 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
|
||||
}
|
||||
}
|
||||
|
||||
/* update brk/cont array */
|
||||
for (j = 0; j < op_array->last_brk_cont; j++) {
|
||||
op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk];
|
||||
op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont];
|
||||
op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start];
|
||||
}
|
||||
|
||||
/* 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];
|
||||
|
||||
@@ -624,6 +624,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_JMP:
|
||||
|
||||
@@ -340,11 +340,29 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||
*/
|
||||
case ZEND_FREE:
|
||||
case ZEND_CASE: {
|
||||
zend_op *m = opline;
|
||||
zend_op *end = op_array->opcodes + op_array->last;
|
||||
zend_op *m, *n;
|
||||
int brk = op_array->last_brk_cont;
|
||||
zend_bool in_switch = 0;
|
||||
while (brk--) {
|
||||
if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
|
||||
op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
|
||||
in_switch = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (m < end) {
|
||||
if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) {
|
||||
if (!in_switch) {
|
||||
ZEND_ASSERT(opline->opcode == ZEND_FREE);
|
||||
MAKE_NOP(opline);
|
||||
zval_dtor(val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
m = opline;
|
||||
n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
|
||||
while (m < n) {
|
||||
if (ZEND_OP1_TYPE(m) == type &&
|
||||
ZEND_OP1(m).var == var) {
|
||||
if (m->opcode == ZEND_CASE) {
|
||||
zval old_val;
|
||||
ZVAL_COPY_VALUE(&old_val, val);
|
||||
@@ -353,7 +371,6 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||
ZVAL_COPY_VALUE(val, &old_val);
|
||||
} else if (m->opcode == ZEND_FREE) {
|
||||
MAKE_NOP(m);
|
||||
break;
|
||||
} else {
|
||||
ZEND_ASSERT(0);
|
||||
}
|
||||
@@ -458,17 +475,6 @@ static void zend_optimize(zend_op_array *op_array,
|
||||
if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
|
||||
zend_optimizer_compact_literals(op_array, ctx);
|
||||
}
|
||||
|
||||
if ((ZEND_OPTIMIZER_PASS_1
|
||||
|ZEND_OPTIMIZER_PASS_2
|
||||
|ZEND_OPTIMIZER_PASS_3
|
||||
|ZEND_OPTIMIZER_PASS_4
|
||||
|ZEND_OPTIMIZER_PASS_5
|
||||
|ZEND_OPTIMIZER_PASS_9
|
||||
|ZEND_OPTIMIZER_PASS_10
|
||||
|ZEND_OPTIMIZER_PASS_11) & OPTIMIZATION_LEVEL) {
|
||||
zend_regenerate_var_liveliness_info(op_array);
|
||||
}
|
||||
}
|
||||
|
||||
static void zend_accel_optimize(zend_op_array *op_array,
|
||||
@@ -488,6 +494,7 @@ static void zend_accel_optimize(zend_op_array *op_array,
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@@ -532,6 +539,7 @@ static void zend_accel_optimize(zend_op_array *op_array,
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
|
||||
@@ -73,6 +73,9 @@ typedef struct _zend_cfg {
|
||||
zend_code_block *blocks;
|
||||
zend_code_block **try;
|
||||
zend_code_block **catch;
|
||||
zend_code_block **loop_start;
|
||||
zend_code_block **loop_cont;
|
||||
zend_code_block **loop_brk;
|
||||
zend_op **Tsource;
|
||||
char *same_t;
|
||||
} zend_cfg;
|
||||
|
||||
@@ -386,6 +386,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
|
||||
# if ZEND_USE_ABS_JMP_ADDR
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@@ -458,11 +459,11 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
|
||||
|
||||
SERIALIZE_STR(op_array->function_name);
|
||||
SERIALIZE_STR(op_array->filename);
|
||||
SERIALIZE_PTR(op_array->brk_cont_array);
|
||||
SERIALIZE_PTR(op_array->scope);
|
||||
SERIALIZE_STR(op_array->doc_comment);
|
||||
SERIALIZE_PTR(op_array->try_catch_array);
|
||||
SERIALIZE_PTR(op_array->prototype);
|
||||
SERIALIZE_PTR(op_array->T_liveliness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,6 +913,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
|
||||
# if ZEND_USE_ABS_JMP_ADDR
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@@ -980,11 +982,11 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
|
||||
|
||||
UNSERIALIZE_STR(op_array->function_name);
|
||||
UNSERIALIZE_STR(op_array->filename);
|
||||
UNSERIALIZE_PTR(op_array->brk_cont_array);
|
||||
UNSERIALIZE_PTR(op_array->scope);
|
||||
UNSERIALIZE_STR(op_array->doc_comment);
|
||||
UNSERIALIZE_PTR(op_array->try_catch_array);
|
||||
UNSERIALIZE_PTR(op_array->prototype);
|
||||
UNSERIALIZE_PTR(op_array->T_liveliness);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -501,6 +501,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
|
||||
/* fix jumps to point to new array */
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@@ -589,6 +590,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
|
||||
op_array->arg_info = arg_info;
|
||||
}
|
||||
|
||||
if (op_array->brk_cont_array) {
|
||||
zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
|
||||
}
|
||||
|
||||
if (op_array->scope) {
|
||||
op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
|
||||
}
|
||||
@@ -613,10 +618,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
|
||||
zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
|
||||
}
|
||||
|
||||
if (op_array->T_liveliness) {
|
||||
zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
|
||||
}
|
||||
|
||||
if (op_array->vars) {
|
||||
if (already_stored) {
|
||||
persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars);
|
||||
|
||||
@@ -229,6 +229,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
|
||||
}
|
||||
}
|
||||
|
||||
if (op_array->brk_cont_array) {
|
||||
ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
|
||||
}
|
||||
|
||||
if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
|
||||
ADD_STRING(op_array->doc_comment);
|
||||
}
|
||||
@@ -237,10 +241,6 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
|
||||
ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
|
||||
}
|
||||
|
||||
if (op_array->T_liveliness) {
|
||||
ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
|
||||
}
|
||||
|
||||
if (op_array->vars) {
|
||||
int i;
|
||||
|
||||
|
||||
+2
-2
@@ -3,6 +3,6 @@
|
||||
#define PHP_MAJOR_VERSION 7
|
||||
#define PHP_MINOR_VERSION 0
|
||||
#define PHP_RELEASE_VERSION 0
|
||||
#define PHP_EXTRA_VERSION "-dev"
|
||||
#define PHP_VERSION "7.0.0-dev"
|
||||
#define PHP_EXTRA_VERSION "beta1"
|
||||
#define PHP_VERSION "7.0.0beta1"
|
||||
#define PHP_VERSION_ID 70000
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "phpdbg_opcode.h"
|
||||
#include "phpdbg_utils.h"
|
||||
#include "ext/standard/php_string.h"
|
||||
#include "zend_smart_str.h"
|
||||
|
||||
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
||||
|
||||
@@ -57,6 +56,7 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
|
||||
/* OP1 */
|
||||
switch (op->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
|
||||
break;
|
||||
@@ -114,44 +114,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (ops->T_liveliness) {
|
||||
uint32_t *var = ops->T_liveliness + (op - ops->opcodes);
|
||||
|
||||
if (*var != (uint32_t)-1) {
|
||||
smart_str str = {0};
|
||||
|
||||
var = ops->T_liveliness + (*var);
|
||||
smart_str_appends(&str, "; [@");
|
||||
smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
|
||||
while (*(++var) != (uint32_t)-1) {
|
||||
smart_str_appends(&str, ", @");
|
||||
smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
|
||||
}
|
||||
smart_str_appendc(&str, ']');
|
||||
smart_str_0(&str);
|
||||
|
||||
asprintf(&decode[0],
|
||||
"%-20s %-20s %-20s%-20s",
|
||||
decode[1] ? decode[1] : "",
|
||||
decode[2] ? decode[2] : "",
|
||||
decode[3] ? decode[3] : "",
|
||||
ZSTR_VAL(str.s));
|
||||
|
||||
smart_str_free(&str);
|
||||
|
||||
if (decode[1])
|
||||
free(decode[1]);
|
||||
if (decode[2])
|
||||
free(decode[2]);
|
||||
if (decode[3])
|
||||
free(decode[3]);
|
||||
|
||||
return decode[0];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
asprintf(&decode[0],
|
||||
"%-20s %-20s %-20s",
|
||||
decode[1] ? decode[1] : "",
|
||||
|
||||
Reference in new issue
Block a user