summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c1390
1 files changed, 1002 insertions, 388 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 593c19e015..1d544c6561 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -60,7 +60,7 @@ typedef struct _zend_loop_var {
uint32_t var_num;
union {
uint32_t try_catch_offset;
- uint32_t brk_cont_offset;
+ uint32_t live_range_offset;
} u;
} zend_loop_var;
@@ -86,6 +86,8 @@ ZEND_API zend_compiler_globals compiler_globals;
ZEND_API zend_executor_globals executor_globals;
#endif
+static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2);
+
static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
{
zend_property_info *property_info = Z_PTR_P(zv);
@@ -95,6 +97,12 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
}
/* }}} */
+static void zend_destroy_class_constant_internal(zval *zv) /* {{{ */
+{
+ free(Z_PTR_P(zv));
+}
+/* }}} */
+
static zend_string *zend_new_interned_string_safe(zend_string *str) /* {{{ */ {
zend_string *interned_str;
@@ -151,6 +159,7 @@ static const struct reserved_class_name reserved_class_names[] = {
{ZEND_STRL("static")},
{ZEND_STRL("string")},
{ZEND_STRL("true")},
+ {ZEND_STRL("void")},
{NULL, 0}
};
@@ -194,6 +203,7 @@ static const builtin_type_info builtin_types[] = {
{ZEND_STRL("float"), IS_DOUBLE},
{ZEND_STRL("string"), IS_STRING},
{ZEND_STRL("bool"), _IS_BOOL},
+ {ZEND_STRL("void"), IS_VOID},
{NULL, 0, IS_UNDEF}
};
@@ -221,16 +231,23 @@ 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).try_catch_offset = -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);
+ CG(context).brk_cont_array = NULL;
+ }
if (CG(context).labels) {
zend_hash_destroy(CG(context).labels);
FREE_HASHTABLE(CG(context).labels);
@@ -570,22 +587,111 @@ void zend_stop_lexing(void)
LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
}
+static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /* {{{ */
+{
+ zend_live_range *range;
+
+ op_array->last_live_range++;
+ op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
+ range = op_array->live_range + op_array->last_live_range - 1;
+ range->start = start;
+ return op_array->last_live_range - 1;
+}
+/* }}} */
+
+static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start) /* {{{ */
+{
+ if (op_array->last_live_range == 0 ||
+ op_array->live_range[op_array->last_live_range - 1].start <= start) {
+ return zend_start_live_range(op_array, start);
+ } else {
+ /* Live ranges have to be sorted by "start" field */
+ uint32_t n = op_array->last_live_range;
+
+ /* move early ranges to make a room */
+ op_array->last_live_range = n + 1;
+ op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
+ do {
+ op_array->live_range[n] = op_array->live_range[n-1];
+ n--;
+ } while (n != 0 && op_array->live_range[n-1].start > start);
+
+ /* initialize new range */
+ op_array->live_range[n].start = start;
+
+ /* update referens to live-ranges from stack */
+ if (!zend_stack_is_empty(&CG(loop_var_stack))) {
+ zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+ zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
+ int check_opcodes = 0;
+
+ for (; loop_var >= base; loop_var--) {
+ if (loop_var->opcode == ZEND_RETURN) {
+ /* Stack separator */
+ break;
+ } else if (loop_var->opcode == ZEND_FREE ||
+ loop_var->opcode == ZEND_FE_FREE) {
+ if (loop_var->u.live_range_offset >= n) {
+ loop_var->u.live_range_offset++;
+ check_opcodes = 1;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* update previously generated FREE/FE_FREE opcodes */
+ if (check_opcodes) {
+ zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start;
+ zend_op *end = op_array->opcodes + op_array->last;
+
+ while (opline < end) {
+ if ((opline->opcode == ZEND_FREE ||
+ opline->opcode == ZEND_FE_FREE) &&
+ (opline->extended_value & ZEND_FREE_ON_RETURN) &&
+ opline->op2.num >= n) {
+ opline->op2.num++;
+ }
+ opline++;
+ }
+ }
+ }
+ return n;
+ }
+}
+/* }}} */
+
+static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */
+{
+ zend_live_range *range = op_array->live_range + offset;
+
+ if (range->start == end && offset == op_array->last_live_range - 1) {
+ op_array->last_live_range--;
+ } else {
+ range->end = end;
+ range->var = (var * sizeof(zval)) | kind;
+ }
+}
+/* }}} */
+
static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */
{
zend_brk_cont_element *brk_cont_element;
int parent = CG(context).current_brk_cont;
zend_loop_var info = {0};
- CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
- brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
+ CG(context).current_brk_cont = CG(context).last_brk_cont;
+ brk_cont_element = get_next_brk_cont_element();
brk_cont_element->parent = parent;
if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
+ uint32_t start = get_next_op_number(CG(active_op_array));
+
info.opcode = free_opcode;
info.var_type = loop_var->op_type;
info.var_num = loop_var->u.op.var;
- info.u.brk_cont_offset = CG(context).current_brk_cont;
- brk_cont_element->start = get_next_op_number(CG(active_op_array));
+ info.u.live_range_offset = zend_start_live_range(CG(active_op_array), start);
+ brk_cont_element->start = start;
} else {
info.opcode = ZEND_NOP;
/* The start field is used to free temporary variables in case of exceptions.
@@ -597,14 +703,22 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var
}
/* }}} */
-static inline void zend_end_loop(int cont_addr) /* {{{ */
+static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */
{
+ uint32_t end = get_next_op_number(CG(active_op_array));
zend_brk_cont_element *brk_cont_element
- = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
+ = &CG(context).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));
+ brk_cont_element->brk = end;
CG(context).current_brk_cont = brk_cont_element->parent;
+ if (brk_cont_element->start != -1) {
+ zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+ zend_end_live_range(CG(active_op_array), loop_var->u.live_range_offset, end,
+ loop_var->opcode == ZEND_FE_FREE ? ZEND_LIVE_LOOP : ZEND_LIVE_TMPVAR,
+ var_node->u.op.var);
+ }
+
zend_stack_del_top(&CG(loop_var_stack));
}
/* }}} */
@@ -624,11 +738,7 @@ void zend_do_free(znode *op1) /* {{{ */
}
}
- opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
} else if (op1->op_type == IS_VAR) {
zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
while (opline->opcode == ZEND_END_SILENCE ||
@@ -640,38 +750,30 @@ void zend_do_free(znode *op1) /* {{{ */
&& opline->result.var == op1->u.op.var) {
if (opline->opcode == ZEND_FETCH_R ||
opline->opcode == ZEND_FETCH_DIM_R ||
- opline->opcode == ZEND_FETCH_OBJ_R) {
+ opline->opcode == ZEND_FETCH_OBJ_R ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_R) {
/* It's very rare and useless case. It's better to use
additional FREE opcode and simplify the FETCH handlers
their selves */
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
+ } else if (opline->opcode == ZEND_FETCH_THIS) {
+ opline->opcode = ZEND_NOP;
+ opline->result_type = IS_UNUSED;
} else {
- opline->result_type |= EXT_TYPE_UNUSED;
+ opline->result_type = IS_UNUSED;
}
} else {
while (opline >= CG(active_op_array)->opcodes) {
if (opline->opcode == ZEND_FETCH_LIST &&
opline->op1_type == IS_VAR &&
opline->op1.var == op1->u.op.var) {
- opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
return;
}
if (opline->result_type == IS_VAR
&& opline->result.var == op1->u.op.var) {
if (opline->opcode == ZEND_NEW) {
- opline->result_type |= EXT_TYPE_UNUSED;
- opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
- while (opline->opcode != ZEND_DO_FCALL || opline->op1.num != ZEND_CALL_CTOR) {
- opline--;
- }
- opline->op1.num |= ZEND_CALL_CTOR_RESULT_UNUSED;
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
}
break;
}
@@ -952,24 +1054,24 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
{
zend_function *function, *new_function;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- function = zend_hash_find_ptr(function_table, Z_STR_P(op1));
+ function = zend_hash_find_ptr(function_table, Z_STR_P(rtd_key));
new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
memcpy(new_function, function, sizeof(zend_op_array));
- if (zend_hash_add_ptr(function_table, Z_STR_P(op2), new_function) == NULL) {
+ if (zend_hash_add_ptr(function_table, Z_STR_P(lcname), new_function) == NULL) {
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
zend_function *old_function;
- if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(op2))) != NULL
+ if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(lcname))) != NULL
&& old_function->type == ZEND_USER_FUNCTION
&& old_function->op_array.last > 0) {
zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
@@ -993,21 +1095,21 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opli
ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time) /* {{{ */
{
zend_class_entry *ce;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(op1))) == NULL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1));
+ if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key))) == NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(rtd_key));
return NULL;
}
ce->refcount++;
- if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
+ if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
ce->refcount--;
if (!compile_time) {
/* If we're in compile time, in practice, it's quite possible
@@ -1030,17 +1132,17 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze
ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time) /* {{{ */
{
zend_class_entry *ce;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- ce = zend_hash_find_ptr(class_table, Z_STR_P(op1));
+ ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key));
if (!ce) {
if (!compile_time) {
@@ -1049,12 +1151,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
* so we shut up about it. This allows the if (!defined('FOO')) { return; }
* approach to work.
*/
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(op2)), Z_STRVAL_P(op2));
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)), Z_STRVAL_P(lcname));
}
return NULL;
}
- if (zend_hash_exists(class_table, Z_STR_P(op2))) {
+ if (zend_hash_exists(class_table, Z_STR_P(lcname))) {
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));
}
@@ -1063,7 +1165,7 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
ce->refcount++;
/* Register the derived class */
- if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
+ if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == 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 ce;
@@ -1137,9 +1239,9 @@ void zend_do_early_binding(void) /* {{{ */
return;
}
- zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)));
+ zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)+1));
+ zend_del_literal(CG(active_op_array), opline->op1.constant+1);
zend_del_literal(CG(active_op_array), opline->op1.constant);
- zend_del_literal(CG(active_op_array), opline->op2.constant);
MAKE_NOP(opline);
}
/* }}} */
@@ -1150,19 +1252,19 @@ static void zend_mark_function_as_generator() /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR,
"The \"yield\" expression can only be used inside a function");
}
+
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted";
- if (!CG(active_op_array)->arg_info[-1].class_name) {
- zend_error_noreturn(E_COMPILE_ERROR, msg,
- zend_get_type_by_const(CG(active_op_array)->arg_info[-1].type_hint));
+ zend_arg_info return_info = CG(active_op_array)->arg_info[-1];
+
+ if (!return_info.class_name) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint));
}
- if (!(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Traversable")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Traversable")-1, "Traversable", sizeof("Traversable")-1) == 0) &&
- !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Iterator")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Iterator")-1, "Iterator", sizeof("Iterator")-1) == 0) &&
- !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Generator")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Generator")-1, "Generator", sizeof("Generator")-1) == 0)) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name));
+
+ if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable")
+ && !zend_string_equals_literal_ci(return_info.class_name, "Iterator")
+ && !zend_string_equals_literal_ci(return_info.class_name, "Generator")) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name));
}
}
@@ -1416,14 +1518,15 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
static zend_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;
zval *c;
if (class_name_refers_to_active_ce(class_name, fetch_type)) {
- c = zend_hash_find(&CG(active_class_entry)->constants_table, name);
+ cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name);
} else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), ZSTR_VAL(class_name), ZSTR_LEN(class_name));
if (ce) {
- c = zend_hash_find(&ce->constants_table, name);
+ cc = zend_hash_find_ptr(&ce->constants_table, name);
} else {
return 0;
}
@@ -1435,8 +1538,14 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name,
return 0;
}
+ if (!cc || !zend_verify_const_access(cc, CG(active_class_entry))) {
+ return 0;
+ }
+
+ c = &cc->value;
+
/* Substitute case-sensitive (or lowercase) persistent class constants */
- if (c && Z_TYPE_P(c) < IS_OBJECT) {
+ if (Z_TYPE_P(c) < IS_OBJECT) {
ZVAL_DUP(zv, c);
return 1;
}
@@ -1617,7 +1726,6 @@ again:
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */
{
zend_bool persistent_hashes = (ce->type == ZEND_INTERNAL_CLASS) ? 1 : 0;
- dtor_func_t zval_ptr_dtor_func = ((persistent_hashes) ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR);
ce->refcount = 1;
ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED;
@@ -1629,7 +1737,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
ce->default_properties_table = NULL;
ce->default_static_members_table = NULL;
zend_hash_init_ex(&ce->properties_info, 8, NULL, (persistent_hashes ? zend_destroy_property_info_internal : NULL), persistent_hashes, 0);
- zend_hash_init_ex(&ce->constants_table, 8, NULL, zval_ptr_dtor_func, persistent_hashes, 0);
+ zend_hash_init_ex(&ce->constants_table, 8, NULL, (persistent_hashes ? zend_destroy_class_constant_internal : NULL), persistent_hashes, 0);
zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0);
if (ce->type == ZEND_INTERNAL_CLASS) {
@@ -1821,25 +1929,30 @@ ZEND_API size_t zend_dirname(char *path, size_t len)
static void zend_adjust_for_fetch_type(zend_op *opline, uint32_t type) /* {{{ */
{
+ zend_uchar factor = (opline->opcode == ZEND_FETCH_STATIC_PROP_R) ? 1 : 3;
+
+ if (opline->opcode == ZEND_FETCH_THIS) {
+ return;
+ }
+
switch (type & BP_VAR_MASK) {
case BP_VAR_R:
return;
case BP_VAR_W:
- case BP_VAR_REF:
- opline->opcode += 3;
+ opline->opcode += 1 * factor;
return;
case BP_VAR_RW:
- opline->opcode += 6;
+ opline->opcode += 2 * factor;
return;
case BP_VAR_IS:
- opline->opcode += 9;
+ opline->opcode += 3 * factor;
return;
case BP_VAR_FUNC_ARG:
- opline->opcode += 12;
+ opline->opcode += 4 * factor;
opline->extended_value |= type >> BP_VAR_SHIFT;
return;
case BP_VAR_UNSET:
- opline->opcode += 15;
+ opline->opcode += 5 * factor;
return;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -1862,6 +1975,136 @@ static inline void zend_make_tmp_result(znode *result, zend_op *opline) /* {{{ *
}
/* }}} */
+static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
+{
+ zend_op *def = opline;
+
+ while (def != CG(active_op_array)->opcodes) {
+ def--;
+ if (def->result_type == type && def->result.var == var) {
+ if (def->opcode == ZEND_ADD_ARRAY_ELEMENT ||
+ def->opcode == ZEND_ROPE_ADD) {
+ /* not a real definition */
+ continue;
+ } else if (def->opcode == ZEND_JMPZ_EX ||
+ def->opcode == ZEND_JMPNZ_EX ||
+ def->opcode == ZEND_BOOL ||
+ def->opcode == ZEND_BOOL_NOT) {
+ /* result IS_BOOL, it does't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_DECLARE_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
+ def->opcode == ZEND_DECLARE_ANON_CLASS ||
+ def->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS) {
+ /* classes don't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_FAST_CALL) {
+ /* fast_calls don't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_NEW) {
+ /* Objects created via ZEND_NEW are only fully initialized
+ * after the DO_FCALL (constructor call) */
+ def = CG(active_op_array)->opcodes + def->op2.opline_num - 1;
+ if (def + 1 == opline) {
+ break;
+ }
+ }
+
+ zend_end_live_range(CG(active_op_array),
+ zend_start_live_range_ex(CG(active_op_array),
+ def + 1 - CG(active_op_array)->opcodes),
+ opline - CG(active_op_array)->opcodes,
+ ZEND_LIVE_TMPVAR, var);
+ break;
+ }
+ }
+}
+/* }}} */
+
+static zend_always_inline int zend_is_def_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
+{
+ while (1) {
+ if (opline->result_type == type && opline->result.var == var) {
+ return opline->opcode != ZEND_ADD_ARRAY_ELEMENT &&
+ opline->opcode != ZEND_ROPE_ADD;
+ } else if (opline->opcode == ZEND_OP_DATA) {
+ return (opline-1)->result_type == type &&
+ (opline-1)->result.var == var;
+ } else if (opline->opcode == ZEND_END_SILENCE ||
+ opline->opcode == ZEND_NOP ||
+ opline->opcode == ZEND_EXT_NOP ||
+ opline->opcode == ZEND_EXT_STMT ||
+ opline->opcode == ZEND_EXT_FCALL_BEGIN ||
+ opline->opcode == ZEND_EXT_FCALL_END ||
+ opline->opcode == ZEND_TICKS) {
+ opline--;
+ } else {
+ return 0;
+ }
+ }
+}
+/* }}} */
+
+static void zend_check_live_ranges(zend_op *opline) /* {{{ */
+{
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+ !zend_is_def_range(opline - 1, opline->op1_type, opline->op1.var)) {
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ if (!zend_is_def_range(opline - 2, opline->op1_type, opline->op1.var)) {
+ zend_find_live_range(opline - 1, opline->op1_type, opline->op1.var);
+ }
+ } else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
+ opline->opcode == ZEND_NEW ||
+ opline->opcode == ZEND_FETCH_CLASS_CONSTANT ||
+ opline->opcode == ZEND_ADD_INTERFACE ||
+ opline->opcode == ZEND_ADD_TRAIT ||
+ opline->opcode == ZEND_BIND_TRAITS ||
+ opline->opcode == ZEND_VERIFY_ABSTRACT_CLASS) {
+ /* classes don't have to be destroyed */
+ } else if (opline->opcode == ZEND_FAST_RET) {
+ /* fast_calls don't have to be destroyed */
+ } else if (opline->opcode == ZEND_CASE ||
+ opline->opcode == ZEND_FE_FETCH_R ||
+ opline->opcode == ZEND_FE_FETCH_RW ||
+ opline->opcode == ZEND_FE_FREE ||
+ opline->opcode == ZEND_ROPE_ADD ||
+ opline->opcode == ZEND_ROPE_END ||
+ opline->opcode == ZEND_END_SILENCE ||
+ opline->opcode == ZEND_FETCH_LIST ||
+ opline->opcode == ZEND_VERIFY_RETURN_TYPE ||
+ opline->opcode == ZEND_BIND_LEXICAL) {
+ /* these opcodes are handled separately */
+ } else {
+ zend_find_live_range(opline, opline->op1_type, opline->op1.var);
+ }
+ }
+
+ if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+ !zend_is_def_range(opline - 1, opline->op2_type, opline->op2.var)) {
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ if (!zend_is_def_range(opline - 2, opline->op2_type, opline->op2.var)) {
+ zend_find_live_range(opline-1, opline->op2_type, opline->op2.var);
+ }
+ } else if (opline->opcode == ZEND_FETCH_STATIC_PROP_R ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_W ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_RW ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_IS ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_FUNC_ARG ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_UNSET ||
+ opline->opcode == ZEND_UNSET_STATIC_PROP ||
+ opline->opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
+ opline->opcode == ZEND_INSTANCEOF) {
+ /* classes don't have to be destroyed */
+ } else {
+ zend_find_live_range(opline, opline->op2_type, opline->op2.var);
+ }
+ }
+}
+/* }}} */
+
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array));
@@ -1879,6 +2122,8 @@ static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_var_result(result, opline);
}
@@ -1903,6 +2148,8 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_tmp_result(result, opline);
}
@@ -2017,16 +2264,58 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
for (i = offset; i < count; ++i) {
opline = get_next_op(CG(active_op_array));
memcpy(opline, &oplines[i], sizeof(zend_op));
+ zend_check_live_ranges(opline);
}
CG(delayed_oplines_stack).top = offset;
return opline;
}
/* }}} */
-static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
+static void zend_emit_return_type_check(
+ znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
+ /* `return ...;` is illegal in a void function (but `return;` isn't) */
+ if (return_info->type_hint == IS_VOID) {
+ if (expr) {
+ if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A void function must not return a value "
+ "(did you mean \"return;\" instead of \"return null;\"?)");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ }
+ }
+ /* we don't need run-time check */
+ return;
+ }
+
if (return_info->type_hint != IS_UNDEF) {
- zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
+ zend_op *opline;
+
+ if (!expr && !implicit) {
+ if (return_info->allow_null) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A function with return type must return a value "
+ "(did you mean \"return null;\" instead of \"return;\"?)");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A function with return type must return a value");
+ }
+ }
+
+ if (expr && expr->op_type == IS_CONST) {
+ if ((return_info->type_hint == Z_TYPE(expr->u.constant))
+ ||((return_info->type_hint == _IS_BOOL)
+ && (Z_TYPE(expr->u.constant) == IS_FALSE
+ || Z_TYPE(expr->u.constant) == IS_TRUE))
+ || (return_info->allow_null
+ && Z_TYPE(expr->u.constant) == IS_NULL)) {
+ /* we don't need run-time check */
+ return;
+ }
+ }
+
+ opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
if (expr && expr->op_type == IS_CONST) {
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array));
@@ -2041,19 +2330,20 @@ static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info)
}
/* }}} */
-void zend_emit_final_return(zval *zv) /* {{{ */
+void zend_emit_final_return(int return_one) /* {{{ */
{
znode zn;
zend_op *ret;
zend_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) {
- zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1);
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE
+ && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) {
+ zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1, 1);
}
zn.op_type = IS_CONST;
- if (zv) {
- ZVAL_COPY_VALUE(&zn.u.constant, zv);
+ if (return_one) {
+ ZVAL_LONG(&zn.u.constant, 1);
} else {
ZVAL_NULL(&zn.u.constant);
}
@@ -2172,6 +2462,61 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th
}
/* }}} */
+static void zend_compile_class_ref_ex(znode *result, zend_ast *name_ast, uint32_t fetch_flags) /* {{{ */
+{
+ uint32_t fetch_type;
+
+ if (name_ast->kind != ZEND_AST_ZVAL) {
+ znode name_node;
+
+ zend_compile_expr(&name_node, name_ast);
+
+ if (name_node.op_type == IS_CONST) {
+ zend_string *name;
+
+ if (Z_TYPE(name_node.u.constant) != IS_STRING) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name");
+ }
+
+ name = Z_STR(name_node.u.constant);
+ fetch_type = zend_get_class_fetch_type(name);
+
+ if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name(name, ZEND_NAME_FQ));
+ } else {
+ zend_ensure_valid_class_fetch_type(fetch_type);
+ result->op_type = IS_UNUSED;
+ result->u.op.num = fetch_type | fetch_flags;
+ }
+
+ zend_string_release(name);
+ } else {
+ zend_op *opline = zend_emit_op(result, ZEND_FETCH_CLASS, NULL, &name_node);
+ opline->extended_value = ZEND_FETCH_CLASS_DEFAULT | fetch_flags;
+ }
+ return;
+ }
+
+ /* Fully qualified names are always default refs */
+ if (name_ast->attr == ZEND_NAME_FQ) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast));
+ return;
+ }
+
+ fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
+ if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast));
+ } else {
+ zend_ensure_valid_class_fetch_type(fetch_type);
+ result->op_type = IS_UNUSED;
+ result->u.op.num = fetch_type | fetch_flags;
+ }
+}
+/* }}} */
+
static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
@@ -2189,9 +2534,6 @@ static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
/* lookup_cv may be using another zend_string instance */
name = CG(active_op_array)->vars[EX_VAR_TO_NUM(result->u.op.var)];
- if (zend_string_equals_literal(name, "this")) {
- CG(active_op_array)->this_var = result->u.op.var;
- }
return SUCCESS;
}
@@ -2222,22 +2564,31 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
opline->extended_value = ZEND_FETCH_GLOBAL;
} else {
opline->extended_value = ZEND_FETCH_LOCAL;
- /* there is a chance someone is accessing $this */
- if (ast->kind != ZEND_AST_ZVAL
- && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1
- ) {
- zend_string *key = zend_string_init("this", sizeof("this") - 1, 0);
- CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key);
- }
}
return opline;
}
/* }}} */
+static zend_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]);
+ return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this");
+ }
+
+ return 0;
+}
+/* }}} */
+
static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
{
- if (zend_try_compile_cv(result, ast) == FAILURE) {
+ zend_op *opline;
+
+ if (is_this_fetch(ast)) {
+ opline = zend_emit_op(result, ZEND_FETCH_THIS, NULL, NULL);
+ zend_adjust_for_fetch_type(opline, type);
+ } else if (zend_try_compile_cv(result, ast) == FAILURE) {
zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type, delayed);
zend_adjust_for_fetch_type(opline, type);
}
@@ -2260,13 +2611,13 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type);
void zend_compile_assign(znode *result, zend_ast *ast);
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node);
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style);
static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
znode dummy_node;
- if (var_ast->kind == ZEND_AST_LIST) {
- zend_compile_list_assign(&dummy_node, var_ast, value_node);
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ zend_compile_list_assign(&dummy_node, var_ast, value_node, var_ast->attr);
} else {
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast,
zend_ast_create_znode(value_node));
@@ -2318,17 +2669,6 @@ void zend_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
/* }}} */
-static zend_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]);
- return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this");
- }
-
- return 0;
-}
-/* }}} */
-
static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *obj_ast = ast->child[0];
@@ -2378,19 +2718,14 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
znode class_node, prop_node;
zend_op *opline;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- zend_compile_class_ref(&class_node, class_ast, 1);
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&prop_node, prop_ast);
if (delayed) {
- opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL);
} else {
- opline = zend_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ opline = zend_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL);
}
if (opline->op1_type == IS_CONST) {
convert_to_string(CT_CONSTANT(opline->op1));
@@ -2403,7 +2738,6 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
} else {
SET_NODE(opline->op2, &class_node);
}
- opline->extended_value |= ZEND_FETCH_STATIC_MEMBER;
return opline;
}
@@ -2416,19 +2750,35 @@ void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, int d
}
/* }}} */
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node) /* {{{ */
+static void zend_verify_list_assign_target(zend_ast *var_ast, zend_bool old_style) /* {{{ */ {
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ if (old_style != var_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix [] and list()");
+ }
+ } else if (!zend_can_write_to_variable(var_ast)) {
+ zend_error(E_COMPILE_ERROR, "Assignments can only happen to writable values");
+ }
+}
+/* }}} */
+
+static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
zend_bool has_elems = 0;
for (i = 0; i < list->children; ++i) {
- zend_ast *var_ast = list->child[i];
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
znode fetch_result, dim_node;
- if (var_ast == NULL) {
+ if (elem_ast == NULL) {
continue;
}
+ if (elem_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
+ var_ast = elem_ast->child[0];
has_elems = 1;
dim_node.op_type = IS_CONST;
@@ -2438,14 +2788,69 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n
Z_TRY_ADDREF(expr_node->u.constant);
}
+ if (elem_ast->child[1] != NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
}
- if (!has_elems) {
+ if (has_elems == 0) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list");
}
+}
+/* }}} */
+
+static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
+{
+ uint32_t i;
+
+ for (i = 0; i < list->children; ++i) {
+ zend_ast *pair_ast = list->child[i];
+ zend_ast *var_ast = pair_ast->child[0];
+ zend_ast *key_ast = pair_ast->child[1];
+ znode fetch_result, dim_node;
+
+ if (pair_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
+ if (key_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_compile_expr(&dim_node, key_ast);
+
+ if (expr_node->op_type == IS_CONST) {
+ Z_TRY_ADDREF(expr_node->u.constant);
+ }
+
+ if (var_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array entries in keyed array");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
+ zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
+ zend_emit_assign_znode(var_ast, &fetch_result);
+ }
+}
+/* }}} */
+
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style) /* {{{ */
+{
+ zend_ast_list *list = zend_ast_get_list(ast);
+
+ if (list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL /* has key */) {
+ zend_compile_keyed_list_assign(list, expr_node, old_style);
+ } else {
+ zend_compile_unkeyed_list_assign(list, expr_node, old_style);
+ }
+
*result = *expr_node;
}
/* }}} */
@@ -2493,13 +2898,16 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{
zend_ast_list *list = zend_ast_get_list(list_ast);
uint32_t i;
for (i = 0; i < list->children; i++) {
- zend_ast *var_ast = list->child[i];
- if (!var_ast) {
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
+
+ if (!elem_ast) {
continue;
}
+ var_ast = elem_ast->child[0];
/* Recursively check nested list()s */
- if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) {
+ if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) {
return 1;
}
@@ -2559,7 +2967,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
offset = zend_delayed_compile_begin();
zend_delayed_compile_dim(result, var_ast, BP_VAR_W);
- if (zend_is_assign_to_self(var_ast, expr_ast)) {
+ if (zend_is_assign_to_self(var_ast, expr_ast)
+ && !is_this_fetch(expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
} else {
@@ -2581,7 +2990,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_data(&expr_node);
return;
- case ZEND_AST_LIST:
+ case ZEND_AST_ARRAY:
if (zend_list_has_assign_to_self(var_ast, expr_ast)) {
/* list($a, $b) = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
@@ -2589,7 +2998,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- zend_compile_list_assign(result, var_ast, &expr_node);
+ zend_compile_list_assign(result, var_ast, &expr_node, var_ast->attr);
return;
EMPTY_SWITCH_DEFAULT_CASE();
}
@@ -2612,7 +3021,7 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W);
- zend_delayed_compile_var(&source_node, source_ast, BP_VAR_REF);
+ zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W);
zend_delayed_compile_end(offset);
if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) {
@@ -2620,9 +3029,6 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
}
opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node);
- if (!result) {
- opline->result_type |= EXT_TYPE_UNUSED;
- }
if (zend_is_call(source_ast)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
@@ -2688,7 +3094,6 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
{
- /* TODO.AST &var error */
zend_ast_list *args = zend_ast_get_list(ast);
uint32_t i;
zend_bool uses_arg_unpack = 0;
@@ -2701,7 +3106,6 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
znode arg_node;
zend_op *opline;
zend_uchar opcode;
- zend_ulong flags = 0;
if (arg->kind == ZEND_AST_UNPACK) {
uses_arg_unpack = 1;
@@ -2727,13 +3131,16 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
/* Function call was converted into builtin instruction */
opcode = ZEND_SEND_VAL;
} else {
- opcode = ZEND_SEND_VAR_NO_REF;
- flags |= ZEND_ARG_SEND_FUNCTION;
- if (fbc && ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_BY_REF;
- if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_SILENT;
+ if (fbc) {
+ if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAL;
+ } else {
+ opcode = ZEND_SEND_VAR;
}
+ } else {
+ opcode = ZEND_SEND_VAR_NO_REF_EX;
}
}
} else if (fbc) {
@@ -2753,9 +3160,17 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
zend_compile_expr(&arg_node, arg);
ZEND_ASSERT(arg_node.op_type != IS_CV);
if (arg_node.op_type == IS_VAR) {
- opcode = ZEND_SEND_VAR_NO_REF;
- if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_BY_REF;
+ /* pass ++$a or something similar */
+ if (fbc) {
+ if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAL;
+ } else {
+ opcode = ZEND_SEND_VAR;
+ }
+ } else {
+ opcode = ZEND_SEND_VAR_NO_REF_EX;
}
} else {
if (fbc) {
@@ -2769,51 +3184,35 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
}
}
- opline = get_next_op(CG(active_op_array));
- opline->opcode = opcode;
- SET_NODE(opline->op1, &arg_node);
- SET_UNUSED(opline->op2);
+ opline = zend_emit_op(NULL, opcode, &arg_node, NULL);
opline->op2.opline_num = arg_num;
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_num);
-
- if (opcode == ZEND_SEND_VAR_NO_REF) {
- if (fbc) {
- flags |= ZEND_ARG_COMPILE_TIME_BOUND;
- }
- if ((flags & ZEND_ARG_COMPILE_TIME_BOUND) && !(flags & ZEND_ARG_SEND_BY_REF)) {
- opline->opcode = ZEND_SEND_VAR;
- opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND;
- } else {
- opline->extended_value = flags;
- }
- } else if (fbc) {
- opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND;
- }
}
return arg_count;
}
/* }}} */
-ZEND_API zend_uchar zend_get_call_op(zend_uchar init_op, zend_function *fbc) /* {{{ */
+ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */
{
- if (fbc) {
+ if (fbc && init_op->opcode == ZEND_INIT_FCALL) {
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
- if (!zend_execute_internal &&
- !fbc->common.scope &&
- !(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) {
- return ZEND_DO_ICALL;
+ if (!zend_execute_internal) {
+ if (!(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) {
+ return ZEND_DO_ICALL;
+ } else {
+ return ZEND_DO_FCALL_BY_NAME;
+ }
}
} else {
- if (zend_execute_ex == execute_ex &&
- !(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) {
+ if (zend_execute_ex == execute_ex) {
return ZEND_DO_UCALL;
}
}
} else if (zend_execute_ex == execute_ex &&
!zend_execute_internal &&
- (init_op == ZEND_INIT_FCALL_BY_NAME ||
- init_op == ZEND_INIT_NS_FCALL_BY_NAME)) {
+ (init_op->opcode == ZEND_INIT_FCALL_BY_NAME ||
+ init_op->opcode == ZEND_INIT_NS_FCALL_BY_NAME)) {
return ZEND_DO_FCALL_BY_NAME;
}
return ZEND_DO_FCALL;
@@ -2839,7 +3238,7 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
}
call_flags = (opline->opcode == ZEND_NEW ? ZEND_CALL_CTOR : 0);
- opline = zend_emit_op(result, zend_get_call_op(opline->opcode, fbc), NULL, NULL);
+ opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
opline->op1.num = call_flags;
zend_do_extended_fcall_end();
@@ -2875,13 +3274,14 @@ void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast) /
void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast) /* {{{ */
{
- zend_op *opline = get_next_op(CG(active_op_array));
if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) {
const char *colon;
zend_string *str = Z_STR(name_node->u.constant);
if ((colon = zend_memrchr(ZSTR_VAL(str), ':', ZSTR_LEN(str))) != NULL && colon > ZSTR_VAL(str) && *(colon - 1) == ':') {
zend_string *class = zend_string_init(ZSTR_VAL(str), colon - ZSTR_VAL(str) - 1, 0);
zend_string *method = zend_string_init(colon + 1, ZSTR_LEN(str) - (colon - ZSTR_VAL(str)) - 1, 0);
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
opline->op1_type = IS_CONST;
opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), class);
@@ -2890,6 +3290,8 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
zval_ptr_dtor(&name_node->u.constant);
} else {
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
opline->op2_type = IS_CONST;
@@ -2897,9 +3299,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
}
} else {
- opline->opcode = ZEND_INIT_DYNAMIC_CALL;
- SET_UNUSED(opline->op1);
- SET_NODE(opline->op2, name_node);
+ zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node);
}
zend_compile_call_common(result, args_ast, NULL);
@@ -2971,6 +3371,14 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */
return FAILURE;
}
+ if (zend_try_ct_eval_const(&result->u.constant, name, 0)) {
+ zend_string_release(name);
+ zval_ptr_dtor(&result->u.constant);
+ ZVAL_TRUE(&result->u.constant);
+ result->op_type = IS_CONST;
+ return SUCCESS;
+ }
+
opline = zend_emit_op_tmp(result, ZEND_DEFINED, NULL, NULL);
opline->op1_type = IS_CONST;
LITERAL_STR(opline->op1, name);
@@ -2987,6 +3395,46 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */
}
/* }}} */
+int zend_compile_func_chr(znode *result, zend_ast_list *args) /* {{{ */
+{
+
+ if (args->children == 1 &&
+ args->child[0]->kind == ZEND_AST_ZVAL &&
+ Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_LONG) {
+
+ zend_long c = Z_LVAL_P(zend_ast_get_zval(args->child[0])) & 0xff;
+
+ result->op_type = IS_CONST;
+ if (CG(one_char_string)[c]) {
+ ZVAL_INTERNED_STR(&result->u.constant, CG(one_char_string)[c]);
+ } else {
+ ZVAL_NEW_STR(&result->u.constant, zend_string_alloc(1, 0));
+ Z_STRVAL_P(&result->u.constant)[0] = (char)c;
+ Z_STRVAL_P(&result->u.constant)[1] = '\0';
+ }
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+/* }}} */
+
+int zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */
+{
+ if (args->children == 1 &&
+ args->child[0]->kind == ZEND_AST_ZVAL &&
+ Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_STRING) {
+
+ result->op_type = IS_CONST;
+ ZVAL_LONG(&result->u.constant, (unsigned char)Z_STRVAL_P(zend_ast_get_zval(args->child[0]))[0]);
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+/* }}} */
+
+
static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t num_args) /* {{{ */
{
zend_string *name, *lcname;
@@ -3097,24 +3545,6 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam
}
/* }}} */
-static void zend_compile_assert_side_effects(zend_ast *ast) /* {{{ */
-{
- int i;
- int children = zend_ast_is_list(ast) ? zend_ast_get_list(ast)->children : zend_ast_get_num_children(ast);
-
- for (i = 0; i < children; i++) {
- zend_ast *child = (zend_ast_is_list(ast) ? zend_ast_get_list(ast)->child : ast->child)[i];
- if (child) {
- if (child->kind == ZEND_AST_YIELD) {
- zend_mark_function_as_generator();
- } else if (ast->kind >= ZEND_AST_IS_LIST_SHIFT) {
- zend_compile_assert_side_effects(child);
- }
- }
- }
-}
-/* }}} */
-
static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, zend_function *fbc) /* {{{ */
{
if (EG(assertions) >= 0) {
@@ -3148,22 +3578,22 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *
zend_compile_call_common(result, (zend_ast*)args, fbc);
- CG(active_op_array)->opcodes[check_op_number].op2.opline_num = get_next_op_number(CG(active_op_array));
+ opline = &CG(active_op_array)->opcodes[check_op_number];
+ opline->op2.opline_num = get_next_op_number(CG(active_op_array));
+ SET_NODE(opline->result, result);
} else {
if (!fbc) {
zend_string_release(name);
}
result->op_type = IS_CONST;
ZVAL_TRUE(&result->u.constant);
-
- zend_compile_assert_side_effects((zend_ast *) args);
}
return SUCCESS;
}
/* }}} */
-int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc) /* {{{ */
+int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
{
if (fbc->internal_function.handler == ZEND_FN(display_disabled_function)) {
return FAILURE;
@@ -3203,6 +3633,10 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l
return zend_compile_func_typecheck(result, args, IS_RESOURCE);
} else if (zend_string_equals_literal(lcname, "defined")) {
return zend_compile_func_defined(result, args);
+ } else if (zend_string_equals_literal(lcname, "chr") && type == BP_VAR_R) {
+ return zend_compile_func_chr(result, args);
+ } else if (zend_string_equals_literal(lcname, "ord") && type == BP_VAR_R) {
+ return zend_compile_func_ord(result, args);
} else if (zend_string_equals_literal(lcname, "call_user_func_array")) {
return zend_compile_func_cufa(result, args, lcname);
} else if (zend_string_equals_literal(lcname, "call_user_func")) {
@@ -3257,7 +3691,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
if (zend_try_compile_special_func(result, lcname,
- zend_ast_get_list(args_ast), fbc) == SUCCESS
+ zend_ast_get_list(args_ast), fbc, type) == SUCCESS
) {
zend_string_release(lcname);
zval_ptr_dtor(&name_node.u.constant);
@@ -3283,6 +3717,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;
if (is_this_fetch(obj_ast)) {
obj_node.op_type = IS_UNUSED;
@@ -3306,7 +3741,20 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
SET_NODE(opline->op2, &method_node);
}
- zend_compile_call_common(result, args_ast, NULL);
+ /* Check if this calls a known method on $this */
+ if (opline->op1_type == IS_UNUSED && opline->op2_type == IS_CONST &&
+ CG(active_class_entry) && zend_is_scope_known()) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
+ fbc = zend_hash_find_ptr(&CG(active_class_entry)->function_table, lcname);
+
+ /* We only know the exact method that is being called if it is either private or final.
+ * Otherwise an overriding method in a child class may be called. */
+ if (fbc && !(fbc->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_FINAL))) {
+ fbc = NULL;
+ }
+ }
+
+ zend_compile_call_common(result, args_ast, fbc);
}
/* }}} */
@@ -3324,15 +3772,9 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
znode class_node, method_node;
zend_op *opline;
- zend_ulong extended_value = 0;
+ zend_function *fbc = NULL;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- opline = zend_compile_class_ref(&class_node, class_ast, 1);
- extended_value = opline->extended_value;
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&method_node, method_ast);
if (method_node.op_type == IS_CONST) {
@@ -3348,7 +3790,6 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
- opline->extended_value = extended_value;
zend_set_class_name_op1(opline, &class_node);
@@ -3364,8 +3805,30 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
} else {
SET_NODE(opline->op2, &method_node);
}
+ zend_check_live_ranges(opline);
- zend_compile_call_common(result, args_ast, NULL);
+ /* Check if we already know which method we're calling */
+ if (opline->op2_type == IS_CONST) {
+ zend_class_entry *ce = NULL;
+ if (opline->op1_type == IS_CONST) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1);
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (!ce && CG(active_class_entry)
+ && zend_string_equals_ci(CG(active_class_entry)->name, lcname)) {
+ ce = CG(active_class_entry);
+ }
+ } else if (opline->op1_type == IS_UNUSED
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
+ && zend_is_scope_known()) {
+ ce = CG(active_class_entry);
+ }
+ if (ce) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
+ fbc = zend_hash_find_ptr(&ce->function_table, lcname);
+ }
+ }
+
+ zend_compile_call_common(result, args_ast, fbc);
}
/* }}} */
@@ -3380,10 +3843,7 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
zend_op *opline;
uint32_t opnum;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else if (class_ast->kind == ZEND_AST_CLASS) {
+ if (class_ast->kind == ZEND_AST_CLASS) {
uint32_t dcl_opnum = get_next_op_number(CG(active_op_array));
zend_compile_class_decl(class_ast);
/* jump over anon class declaration */
@@ -3393,9 +3853,9 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
}
class_node.op_type = opline->result_type;
class_node.u.op.var = opline->result.var;
- opline->op1.opline_num = get_next_op_number(CG(active_op_array));
+ opline->extended_value = get_next_op_number(CG(active_op_array));
} else {
- zend_compile_class_ref(&class_node, class_ast, 1);
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
}
opnum = get_next_op_number(CG(active_op_array));
@@ -3412,7 +3872,8 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
zend_compile_call_common(&ctor_result, args_ast, NULL);
zend_do_free(&ctor_result);
- /* New jumps over ctor call if ctor does not exist */
+ /* We save the position of DO_FCALL for convenience in find_live_range().
+ * This info is not preserved for runtime. */
opline = &CG(active_op_array)->opcodes[opnum];
opline->op2.opline_num = get_next_op_number(CG(active_op_array));
}
@@ -3425,7 +3886,7 @@ void zend_compile_clone(znode *result, zend_ast *ast) /* {{{ */
znode obj_node;
zend_compile_expr(&obj_node, obj_ast);
- zend_emit_op(result, ZEND_CLONE, &obj_node, NULL);
+ zend_emit_op_tmp(result, ZEND_CLONE, &obj_node, NULL);
}
/* }}} */
@@ -3441,23 +3902,33 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */
convert_to_string(&name_node.u.constant);
}
- if (zend_try_compile_cv(&result, var_ast) == SUCCESS) {
+ 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) {
zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node);
zend_alloc_cache_slot(opline->op2.constant);
} else {
- zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL);
+ /* name_ast should be evaluated only. FETCH_GLOBAL_LOCK instructs FETCH_W
+ * to not free the name_node operand, so it can be reused in the following
+ * ASSIGN_REF, which then frees it. */
+ zend_op *opline = zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL_LOCK;
- // TODO.AST Avoid double fetch
- //opline->extended_value = ZEND_FETCH_GLOBAL_LOCK;
+ if (name_node.op_type == IS_CONST) {
+ zend_string_addref(Z_STR(name_node.u.constant));
+ }
- zend_emit_assign_ref_znode(var_ast, &result);
+ zend_emit_assign_ref_znode(
+ zend_ast_create(ZEND_AST_VAR, zend_ast_create_znode(&name_node)),
+ &result
+ );
}
}
/* }}} */
static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_bool by_ref) /* {{{ */
{
- znode var_node, result;
+ znode var_node;
zend_op *opline;
zend_compile_expr(&var_node, var_ast);
@@ -3478,16 +3949,14 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_
}
zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value);
- opline = zend_emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL);
- opline->extended_value = ZEND_FETCH_STATIC;
-
- if (by_ref) {
- zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast);
- zend_emit_assign_ref_znode(fetch_ast, &result);
- } else {
- zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast);
- zend_emit_assign_znode(fetch_ast, &result);
+ if (zend_string_equals_literal(Z_STR(var_node.u.constant), "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable");
}
+
+ opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node);
+ opline->op1_type = IS_CV;
+ opline->op1.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR(var_node.u.constant)));
+ opline->extended_value = by_ref;
}
/* }}} */
@@ -3517,7 +3986,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
switch (var_ast->kind) {
case ZEND_AST_VAR:
- if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
+ if (is_this_fetch(var_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this");
+ } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
@@ -3535,14 +4006,14 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
return;
case ZEND_AST_STATIC_PROP:
opline = zend_compile_static_prop_common(NULL, var_ast, BP_VAR_UNSET, 0);
- opline->opcode = ZEND_UNSET_VAR;
+ opline->opcode = ZEND_UNSET_STATIC_PROP;
return;
EMPTY_SWITCH_DEFAULT_CASE()
}
}
/* }}} */
-static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
+static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value) /* {{{ */
{
zend_loop_var *base;
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
@@ -3559,8 +4030,18 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
opline->result_type = IS_TMP_VAR;
opline->result.var = loop_var->var_num;
SET_UNUSED(opline->op1);
- SET_UNUSED(opline->op2);
+ if (return_value) {
+ SET_NODE(opline->op2, return_value);
+ } else {
+ SET_UNUSED(opline->op2);
+ }
opline->op1.num = loop_var->u.try_catch_offset;
+ } else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
+ zend_op *opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_DISCARD_EXCEPTION;
+ opline->op1_type = IS_TMP_VAR;
+ opline->op1.var = loop_var->var_num;
+ SET_UNUSED(opline->op2);
} else if (loop_var->opcode == ZEND_RETURN) {
/* Stack separator */
break;
@@ -3572,13 +4053,13 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
} else {
zend_op *opline;
- ZEND_ASSERT(loop_var->var_type == IS_VAR || loop_var->var_type == IS_TMP_VAR);
+ ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR));
opline = get_next_op(CG(active_op_array));
opline->opcode = loop_var->opcode;
opline->op1_type = loop_var->var_type;
opline->op1.var = loop_var->var_num;
SET_UNUSED(opline->op2);
- opline->op2.num = loop_var->u.brk_cont_offset;
+ opline->op2.num = loop_var->u.live_range_offset;
opline->extended_value = ZEND_FREE_ON_RETURN;
depth--;
}
@@ -3587,9 +4068,9 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
}
/* }}} */
-static int zend_handle_loops_and_finally(void) /* {{{ */
+static int zend_handle_loops_and_finally(znode *return_value) /* {{{ */
{
- return zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1);
+ return zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1, return_value);
}
/* }}} */
@@ -3605,23 +4086,18 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
expr_node.op_type = IS_CONST;
ZVAL_NULL(&expr_node.u.constant);
} else if (by_ref && zend_is_variable(expr_ast) && !zend_is_call(expr_ast)) {
- zend_compile_var(&expr_node, expr_ast, BP_VAR_REF);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_W);
} else {
zend_compile_expr(&expr_node, expr_ast);
}
- if (CG(context).in_finally) {
- opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
- opline->op1_type = IS_TMP_VAR;
- opline->op1.var = CG(context).fast_call_var;
- }
-
/* Generator return types are handled separately */
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
+ zend_emit_return_type_check(
+ expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
}
- zend_handle_loops_and_finally();
+ zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL);
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL);
@@ -3691,7 +4167,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context",
ast->kind == ZEND_AST_BREAK ? "break" : "continue");
} else {
- if (!zend_handle_loops_and_finally_ex(depth)) {
+ if (!zend_handle_loops_and_finally_ex(depth, NULL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
ast->kind == ZEND_AST_BREAK ? "break" : "continue",
depth, depth == 1 ? "" : "s");
@@ -3724,14 +4200,14 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
ZVAL_NULL(label);
current = opline->extended_value;
- for (; current != dest->brk_cont; current = op_array->brk_cont_array[current].parent) {
+ for (; current != dest->brk_cont; current = CG(context).brk_cont_array[current].parent) {
if (current == -1) {
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");
}
- if (op_array->brk_cont_array[current].start >= 0) {
+ if (CG(context).brk_cont_array[current].start >= 0) {
remove_oplines--;
}
}
@@ -3774,7 +4250,7 @@ void zend_compile_goto(zend_ast *ast) /* {{{ */
zend_compile_expr(&label_node, label_ast);
/* Label resolution and unwinding adjustments happen in pass two. */
- zend_handle_loops_and_finally();
+ zend_handle_loops_and_finally(NULL);
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
opline->op1.num = get_next_op_number(CG(active_op_array)) - opnum_start - 1;
opline->extended_value = CG(context).current_brk_cont;
@@ -3820,7 +4296,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3842,7 +4318,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3898,7 +4374,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
- zend_end_loop(opnum_loop);
+ zend_end_loop(opnum_loop, NULL);
}
/* }}} */
@@ -3920,7 +4396,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
if (key_ast->kind == ZEND_AST_REF) {
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
}
- if (key_ast->kind == ZEND_AST_LIST) {
+ if (key_ast->kind == ZEND_AST_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
}
}
@@ -3942,10 +4418,14 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
opnum_reset = get_next_op_number(CG(active_op_array));
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
+ zend_begin_loop(ZEND_FE_FREE, &reset_node);
+
opnum_fetch = get_next_op_number(CG(active_op_array));
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
- if (value_ast->kind == ZEND_AST_VAR &&
+ if (is_this_fetch(value_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
+ } else if (value_ast->kind == ZEND_AST_VAR &&
zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
SET_NODE(opline->op2, &value_node);
} else {
@@ -3965,8 +4445,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
zend_emit_assign_znode(key_ast, &key_node);
}
- zend_begin_loop(ZEND_FE_FREE, &reset_node);
-
zend_compile_stmt(stmt_ast);
zend_emit_jump(opnum_fetch);
@@ -3977,9 +4455,9 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
opline = &CG(active_op_array)->opcodes[opnum_fetch];
opline->extended_value = get_next_op_number(CG(active_op_array));
- zend_end_loop(opnum_fetch);
+ zend_end_loop(opnum_fetch, &reset_node);
- zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
+ opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
}
/* }}} */
@@ -4099,10 +4577,14 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_update_jump_target_to_next(opnum_default_jmp);
}
- zend_end_loop(get_next_op_number(CG(active_op_array)));
+ zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node);
- if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) {
- zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+ if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
+ /* don't use emit_op() to prevent automatic live-range construction */
+ opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_FREE;
+ SET_NODE(opline->op1, &expr_node);
+ SET_UNUSED(opline->op2);
} else if (expr_node.op_type == IS_CONST) {
zval_dtor(&expr_node.u.constant);
}
@@ -4117,10 +4599,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_ast_list *catches = zend_ast_get_list(ast->child[1]);
zend_ast *finally_ast = ast->child[2];
- uint32_t i;
+ uint32_t i, j;
zend_op *opline;
uint32_t try_catch_offset;
uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0);
+ uint32_t orig_fast_call_var = CG(context).fast_call_var;
+ uint32_t orig_try_catch_offset = CG(context).try_catch_offset;
if (catches->children == 0 && !finally_ast) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally");
@@ -4143,8 +4627,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_loop_var fast_call;
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
CG(active_op_array)->fn_flags |= ZEND_ACC_HAS_FINALLY_BLOCK;
- CG(context).fast_call_var = get_temporary_variable(CG(active_op_array));
}
+ CG(context).fast_call_var = get_temporary_variable(CG(active_op_array));
/* Push FAST_CALL on unwind stack */
fast_call.opcode = ZEND_FAST_CALL;
@@ -4154,6 +4638,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_stack_push(&CG(loop_var_stack), &fast_call);
}
+ CG(context).try_catch_offset = try_catch_offset;
+
zend_compile_stmt(try_ast);
if (catches->children != 0) {
@@ -4162,34 +4648,57 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
for (i = 0; i < catches->children; ++i) {
zend_ast *catch_ast = catches->child[i];
- zend_ast *class_ast = catch_ast->child[0];
+ zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
zend_ast *var_ast = catch_ast->child[1];
zend_ast *stmt_ast = catch_ast->child[2];
zval *var_name = zend_ast_get_zval(var_ast);
zend_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;
- if (!zend_is_const_default_class_ref(class_ast)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement");
- }
+ CG(zend_lineno) = catch_ast->lineno;
- opnum_catch = get_next_op_number(CG(active_op_array));
- if (i == 0) {
- CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch;
- }
+ for (j = 0; j < classes->children; j++) {
- CG(zend_lineno) = catch_ast->lineno;
+ zend_ast *class_ast = classes->child[j];
+ zend_bool is_last_class = (j + 1 == classes->children);
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_CATCH;
- opline->op1_type = IS_CONST;
- opline->op1.constant = zend_add_class_name_literal(CG(active_op_array),
- zend_resolve_class_name_ast(class_ast));
+ if (!zend_is_const_default_class_ref(class_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement");
+ }
- opline->op2_type = IS_CV;
- opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name)));
- opline->result.num = is_last_catch;
+ opnum_catch = get_next_op_number(CG(active_op_array));
+ if (i == 0 && j == 0) {
+ CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch;
+ }
+
+ opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_CATCH;
+ opline->op1_type = IS_CONST;
+ opline->op1.constant = zend_add_class_name_literal(CG(active_op_array),
+ zend_resolve_class_name_ast(class_ast));
+
+ if (zend_string_equals_literal(Z_STR_P(var_name), "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
+ }
+
+ opline->op2_type = IS_CV;
+ opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name)));
+
+ opline->result.num = is_last_catch && is_last_class;
+
+ if (!is_last_class) {
+ jmp_multicatch[j] = zend_emit_jump(0);
+ opline->extended_value = get_next_op_number(CG(active_op_array));
+ }
+ }
+
+ for (j = 0; j < classes->children - 1; j++) {
+ zend_update_jump_target_to_next(jmp_multicatch[j]);
+ }
+
+ efree(jmp_multicatch);
zend_compile_stmt(stmt_ast);
@@ -4198,7 +4707,9 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
}
opline = &CG(active_op_array)->opcodes[opnum_catch];
- opline->extended_value = get_next_op_number(CG(active_op_array));
+ if (!is_last_catch) {
+ opline->extended_value = get_next_op_number(CG(active_op_array));
+ }
}
for (i = 0; i < catches->children; ++i) {
@@ -4206,11 +4717,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
}
if (finally_ast) {
+ zend_loop_var discard_exception;
uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1;
/* Pop FAST_CALL from unwind stack */
zend_stack_del_top(&CG(loop_var_stack));
+ /* Push DISCARD_EXCEPTION on unwind stack */
+ discard_exception.opcode = ZEND_DISCARD_EXCEPTION;
+ discard_exception.var_type = IS_TMP_VAR;
+ discard_exception.var_num = CG(context).fast_call_var;
+ zend_stack_push(&CG(loop_var_stack), &discard_exception);
+
CG(zend_lineno) = finally_ast->lineno;
opline = zend_emit_op(NULL, ZEND_FAST_CALL, NULL, NULL);
@@ -4231,10 +4749,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_FAST_RET, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
+ opline->op2.num = orig_try_catch_offset;
zend_update_jump_target_to_next(opnum_jmp);
+
+ CG(context).fast_call_var = orig_fast_call_var;
+
+ /* Pop DISCARD_EXCEPTION from unwind stack */
+ zend_stack_del_top(&CG(loop_var_stack));
}
+ CG(context).try_catch_offset = orig_try_catch_offset;
+
efree(jmp_opnums);
}
/* }}} */
@@ -4414,7 +4940,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
if (type != 0) {
- if (ast->attr != ZEND_NAME_NOT_FQ) {
+ if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
zend_error_noreturn(E_COMPILE_ERROR,
"Scalar type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
@@ -4454,8 +4980,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_infos->allow_null = 0;
arg_infos->class_name = NULL;
+ if (return_type_ast->attr & ZEND_TYPE_NULLABLE) {
+ arg_infos->allow_null = 1;
+ return_type_ast->attr &= ~ZEND_TYPE_NULLABLE;
+ }
+
zend_compile_typename(return_type_ast, arg_infos);
+ if (arg_infos->type_hint == IS_VOID && arg_infos->allow_null) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
+ }
+
arg_infos++;
op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
} else {
@@ -4491,11 +5026,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Redefinition of parameter $%s",
ZSTR_VAL(name));
} else if (zend_string_equals_literal(name, "this")) {
- if ((op_array->scope || (op_array->fn_flags & ZEND_ACC_CLOSURE))
- && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter");
- }
- op_array->this_var = var_node.u.op.var;
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter");
}
if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
@@ -4542,12 +5073,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
&& (Z_TYPE(default_node.u.constant) == IS_NULL
|| (Z_TYPE(default_node.u.constant) == IS_CONSTANT
&& strcasecmp(Z_STRVAL(default_node.u.constant), "NULL") == 0));
+ zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE;
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- arg_info->allow_null = has_null_default;
+ arg_info->allow_null = has_null_default || is_explicitly_nullable;
zend_compile_typename(type_ast, arg_info);
+ if (arg_info->type_hint == IS_VOID) {
+ zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
+ }
+
if (type_ast->kind == ZEND_AST_TYPE) {
if (arg_info->type_hint == IS_ARRAY) {
if (default_ast && !has_null_default
@@ -4623,23 +5159,61 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
}
/* }}} */
+static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */
+{
+ zend_ast_list *list = zend_ast_get_list(uses_ast);
+ uint32_t i;
+
+ for (i = 0; i < list->children; ++i) {
+ zend_ast *var_name_ast = list->child[i];
+ zend_string *var_name = zend_ast_get_str(var_name_ast);
+ zend_bool by_ref = var_name_ast->attr;
+ zend_op *opline;
+
+ if (zend_string_equals_literal(var_name, "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
+ }
+
+ if (zend_is_auto_global(var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable");
+ }
+
+ opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL);
+ opline->op2_type = IS_CV;
+ opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(var_name));
+ opline->extended_value = by_ref;
+ }
+}
+/* }}} */
+
void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
{
+ zend_op_array *op_array = CG(active_op_array);
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
zend_ast *var_ast = list->child[i];
- zend_string *name = zend_ast_get_str(var_ast);
+ zend_string *var_name = zend_ast_get_str(var_ast);
zend_bool by_ref = var_ast->attr;
zval zv;
+ ZVAL_NULL(&zv);
- if (zend_string_equals_literal(name, "this")) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
+ if (op_array->static_variables
+ && zend_hash_exists(op_array->static_variables, var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use variable $%s twice", ZSTR_VAL(var_name));
}
- ZVAL_NULL(&zv);
- Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR;
+ {
+ int i;
+ for (i = 0; i < op_array->last_var; i++) {
+ if (zend_string_equals(op_array->vars[i], var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use lexical variable $%s as a parameter name", ZSTR_VAL(var_name));
+ }
+ }
+ }
zend_compile_static_var_common(var_ast, &zv, by_ref);
}
@@ -4819,7 +5393,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) /* {{{ */
{
zend_ast *params_ast = decl->child[0];
- zend_string *unqualified_name, *name, *lcname;
+ zend_string *unqualified_name, *name, *lcname, *key;
zend_op *opline;
unqualified_name = decl->name;
@@ -4842,22 +5416,21 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
ZEND_AUTOLOAD_FUNC_NAME);
}
+ key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
+ zend_hash_update_ptr(CG(function_table), key, op_array);
+
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, key);
} else {
opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_DECLARE_FUNCTION;
- opline->op2_type = IS_CONST;
- LITERAL_STR(opline->op2, zend_string_copy(lcname));
- }
-
- {
- zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
-
opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, key);
-
- zend_hash_update_ptr(CG(function_table), key, op_array);
+ LITERAL_STR(opline->op1, zend_string_copy(lcname));
+ /* RTD key is placed after lcname literal in op1 */
+ zend_add_literal_string(CG(active_op_array), &key);
+ SET_UNUSED(opline->op2);
}
zend_string_release(lcname);
@@ -4895,6 +5468,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
zend_begin_method_decl(op_array, decl->name, has_body);
} else {
zend_begin_func_decl(result, op_array, decl);
+ if (uses_ast) {
+ zend_compile_closure_binding(result, uses_ast);
+ }
}
CG(active_op_array) = op_array;
@@ -4915,6 +5491,10 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
}
zend_compile_params(params_ast, return_type_ast);
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ zend_mark_function_as_generator();
+ zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL);
+ }
if (uses_ast) {
zend_compile_closure_uses(uses_ast);
}
@@ -4929,7 +5509,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
CG(zend_lineno) = decl->end_lineno;
zend_do_extended_info();
- zend_emit_final_return(NULL);
+ zend_emit_final_return(0);
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
@@ -5008,25 +5588,25 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */
zend_ast *const_ast = list->child[i];
zend_ast *name_ast = const_ast->child[0];
zend_ast *value_ast = const_ast->child[1];
+ zend_ast *doc_comment_ast = const_ast->child[2];
zend_string *name = zend_ast_get_str(name_ast);
+ zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
zval value_zv;
- if (zend_string_equals_literal_ci(name, "class")) {
- zend_error(E_COMPILE_ERROR,
- "A class constant must not be called 'class'; it is reserved for class name fetching");
+ if (UNEXPECTED(ast->attr & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) {
+ if (ast->attr & ZEND_ACC_STATIC) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as constant modifier");
+ } else if (ast->attr & ZEND_ACC_ABSTRACT) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as constant modifier");
+ } else if (ast->attr & ZEND_ACC_FINAL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as constant modifier");
+ }
}
zend_const_expr_to_zval(&value_zv, value_ast);
name = zend_new_interned_string_safe(name);
- if (zend_hash_add(&ce->constants_table, name, &value_zv) == NULL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s",
- ZSTR_VAL(ce->name), ZSTR_VAL(name));
- }
-
- if (Z_CONSTANT(value_zv)) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
+ zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment);
}
}
/* }}} */
@@ -5277,37 +5857,34 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
opline = get_next_op(CG(active_op_array));
zend_make_var_result(&declare_node, opline);
- // TODO.AST drop this
GET_NODE(&FC(implementing_class), opline->result);
- opline->op2_type = IS_CONST;
- LITERAL_STR(opline->op2, lcname);
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, lcname);
if (decl->flags & ZEND_ACC_ANON_CLASS) {
if (extends_ast) {
opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
- opline->extended_value = extends_node.u.op.var;
+ SET_NODE(opline->op2, &extends_node);
} else {
opline->opcode = ZEND_DECLARE_ANON_CLASS;
}
- opline->op1_type = IS_UNUSED;
-
zend_hash_update_ptr(CG(class_table), lcname, ce);
} else {
zend_string *key;
if (extends_ast) {
opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
- opline->extended_value = extends_node.u.op.var;
+ SET_NODE(opline->op2, &extends_node);
} else {
opline->opcode = ZEND_DECLARE_CLASS;
+ SET_UNUSED(opline->op2);
}
key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
-
- opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, key);
+ /* RTD key is placed after lcname literal in op1 */
+ zend_add_literal_string(CG(active_op_array), &key);
zend_hash_update_ptr(CG(class_table), key, ce);
}
@@ -5804,6 +6381,35 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
}
/* }}} */
+ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */
+{
+ if (!(opcode == ZEND_ADD || opcode == ZEND_SUB || opcode == ZEND_MUL || opcode == ZEND_DIV
+ || opcode == ZEND_POW || opcode == ZEND_MOD || opcode == ZEND_SL || opcode == ZEND_SR
+ || opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)) {
+ return 0;
+ }
+
+ /* While basic arithmetic operators always produce numeric string errors,
+ * bitwise operators don't produce errors if both operands are strings */
+ if ((opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)
+ && Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) {
+ return 0;
+ }
+
+ if (Z_TYPE_P(op1) == IS_STRING
+ && !is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), NULL, NULL, 0)) {
+ return 1;
+ }
+
+ if (Z_TYPE_P(op2) == IS_STRING
+ && !is_numeric_string(Z_STRVAL_P(op2), Z_STRLEN_P(op2), NULL, NULL, 0)) {
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */
{
binary_op_type fn = get_binary_op(opcode);
@@ -5817,6 +6423,11 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode
return 0;
}
+ /* don't evaluate numeric string error-producing operations at compile-time */
+ if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) {
+ return 0;
+ }
+
fn(result, op1, op2);
return 1;
}
@@ -5829,11 +6440,11 @@ static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op
}
/* }}} */
-static inline void zend_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
+static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
{
zval left;
ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
- mul_function(result, &left, op);
+ return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op);
}
/* }}} */
@@ -5851,14 +6462,22 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
uint32_t i;
zend_bool is_constant = 1;
+ if (ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression");
+ }
+
/* First ensure that *all* child nodes are constant and by-val */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
- zend_bool by_ref = elem_ast->attr;
+
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
zend_eval_const_expr(&elem_ast->child[0]);
zend_eval_const_expr(&elem_ast->child[1]);
- if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL
+ if (elem_ast->attr /* by_ref */ || elem_ast->child[0]->kind != ZEND_AST_ZVAL
|| (elem_ast->child[1] && elem_ast->child[1]->kind != ZEND_AST_ZVAL)
) {
is_constant = 0;
@@ -6031,10 +6650,11 @@ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
if (expr_node.op_type == IS_CONST) {
- result->op_type = IS_CONST;
- zend_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant);
- zval_ptr_dtor(&expr_node.u.constant);
- return;
+ if (zend_try_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant)) {
+ result->op_type = IS_CONST;
+ zval_ptr_dtor(&expr_node.u.constant);
+ return;
+ }
}
lefthand_node.op_type = IS_CONST;
@@ -6288,7 +6908,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
if (value_ast) {
if (returns_by_ref && zend_is_variable(value_ast) && !zend_is_call(value_ast)) {
- zend_compile_var(&value_node, value_ast, BP_VAR_REF);
+ zend_compile_var(&value_node, value_ast, BP_VAR_W);
} else {
zend_compile_expr(&value_node, value_ast);
}
@@ -6334,13 +6954,8 @@ void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */
"instanceof expects an object instance, constant given");
}
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- opline = zend_compile_class_ref(&class_node, class_ast, 0);
- opline->extended_value |= ZEND_FETCH_CLASS_NO_AUTOLOAD;
- }
+ zend_compile_class_ref_ex(&class_node, class_ast,
+ ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_EXCEPTION);
opline = zend_emit_op_tmp(result, ZEND_INSTANCEOF, &obj_node, NULL);
@@ -6394,7 +7009,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
switch (var_ast->kind) {
case ZEND_AST_VAR:
- if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
+ if (is_this_fetch(var_ast)) {
+ opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL);
+ } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
@@ -6412,7 +7029,7 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
break;
case ZEND_AST_STATIC_PROP:
opline = zend_compile_static_prop_common(result, var_ast, BP_VAR_IS, 0);
- opline->opcode = ZEND_ISSET_ISEMPTY_VAR;
+ opline->opcode = ZEND_ISSET_ISEMPTY_STATIC_PROP;
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -6426,10 +7043,9 @@ 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;
+ uint32_t range;
- begin_opline_num = get_next_op_number(CG(active_op_array));
+ range = zend_start_live_range(CG(active_op_array), 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) {
@@ -6440,15 +7056,12 @@ 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;
+ zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array)),
+ ZEND_LIVE_SILENCE, silence_node.u.op.var);
+
+ zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
}
/* }}} */
@@ -6484,12 +7097,18 @@ 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 = elem_ast->child[0];
- zend_ast *key_ast = elem_ast->child[1];
- zend_bool by_ref = elem_ast->attr;
-
+ zend_ast *value_ast, *key_ast;
+ zend_bool by_ref;
znode value_node, key_node, *key_node_ptr = NULL;
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
+ value_ast = elem_ast->child[0];
+ key_ast = elem_ast->child[1];
+ by_ref = elem_ast->attr;
+
if (key_ast) {
zend_compile_expr(&key_node, key_ast);
zend_handle_numeric_op(&key_node);
@@ -6526,7 +7145,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
/* Add a flag to INIT_ARRAY if we know this array cannot be packed */
if (!packed) {
- ZEND_ASSERT(opnum_init != -1);
+ ZEND_ASSERT(opnum_init != (uint32_t)-1);
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value |= ZEND_ARRAY_NOT_PACKED;
}
@@ -6592,7 +7211,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
znode class_node, const_node;
zend_op *opline;
- zend_string *resolved_name;
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) {
if (Z_TYPE(result->u.constant) == IS_NULL) {
@@ -6608,31 +7226,26 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
zend_eval_const_expr(&const_ast);
if (class_ast->kind == ZEND_AST_ZVAL) {
+ zend_string *resolved_name;
+
resolved_name = zend_resolve_class_name_ast(class_ast);
if (const_ast->kind == ZEND_AST_ZVAL && zend_try_ct_eval_class_const(&result->u.constant, resolved_name, zend_ast_get_str(const_ast))) {
result->op_type = IS_CONST;
zend_string_release(resolved_name);
return;
}
+ zend_string_release(resolved_name);
}
if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) {
zend_error_noreturn(E_COMPILE_ERROR,
"Dynamic class names are not allowed in compile-time ::class fetch");
}
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, resolved_name);
- } else {
- if (class_ast->kind == ZEND_AST_ZVAL) {
- zend_string_release(resolved_name);
- }
- zend_compile_class_ref(&class_node, class_ast, 1);
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&const_node, const_ast);
- opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, &const_node);
+ opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_CONSTANT, NULL, &const_node);
zend_set_class_name_op1(opline, &class_node);
@@ -6774,10 +7387,7 @@ 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;
+ uint32_t range = zend_start_live_range(CG(active_op_array), rope_init_lineno);
init_opline->extended_value = j;
opline->opcode = ZEND_ROPE_END;
@@ -6791,15 +7401,19 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
get_temporary_variable(CG(active_op_array));
i--;
}
+
+ zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes,
+ ZEND_LIVE_ROPE, var);
+
/* Update all the previous opcodes to use the same variable */
while (opline != init_opline) {
opline--;
if (opline->opcode == ZEND_ROPE_ADD &&
- opline->result.var == -1) {
+ opline->result.var == (uint32_t)-1) {
opline->op1.var = var;
opline->result.var = var;
} else if (opline->opcode == ZEND_ROPE_INIT &&
- opline->result.var == -1) {
+ opline->result.var == (uint32_t)-1) {
opline->result.var = var;
}
}
@@ -7275,9 +7889,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
*result = *zend_ast_get_znode(ast);
return;
default:
- if (type == BP_VAR_W || type == BP_VAR_REF
- || type == BP_VAR_RW || type == BP_VAR_UNSET
- ) {
+ if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use temporary expression in write context");
}
@@ -7387,7 +7999,9 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
}
- zend_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]));
+ if (!zend_try_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]))) {
+ return;
+ }
break;
case ZEND_AST_COALESCE:
/* Set isset fetch indicator here, opcache disallows runtime altering of the AST */