diff options
Diffstat (limited to 'Zend/zend_execute.c')
| -rw-r--r-- | Zend/zend_execute.c | 866 |
1 files changed, 624 insertions, 242 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 86df4f760e..8213164d24 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -75,7 +75,7 @@ static ZEND_FUNCTION(pass) { } -static const zend_internal_function zend_pass_function = { +ZEND_API const zend_internal_function zend_pass_function = { ZEND_INTERNAL_FUNCTION, /* type */ {0, 0, 0}, /* arg_flags */ 0, /* fn_flags */ @@ -86,7 +86,8 @@ static const zend_internal_function zend_pass_function = { 0, /* required_num_args */ NULL, /* arg_info */ ZEND_FN(pass), /* handler */ - NULL /* module */ + NULL, /* module */ + {NULL,NULL,NULL,NULL} /* reserved */ }; #undef zval_ptr_dtor @@ -385,6 +386,11 @@ static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_RW(const zend_exec return EX_VAR(var); } +static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_UNSET(const zend_execute_data *execute_data, uint32_t var) +{ + return EX_VAR(var); +} + static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_W(const zend_execute_data *execute_data, uint32_t var) { zval *ret = EX_VAR(var); @@ -612,7 +618,7 @@ static zend_always_inline zend_class_entry* zend_verify_arg_class_kind(const zen return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD)); } -static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg) +static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind) { zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; const char *fname = ZSTR_VAL(zf->common.function_name); @@ -640,19 +646,19 @@ static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t ar } } -static int is_null_constant(zval *default_value) +static int is_null_constant(zend_class_entry *scope, zval *default_value) { if (Z_CONSTANT_P(default_value)) { zval constant; - ZVAL_COPY_VALUE(&constant, default_value); - if (UNEXPECTED(zval_update_constant_ex(&constant, 0, NULL) != SUCCESS)) { + ZVAL_COPY(&constant, default_value); + if (UNEXPECTED(zval_update_constant_ex(&constant, scope) != SUCCESS)) { return 0; } if (Z_TYPE(constant) == IS_NULL) { return 1; } - zval_dtor(&constant); + zval_ptr_dtor(&constant); } return 0; } @@ -738,25 +744,25 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv if (cur_arg_info->class_name) { need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { - zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); return 0; } } } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { if (cur_arg_info->class_name) { need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); - zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), ""); return 0; } else if (cur_arg_info->type_hint == IS_CALLABLE) { if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { - zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), ""); return 0; } } else if (cur_arg_info->type_hint == _IS_BOOL && EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { /* pass */ } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) { - zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); return 0; } } @@ -764,6 +770,23 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv return 1; } +static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call) +{ + uint32_t i; + uint32_t num_args = ZEND_CALL_NUM_ARGS(call); + zval *p = ZEND_CALL_ARG(call, 1); + + for (i = 0; i < num_args; ++i) { + if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) { + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_args(call); + return 0; + } + p++; + } + return 1; +} + static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) { zend_arg_info *cur_arg_info; @@ -787,7 +810,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a } else { ce = zend_verify_arg_class_kind(cur_arg_info); if (UNEXPECTED(!ce)) { - zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg); + zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); return 0; } *cache_slot = (void*)ce; @@ -796,11 +819,11 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a need_msg = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "implement interface " : "be an instance of "; - zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg); + zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); return 0; } } - } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value)))) { + } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) { if (cur_arg_info->class_name) { if (EXPECTED(*cache_slot)) { ce = (zend_class_entry*)*cache_slot; @@ -808,9 +831,9 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a ce = zend_verify_arg_class_kind(cur_arg_info); if (UNEXPECTED(!ce)) { if (Z_TYPE_P(arg) == IS_OBJECT) { - zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg); + zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); } else { - zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg), arg); + zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg)); } return 0; } @@ -819,18 +842,18 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a need_msg = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "implement interface " : "be an instance of "; - zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), ""); return 0; } else if (cur_arg_info->type_hint == IS_CALLABLE) { if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { - zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), ""); return 0; } } else if (cur_arg_info->type_hint == _IS_BOOL && EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { /* pass */ } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) { - zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg); + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); return 0; } } @@ -859,7 +882,7 @@ static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, ui } else { ce = zend_verify_arg_class_kind(cur_arg_info); if (UNEXPECTED(!ce)) { - zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", "", NULL); + zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", ""); return 0; } *cache_slot = (void*)ce; @@ -867,11 +890,11 @@ static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, ui need_msg = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "implement interface " : "be an instance of "; - zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", "", NULL); + zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", ""); } else if (cur_arg_info->type_hint == IS_CALLABLE) { - zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "", NULL); + zend_verify_arg_error(zf, arg_num, "be callable", "", "none", ""); } else { - zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "", NULL); + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", ""); } return 0; } @@ -913,6 +936,7 @@ static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const ch fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); } +#if ZEND_DEBUG static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) { const char *fname = ZSTR_VAL(zf->common.function_name); @@ -949,7 +973,6 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con fclass, fsep, fname, returned_msg, returned_kind); } -#if ZEND_DEBUG static int zend_verify_internal_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; @@ -1086,35 +1109,14 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c return 1; } -static zend_never_inline void zend_assign_to_object_dim(zval *retval, zval *object, zval *property_name, int value_type, znode_op value_op, const zend_execute_data *execute_data) +static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value) { - zend_free_op free_value; - zval *value = get_zval_ptr_deref(value_type, value_op, execute_data, &free_value, BP_VAR_R); - - /* Note: property_name in this case is really the array index! */ - if (!Z_OBJ_HT_P(object)->write_dimension) { + if (UNEXPECTED(!Z_OBJ_HT_P(object)->write_dimension)) { zend_throw_error(NULL, "Cannot use object as array"); - FREE_OP(free_value); return; } - /* separate our value if necessary */ - if (value_type == IS_CONST) { - if (UNEXPECTED(Z_REFCOUNTED_P(value))) { - Z_ADDREF_P(value); - } - } - - Z_OBJ_HT_P(object)->write_dimension(object, property_name, value); - - if (retval && EXPECTED(!EG(exception))) { - ZVAL_COPY(retval, value); - } - if (value_type == IS_CONST) { - zval_ptr_dtor_nogc(value); - } else { - FREE_OP(free_value); - } + Z_OBJ_HT_P(object)->write_dimension(object, dim, value); } static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, binary_op_type binary_op) @@ -1151,16 +1153,170 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval * } } -static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *value, zval *result) +static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type) +{ + zend_long offset; + +try_again: + if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) { + switch(Z_TYPE_P(dim)) { + case IS_STRING: + if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) { + break; + } + if (type != BP_VAR_UNSET) { + zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim)); + } + break; + case IS_UNDEF: + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + case IS_DOUBLE: + case IS_NULL: + case IS_FALSE: + case IS_TRUE: + zend_error(E_NOTICE, "String offset cast occurred"); + break; + case IS_REFERENCE: + dim = Z_REFVAL_P(dim); + goto try_again; + default: + zend_error(E_WARNING, "Illegal offset type"); + break; + } + + offset = _zval_get_long_func(dim); + } else { + offset = Z_LVAL_P(dim); + } + + return offset; +} + +static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void) +{ + const char *msg = NULL; + const zend_op *opline = EG(current_execute_data)->opline; + const zend_op *end; + uint32_t var; + + switch (opline->opcode) { + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_POW: + msg = "Cannot use assign-op operators with string offsets"; + break; + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_DIM_UNSET: + /* TODO: Encode the "reason" into opline->extended_value??? */ + var = opline->result.var; + opline++; + end = EG(current_execute_data)->func->op_array.opcodes + + EG(current_execute_data)->func->op_array.last; + while (opline < end) { + if (opline->op1_type == IS_VAR && opline->op1.var == var) { + switch (opline->opcode) { + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_POW: + if (opline->extended_value == ZEND_ASSIGN_OBJ) { + msg = "Cannot use string offset as an object"; + } else if (opline->extended_value == ZEND_ASSIGN_DIM) { + msg = "Cannot use string offset as an array"; + } else { + msg = "Cannot use assign-op operators with string offsets"; + } + break; + case ZEND_PRE_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: + msg = "Cannot increment/decrement string offsets"; + break; + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_DIM_UNSET: + case ZEND_ASSIGN_DIM: + msg = "Cannot use string offset as an array"; + break; + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_ASSIGN_OBJ: + msg = "Cannot use string offset as an object"; + break; + case ZEND_ASSIGN_REF: + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_INIT_ARRAY: + msg = "Cannot create references to/from string offsets"; + break; + case ZEND_RETURN_BY_REF: + msg = "Cannot return string offsets by reference"; + break; + case ZEND_UNSET_DIM: + case ZEND_UNSET_OBJ: + msg = "Cannot unset string offsets"; + break; + case ZEND_YIELD: + msg = "Cannot yield string offsets by reference"; + break; + case ZEND_SEND_REF: + case ZEND_SEND_VAR_EX: + msg = "Only variables can be passed by reference"; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + } + if (opline->op2_type == IS_VAR && opline->op2.var == var) { + ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF); + msg = "Cannot create references to/from string offsets"; + break; + } + } + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + ZEND_ASSERT(msg != NULL); + zend_throw_error(NULL, msg); +} + +static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result) { zend_string *old_str; zend_uchar c; size_t string_len; + zend_long offset; + offset = zend_check_string_offset(dim, BP_VAR_W); if (offset < (zend_long)(-Z_STRLEN_P(str))) { /* Error on negative offset */ zend_error(E_WARNING, "Illegal string offset: " ZEND_LONG_FMT, offset); - zend_string_release(Z_STR_P(str)); if (result) { ZVAL_NULL(result); } @@ -1182,7 +1338,6 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu if (string_len == 0) { /* Error on empty input string */ zend_error(E_WARNING, "Cannot assign an empty string to a string offset"); - zend_string_release(Z_STR_P(str)); if (result) { ZVAL_NULL(result); } @@ -1193,7 +1348,6 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu offset += (zend_long)Z_STRLEN_P(str); } - old_str = Z_STR_P(str); if ((size_t)offset >= Z_STRLEN_P(str)) { /* Extend string if needed */ zend_long old_len = Z_STRLEN_P(str); @@ -1202,13 +1356,16 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len); Z_STRVAL_P(str)[offset+1] = 0; } else if (!Z_REFCOUNTED_P(str)) { + old_str = Z_STR_P(str); Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0); Z_TYPE_INFO_P(str) = IS_STRING_EX; + zend_string_release(old_str); + } else { + SEPARATE_STRING(str); } Z_STRVAL_P(str)[offset] = c; - zend_string_release(old_str); if (result) { /* Return the new character */ if (CG(one_char_string)[c]) { @@ -1384,7 +1541,7 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(zend_execute_d ht = &EG(symbol_table); } else { ZEND_ASSERT(fetch_type == ZEND_FETCH_LOCAL); - if (!EX(symbol_table)) { + if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_rebuild_symbol_table(); } ht = EX(symbol_table); @@ -1472,6 +1629,9 @@ str_index: } } else { switch (Z_TYPE_P(dim)) { + case IS_UNDEF: + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + /* break missing intentionally */ case IS_NULL: offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; @@ -1500,169 +1660,24 @@ str_index: return retval; } -static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type) +static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W(HashTable *ht, const zval *dim) { - zend_long offset; - -try_again: - if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) { - switch(Z_TYPE_P(dim)) { - case IS_STRING: - if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) { - break; - } - if (type != BP_VAR_UNSET) { - zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim)); - } - break; - case IS_DOUBLE: - case IS_NULL: - case IS_FALSE: - case IS_TRUE: - zend_error(E_NOTICE, "String offset cast occurred"); - break; - case IS_REFERENCE: - dim = Z_REFVAL_P(dim); - goto try_again; - default: - zend_error(E_WARNING, "Illegal offset type"); - break; - } - - offset = zval_get_long(dim); - } else { - offset = Z_LVAL_P(dim); - } - - return offset; + return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_W); } -static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void) +static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W_CONST(HashTable *ht, const zval *dim) { - const char *msg = NULL; - const zend_op *opline = EG(current_execute_data)->opline; - const zend_op *end; - uint32_t var; - - switch (opline->opcode) { - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - msg = "Cannot use assign-op operators with string offsets"; - break; - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - /* TODO: Encode the "reason" into opline->extended_value??? */ - var = opline->result.var; - opline++; - end = EG(current_execute_data)->func->op_array.opcodes + - EG(current_execute_data)->func->op_array.last; - while (opline < end) { - if (opline->op1_type == IS_VAR && opline->op1.var == var) { - switch (opline->opcode) { - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_POW: - if (opline->extended_value == ZEND_ASSIGN_OBJ) { - msg = "Cannot use string offset as an object"; - } else if (opline->extended_value == ZEND_ASSIGN_DIM) { - msg = "Cannot use string offset as an array"; - } else { - msg = "Cannot use assign-op operators with string offsets"; - } - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - msg = "Cannot increment/decrement string offsets"; - break; - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_ASSIGN_DIM: - msg = "Cannot use string offset as an array"; - break; - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_ASSIGN_OBJ: - msg = "Cannot use string offset as an object"; - break; - case ZEND_ASSIGN_REF: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_INIT_ARRAY: - msg = "Cannot create references to/from string offsets"; - break; - case ZEND_RETURN_BY_REF: - msg = "Cannot return string offsets by reference"; - break; - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - msg = "Cannot unset string offsets"; - break; - case ZEND_YIELD: - msg = "Cannot yield string offsets by reference"; - break; - case ZEND_SEND_REF: - case ZEND_SEND_VAR_EX: - msg = "Only variables can be passed by reference"; - break; - EMPTY_SWITCH_DEFAULT_CASE(); - } - break; - } - if (opline->op2_type == IS_VAR && opline->op2.var == var) { - ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF); - msg = "Cannot create references to/from string offsets"; - break; - } - } - break; - EMPTY_SWITCH_DEFAULT_CASE(); - } - ZEND_ASSERT(msg != NULL); - zend_throw_error(NULL, msg); + return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_W); } -static zend_always_inline zend_long zend_fetch_string_offset(zval *container, zval *dim, int type) +static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW(HashTable *ht, const zval *dim) { - zend_long offset = zend_check_string_offset(dim, type); + return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_RW); +} - if (Z_REFCOUNTED_P(container)) { - if (Z_REFCOUNT_P(container) > 1) { - Z_DELREF_P(container); - zval_copy_ctor_func(container); - } - Z_ADDREF_P(container); - } - return offset; +static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW_CONST(HashTable *ht, const zval *dim) +{ + return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_RW); } static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *container, zval *dim, int dim_type, int type) @@ -1712,6 +1727,10 @@ convert_to_array: } ZVAL_ERROR(result); } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + dim = &EG(uninitialized_zval); + } if (!Z_OBJ_HT_P(container)->read_dimension) { zend_throw_error(NULL, "Cannot use object as array"); ZVAL_ERROR(result); @@ -1750,22 +1769,30 @@ convert_to_array: ZVAL_ERROR(result); } } - } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { - if (type != BP_VAR_UNSET) { - goto convert_to_array; - } else { - /* for read-mode only */ - ZVAL_NULL(result); - } - } else if (EXPECTED(Z_ISERROR_P(container))) { - ZVAL_ERROR(result); } else { - if (type == BP_VAR_UNSET) { - zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); - ZVAL_NULL(result); - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data)); + } + if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + } + if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + if (type != BP_VAR_UNSET) { + goto convert_to_array; + } else { + /* for read-mode only */ + ZVAL_NULL(result); + } + } else if (EXPECTED(Z_ISERROR_P(container))) { ZVAL_ERROR(result); + } else { + if (type == BP_VAR_UNSET) { + zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); + ZVAL_NULL(result); + } else { + zend_error(E_WARNING, "Cannot use a scalar value as an array"); + ZVAL_ERROR(result); + } } } } @@ -1785,7 +1812,7 @@ static zend_never_inline void zend_fetch_dimension_address_UNSET(zval *result, z zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_UNSET); } -static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type) +static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type, int support_strings) { zval *retval; @@ -1800,7 +1827,7 @@ try_array: goto try_array; } } - if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { + if (support_strings && EXPECTED(Z_TYPE_P(container) == IS_STRING)) { zend_long offset; try_string_offset: @@ -1817,6 +1844,8 @@ try_string_offset: } zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim)); break; + case IS_UNDEF: + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); case IS_DOUBLE: case IS_NULL: case IS_FALSE: @@ -1833,7 +1862,7 @@ try_string_offset: break; } - offset = zval_get_long(dim); + offset = _zval_get_long_func(dim); } else { offset = Z_LVAL_P(dim); } @@ -1860,6 +1889,10 @@ try_string_offset: } } } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + dim = &EG(uninitialized_zval); + } if (!Z_OBJ_HT_P(container)->read_dimension) { zend_throw_error(NULL, "Cannot use object as array"); ZVAL_NULL(result); @@ -1876,18 +1909,29 @@ try_string_offset: } } } else { + if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data)); + } + if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data)); + } ZVAL_NULL(result); } } static zend_never_inline void zend_fetch_dimension_address_read_R(zval *result, zval *container, zval *dim, int dim_type) { - zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R); + zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 1); } static zend_never_inline void zend_fetch_dimension_address_read_IS(zval *result, zval *container, zval *dim, int dim_type) { - zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS); + zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1); +} + +static zend_never_inline void zend_fetch_dimension_address_read_LIST(zval *result, zval *container, zval *dim) +{ + zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, BP_VAR_R, 0); } ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim) @@ -1895,6 +1939,12 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval * zend_fetch_dimension_address_read_R(result, container, dim, IS_TMP_VAR); } +ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type) +{ + zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1); +} + + static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) { if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -2057,16 +2107,17 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ } /* }}} */ -#ifdef ZEND_WIN32 -# define ZEND_VM_INTERRUPT_CHECK() do { \ - if (EG(timed_out)) { \ - zend_timeout(0); \ +static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interrupt(void) /* {{{ */ +{ + zend_timeout(0); +} +/* }}} */ + +#define ZEND_VM_INTERRUPT_CHECK() do { \ + if (UNEXPECTED(EG(timed_out))) { \ + zend_interrupt(); \ } \ } while (0) -#else -# define ZEND_VM_INTERRUPT_CHECK() do { \ - } while (0) -#endif /* * Stack Frame Layout (the whole stack frame is allocated at once) @@ -2155,7 +2206,6 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; - ZEND_VM_INTERRUPT_CHECK(); } /* }}} */ @@ -2192,7 +2242,6 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; - ZEND_VM_INTERRUPT_CHECK(); } /* }}} */ @@ -2204,7 +2253,7 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da EX(call) = NULL; EX(return_value) = return_value; - if (UNEXPECTED(EX(symbol_table) != NULL)) { + if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) { if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) { GC_REFCOUNT(Z_OBJ(EX(This)))++; if (!zend_hash_str_add(EX(symbol_table), "this", sizeof("this")-1, &EX(This))) { @@ -2283,7 +2332,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; - ZEND_VM_INTERRUPT_CHECK(); } /* }}} */ @@ -2334,8 +2382,6 @@ ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data } while (arg_src != end); } - EX(symbol_table) = NULL; - if (UNEXPECTED(!op_array->run_time_cache)) { init_func_run_time_cache(op_array); } @@ -2614,6 +2660,342 @@ static void zend_swap_operands(zend_op *op) /* {{{ */ } /* }}} */ +static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_string *function, uint32_t num_args) /* {{{ */ +{ + zend_function *fbc; + zval *func; + zend_class_entry *called_scope; + zend_string *lcname; + const char *colon; + + if ((colon = zend_memrchr(ZSTR_VAL(function), ':', ZSTR_LEN(function))) != NULL && + colon > ZSTR_VAL(function) && + *(colon-1) == ':' + ) { + zend_string *mname; + size_t cname_length = colon - ZSTR_VAL(function) - 1; + size_t mname_length = ZSTR_LEN(function) - cname_length - (sizeof("::") - 1); + + lcname = zend_string_init(ZSTR_VAL(function), cname_length, 0); + + called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(called_scope == NULL)) { + zend_string_release(lcname); + return NULL; + } + + mname = zend_string_init(ZSTR_VAL(function) + (cname_length + sizeof("::") - 1), mname_length, 0); + + if (called_scope->get_static_method) { + fbc = called_scope->get_static_method(called_scope, mname); + } else { + fbc = zend_std_get_static_method(called_scope, mname, NULL); + } + if (UNEXPECTED(fbc == NULL)) { + if (EXPECTED(!EG(exception))) { + zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname)); + } + zend_string_release(lcname); + zend_string_release(mname); + return NULL; + } + + zend_string_release(lcname); + zend_string_release(mname); + + if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) { + if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + zend_error(E_DEPRECATED, + "Non-static method %s::%s() should not be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + if (UNEXPECTED(EG(exception) != NULL)) { + return NULL; + } + } else { + zend_throw_error( + zend_ce_error, + "Non-static method %s::%s() cannot be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + return NULL; + } + } + } else { + if (ZSTR_VAL(function)[0] == '\\') { + lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0); + zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(function) + 1, ZSTR_LEN(function) - 1); + } else { + lcname = zend_string_tolower(function); + } + if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) { + zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function)); + zend_string_release(lcname); + return NULL; + } + zend_string_release(lcname); + + fbc = Z_FUNC_P(func); + called_scope = NULL; + } + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + init_func_run_time_cache(&fbc->op_array); + } + + return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, + fbc, num_args, called_scope, NULL); +} +/* }}} */ + +static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *function, uint32_t num_args) /* {{{ */ +{ + zend_function *fbc; + zend_class_entry *called_scope; + zend_object *object; + uint32_t call_info = ZEND_CALL_NESTED_FUNCTION; + + if (EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)) && + EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)(function, &called_scope, &fbc, &object) == SUCCESS)) { + + if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) { + /* Delay closure destruction until its invocation */ + ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT); + GC_REFCOUNT((zend_object*)fbc->common.prototype)++; + call_info |= ZEND_CALL_CLOSURE; + } else if (object) { + call_info |= ZEND_CALL_RELEASE_THIS; + GC_REFCOUNT(object)++; /* For $this pointer */ + } + } else { + zend_throw_error(NULL, "Function name must be a string"); + return NULL; + } + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + init_func_run_time_cache(&fbc->op_array); + } + + return zend_vm_stack_push_call_frame(call_info, + fbc, num_args, called_scope, object); +} +/* }}} */ + +static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_array *function, uint32_t num_args) /* {{{ */ +{ + zend_function *fbc; + zend_class_entry *called_scope; + zend_object *object; + uint32_t call_info = ZEND_CALL_NESTED_FUNCTION; + + if (zend_hash_num_elements(function) == 2) { + zval *obj; + zval *method; + obj = zend_hash_index_find(function, 0); + method = zend_hash_index_find(function, 1); + + if (UNEXPECTED(!obj) || UNEXPECTED(!method)) { + zend_throw_error(NULL, "Array callback has to contain indices 0 and 1"); + return NULL; + } + + ZVAL_DEREF(obj); + if (UNEXPECTED(Z_TYPE_P(obj) != IS_STRING) && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT)) { + zend_throw_error(NULL, "First array member is not a valid class name or object"); + return NULL; + } + + ZVAL_DEREF(method); + if (UNEXPECTED(Z_TYPE_P(method) != IS_STRING)) { + zend_throw_error(NULL, "Second array member is not a valid method"); + return NULL; + } + + if (Z_TYPE_P(obj) == IS_STRING) { + object = NULL; + called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(called_scope == NULL)) { + return NULL; + } + + if (called_scope->get_static_method) { + fbc = called_scope->get_static_method(called_scope, Z_STR_P(method)); + } else { + fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL); + } + if (UNEXPECTED(fbc == NULL)) { + if (EXPECTED(!EG(exception))) { + zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method)); + } + return NULL; + } + if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { + if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + zend_error(E_DEPRECATED, + "Non-static method %s::%s() should not be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + if (UNEXPECTED(EG(exception) != NULL)) { + return NULL; + } + } else { + zend_throw_error( + zend_ce_error, + "Non-static method %s::%s() cannot be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + return NULL; + } + } + } else { + called_scope = Z_OBJCE_P(obj); + object = Z_OBJ_P(obj); + + fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL); + if (UNEXPECTED(fbc == NULL)) { + if (EXPECTED(!EG(exception))) { + zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method)); + } + return NULL; + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + object = NULL; + } else { + call_info |= ZEND_CALL_RELEASE_THIS; + GC_REFCOUNT(object)++; /* For $this pointer */ + } + } + } else { + zend_throw_error(NULL, "Function name must be a string"); + return NULL; + } + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + init_func_run_time_cache(&fbc->op_array); + } + + return zend_vm_stack_push_call_frame(call_info, + fbc, num_args, called_scope, object); +} +/* }}} */ + +#define ZEND_FAKE_OP_ARRAY ((zend_op_array*)(zend_intptr_t)-1) + +static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type) /* {{{ */ +{ + zend_op_array *new_op_array = NULL; + zval tmp_inc_filename; + + ZVAL_UNDEF(&tmp_inc_filename); + if (Z_TYPE_P(inc_filename) != IS_STRING) { + ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); + inc_filename = &tmp_inc_filename; + } + + if (type != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) { + if (type == ZEND_INCLUDE_ONCE || type == ZEND_INCLUDE) { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename)); + } else { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename)); + } + } else { + switch (type) { + case ZEND_INCLUDE_ONCE: + case ZEND_REQUIRE_ONCE: { + zend_file_handle file_handle; + zend_string *resolved_path; + + resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename)); + if (resolved_path) { + if (zend_hash_exists(&EG(included_files), resolved_path)) { + goto already_compiled; + } + } else { + resolved_path = zend_string_copy(Z_STR_P(inc_filename)); + } + + if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) { + + if (!file_handle.opened_path) { + file_handle.opened_path = zend_string_copy(resolved_path); + } + + if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) { + zend_op_array *op_array = zend_compile_file(&file_handle, (type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE)); + zend_destroy_file_handle(&file_handle); + zend_string_release(resolved_path); + if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) { + zend_string_release(Z_STR(tmp_inc_filename)); + } + return op_array; + } else { + zend_file_handle_dtor(&file_handle); +already_compiled: + new_op_array = ZEND_FAKE_OP_ARRAY; + } + } else { + if (type == ZEND_INCLUDE_ONCE) { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename)); + } else { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename)); + } + } + zend_string_release(resolved_path); + } + break; + case ZEND_INCLUDE: + case ZEND_REQUIRE: + new_op_array = compile_filename(type, inc_filename); + break; + case ZEND_EVAL: { + char *eval_desc = zend_make_compiled_string_description("eval()'d code"); + new_op_array = zend_compile_string(inc_filename, eval_desc); + efree(eval_desc); + } + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } + if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) { + zend_string_release(Z_STR(tmp_inc_filename)); + } + return new_op_array; +} +/* }}} */ + +static zend_never_inline int zend_do_fcall_overloaded(zend_function *fbc, zend_execute_data *call, zval *ret) /* {{{ */ +{ + zend_object *object; + + /* Not sure what should be done here if it's a static method */ + if (UNEXPECTED(Z_TYPE(call->This) != IS_OBJECT)) { + zend_vm_stack_free_args(call); + if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { + zend_string_release(fbc->common.function_name); + } + efree(fbc); + zend_vm_stack_free_call_frame(call); + + zend_throw_error(NULL, "Cannot call overloaded function for non-object"); + return 0; + } + + object = Z_OBJ(call->This); + + ZVAL_NULL(ret); + + EG(current_execute_data) = call; + object->handlers->call_method(fbc->common.function_name, object, call, ret); + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + + if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { + zend_string_release(fbc->common.function_name); + } + efree(fbc); + + return 1; +} +/* }}} */ + #ifdef HAVE_GCC_GLOBAL_REGS # if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386) # define ZEND_VM_FP_GLOBAL_REG "%esi" |
