Ejemplo n.º 1
0
static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
{
	if (func->type == ZEND_USER_FUNCTION
	 && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
	 && fcall->extended_value >= func->op_array.required_num_args
	 && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {

		zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;

		if (ret_opline->op1_type == IS_CONST) {
			uint32_t i, num_args = func->op_array.num_args;
			num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;

			if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
				/* TODO: we can't inlne methods, because $this may be used
				 *       not in object context ???
				 */
				return;
			}

			for (i = 0; i < num_args; i++) {
				/* Don't inline functions with by-reference arguments. This would require
				 * correct handling of INDIRECT arguments. */
				if (func->op_array.arg_info[i].pass_by_reference) {
					return;
				}
			}

			if (fcall->extended_value < func->op_array.num_args) {
				/* don't inline funcions with named constants in default arguments */
				i = fcall->extended_value;

				do {
					if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
						return;
					}
					i++;
				} while (i < func->op_array.num_args);
			}

			if (RETURN_VALUE_USED(opline)) {
				zval zv;

				ZVAL_DUP(&zv, RT_CONSTANT(&func->op_array, ret_opline->op1));
				opline->opcode = ZEND_QM_ASSIGN;
				opline->op1_type = IS_CONST;
				opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
				SET_UNUSED(opline->op2);
			} else {
				MAKE_NOP(opline);
			}

			zend_delete_call_instructions(opline-1);
		}
	}
}
Ejemplo n.º 2
0
static inline char *phpdbg_decode_op(
		zend_op_array *ops, const znode_op *op, uint32_t type) /* {{{ */
{
	char *decode = NULL;

	switch (type) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			spprintf(&decode, 0, "$%.*s%c",
				ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18,
				ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
			spprintf(&decode, 0, "@%u", EX_VAR_TO_NUM(op->var) - ops->last_var);
		break;
		case IS_TMP_VAR:
			spprintf(&decode, 0, "~%u", EX_VAR_TO_NUM(op->var) - ops->last_var);
		break;
		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			decode = phpdbg_short_zval_print(literal, 20);
		} break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 3
0
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */
{
	char *decode = NULL;

	switch (type &~ EXT_TYPE_UNUSED) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			asprintf(&decode, "$%.*s%c", ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18, ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
		case IS_TMP_VAR: {
			zend_ulong id = 0, *pid = NULL;
			if (vars != NULL) {
				if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) {
					id = *pid;
				} else {
					id = zend_hash_num_elements(vars);
					zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong));
				}
			}
			asprintf(&decode, "@" ZEND_ULONG_FMT, id);
		} break;

		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			decode = phpdbg_short_zval_print(literal, 20);
		} break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 4
0
static int coro_exit_handler(zend_execute_data *execute_data)
{
    zval ex;
    zend_object *obj;
    zend_long flags = 0;
    if (sw_get_current_cid() != -1)
    {
        flags |= SW_EXIT_IN_COROUTINE;
    }
    if (SwooleG.serv && SwooleG.serv->gs->start)
    {
        flags |= SW_EXIT_IN_SERVER;
    }
    if (flags)
    {
        const zend_op *opline = EX(opline);
        zval _exit_status;
        zval *exit_status = NULL;

        if (opline->op1_type != IS_UNUSED)
        {
            if (opline->op1_type == IS_CONST)
            {
                // see: https://github.com/php/php-src/commit/e70618aff6f447a298605d07648f2ce9e5a284f5
#ifdef EX_CONSTANT
                exit_status = EX_CONSTANT(opline->op1);
#else
                exit_status = RT_CONSTANT(opline, opline->op1);
#endif
            }
            else
            {
                exit_status = EX_VAR(opline->op1.var);
            }
            if (Z_ISREF_P(exit_status))
            {
                exit_status = Z_REFVAL_P(exit_status);
            }
        }
        else
        {
            exit_status = &_exit_status;
            ZVAL_NULL(exit_status);
        }
        obj = zend_throw_error_exception(swoole_exit_exception_class_entry_ptr, "swoole exit.", 0, E_ERROR TSRMLS_CC);
        ZVAL_OBJ(&ex, obj);
        zend_update_property_long(swoole_exit_exception_class_entry_ptr, &ex, ZEND_STRL("flags"), flags);
        Z_TRY_ADDREF_P(exit_status);
        zend_update_property(swoole_exit_exception_class_entry_ptr, &ex, ZEND_STRL("status"), exit_status);
    }

    return ZEND_USER_OPCODE_DISPATCH;
}
Ejemplo n.º 5
0
static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	zend_function *func;
	zend_op *opline, *end;

	opline = op_array->opcodes;
	end = opline + op_array->last;
	while (opline < end) {
		if (opline->opcode == ZEND_INIT_FCALL) {
			func = zend_hash_find_ptr(
				&ctx->script->function_table,
				Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
			if (func) {
				opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
			}
		}
		opline++;
	}
}
Ejemplo n.º 6
0
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */
{
	char *decode = NULL;

	switch (type &~ EXT_TYPE_UNUSED) {
		case IS_CV: {
			zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)];
			asprintf(&decode, "$%.*s%c", var->len <= 19 ? (int) var->len : 18, var->val, var->len <= 19 ? 0 : '+');
		} break;

		case IS_VAR:
		case IS_TMP_VAR: {
			zend_ulong id = 0, *pid = NULL;
			if (vars != NULL) {
				if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) {
					id = *pid;
				} else {
					id = zend_hash_num_elements(vars);
					zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong));
				}
			}
			asprintf(&decode, "@" ZEND_ULONG_FMT, id);
		} break;

		case IS_CONST: {
			zval *literal = RT_CONSTANT(ops, *op);
			switch (Z_TYPE_P(literal)) {
				case IS_UNDEF:
					decode = zend_strndup("", 0);
					break;
				case IS_NULL:
					decode = zend_strndup(ZEND_STRL("null"));
					break;
				case IS_FALSE:
					decode = zend_strndup(ZEND_STRL("false"));
					break;
				case IS_TRUE:
					decode = zend_strndup(ZEND_STRL("true"));
					break;
				case IS_LONG:
					asprintf(&decode, "%lld", Z_LVAL_P(literal));
					break;
				case IS_DOUBLE:
					asprintf(&decode, "%.*G", 14, Z_DVAL_P(literal));
					break;
				case IS_STRING: {
					int i;
					zend_string *str = php_addcslashes(Z_STR_P(literal), 0, "\\\"", 2);
					for (i = 0; i < str->len; i++) {
						if (str->val[i] < 32) {
							str->val[i] = ' ';
						}
					}
					asprintf(&decode, "\"%.*s\"%c", str->len <= 18 ? (int) str->len : 17, str->val, str->len <= 18 ? 0 : '+');
					zend_string_release(str);
					} break;
				case IS_RESOURCE:
					asprintf(&decode, "Rsrc #%d", Z_RES_HANDLE_P(literal));
					break;
				case IS_ARRAY:
					asprintf(&decode, "array(%d)", zend_hash_num_elements(Z_ARR_P(literal)));
					break;
				case IS_OBJECT: {
					zend_string *str = Z_OBJCE_P(literal)->name;
					asprintf(&decode, "%.*s%c", str->len <= 18 ? (int) str->len : 18, str->val, str->len <= 18 ? 0 : '+');
					} break;
				case IS_CONSTANT:
					decode = zend_strndup(ZEND_STRL("<constant>"));
					break;
				case IS_CONSTANT_AST:
					decode = zend_strndup(ZEND_STRL("<ast>"));
					break;
				default:
					asprintf(&decode, "unknown type: %d", Z_TYPE_P(literal));
					break;
			}
		} break;

		case IS_UNUSED:
			asprintf(&decode, "<unused>");
		break;
	}
	return decode;
} /* }}} */
Ejemplo n.º 7
0
int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
{
	uint32_t flags = 0;
	uint32_t i;
	int j;
	uint32_t *block_map;
	zend_function *fn;
	int blocks_count = 0;
	zend_basic_block *blocks;
	zval *zv;

	cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
	if (!block_map) {
		return FAILURE;
	}

	/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
	BB_START(0);
	if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->static_variables) {
		// FIXME: Really we should try to perform variable initialization
		flags |= ZEND_FUNC_TOO_DYNAMIC;
	}
	for (i = 0; i < op_array->last; i++) {
		zend_op *opline = op_array->opcodes + i;
		switch(opline->opcode) {
			case ZEND_RETURN:
			case ZEND_RETURN_BY_REF:
			case ZEND_GENERATOR_RETURN:
			case ZEND_EXIT:
			case ZEND_THROW:
				if (i + 1 < op_array->last) {
					BB_START(i + 1);
				}
				break;
			case ZEND_INCLUDE_OR_EVAL:
			case ZEND_YIELD:
			case ZEND_YIELD_FROM:
				flags |= ZEND_FUNC_TOO_DYNAMIC;
				if (stackless) {
					BB_START(i + 1);
				}
				break;
			case ZEND_DO_FCALL:
			case ZEND_DO_UCALL:
			case ZEND_DO_FCALL_BY_NAME:
				flags |= ZEND_FUNC_HAS_CALLS;
				if (stackless) {
					BB_START(i + 1);
				}
				break;
			case ZEND_DO_ICALL:
				flags |= ZEND_FUNC_HAS_CALLS;
				break;
			case ZEND_INIT_FCALL:
				if (rt_constants) {
					zv = RT_CONSTANT(op_array, opline->op2);
				} else {
					zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
				}
				if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
					if (fn->type == ZEND_INTERNAL_FUNCTION) {
						if (Z_STRLEN_P(zv) == sizeof("extract")-1 &&
						    memcmp(Z_STRVAL_P(zv), "extract", sizeof("extract")-1) == 0) {
							flags |= ZEND_FUNC_TOO_DYNAMIC;
						} else if (Z_STRLEN_P(zv) == sizeof("compact")-1 &&
						    memcmp(Z_STRVAL_P(zv), "compact", sizeof("compact")-1) == 0) {
							flags |= ZEND_FUNC_TOO_DYNAMIC;
						} else if (Z_STRLEN_P(zv) == sizeof("parse_str")-1 &&
						    memcmp(Z_STRVAL_P(zv), "parse_str", sizeof("parse_str")-1) == 0) {
							flags |= ZEND_FUNC_TOO_DYNAMIC;
						} else if (Z_STRLEN_P(zv) == sizeof("mb_parse_str")-1 &&
						    memcmp(Z_STRVAL_P(zv), "mb_parse_str", sizeof("mb_parse_str")-1) == 0) {
							flags |= ZEND_FUNC_TOO_DYNAMIC;
						} else if (Z_STRLEN_P(zv) == sizeof("get_defined_vars")-1 &&
						    memcmp(Z_STRVAL_P(zv), "get_defined_vars", sizeof("get_defined_vars")-1) == 0) {
							flags |= ZEND_FUNC_TOO_DYNAMIC;
						} else if (Z_STRLEN_P(zv) == sizeof("func_num_args")-1 &&
						    memcmp(Z_STRVAL_P(zv), "func_num_args", sizeof("func_num_args")-1) == 0) {
							flags |= ZEND_FUNC_VARARG;
						} else if (Z_STRLEN_P(zv) == sizeof("func_get_arg")-1 &&
						    memcmp(Z_STRVAL_P(zv), "func_get_arg", sizeof("func_get_arg")-1) == 0) {
							flags |= ZEND_FUNC_VARARG;
						} else if (Z_STRLEN_P(zv) == sizeof("func_get_args")-1 &&
						    memcmp(Z_STRVAL_P(zv), "func_get_args", sizeof("func_get_args")-1) == 0) {
							flags |= ZEND_FUNC_VARARG;
						}
					}
				}
				break;
			case ZEND_FAST_CALL:
				flags |= ZEND_FUNC_TOO_DYNAMIC;
				BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
				BB_START(i + 1);
				break;
			case ZEND_FAST_RET:
				flags |= ZEND_FUNC_TOO_DYNAMIC;
				if (i + 1 < op_array->last) {
					BB_START(i + 1);
				}
				break;
			case ZEND_DECLARE_ANON_CLASS:
			case ZEND_DECLARE_ANON_INHERITED_CLASS:
				BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
				BB_START(i + 1);
				break;
			case ZEND_JMP:
				BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
				if (i + 1 < op_array->last) {
					BB_START(i + 1);
				}
				break;
			case ZEND_JMPZNZ:
				BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
				BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
				if (i + 1 < op_array->last) {
					BB_START(i + 1);
				}
				break;
			case ZEND_JMPZ:
			case ZEND_JMPNZ:
			case ZEND_JMPZ_EX:
			case ZEND_JMPNZ_EX:
			case ZEND_JMP_SET:
			case ZEND_COALESCE:
			case ZEND_ASSERT_CHECK:
				BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
				BB_START(i + 1);
				break;
			case ZEND_CATCH:
				flags |= ZEND_FUNC_TOO_DYNAMIC;
				if (!opline->result.num) {
					BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
				}
				BB_START(i + 1);
				break;
			case ZEND_FE_FETCH_R:
			case ZEND_FE_FETCH_RW:
				BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
				BB_START(i + 1);
				break;
			case ZEND_FE_RESET_R:
			case ZEND_FE_RESET_RW:
			case ZEND_NEW:
				BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
				BB_START(i + 1);
				break;
			case ZEND_DECLARE_LAMBDA_FUNCTION: {
//???					zend_op_array *lambda_op_array;
//???
//???					if (rt_constants) {
//???						zv = RT_CONSTANT(op_array, opline->op1);
//???					} else {
//???						zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
//???					}
//???					if (ctx->main_script &&
//???					    (lambda_op_array = zend_hash_find_ptr(&ctx->main_script->function_table, Z_STR_P(zv))) != NULL) {
//???						if (lambda_op_array->type == ZEND_USER_FUNCTION &&
//???						    lambda_op_array->static_variables) {
//???							// FIXME: Really we should try to perform alias
//???							// analysis on variables used by the closure
//???							info->flags |= ZEND_FUNC_TOO_DYNAMIC;
//???						}
//???					} else {
//???						// FIXME: how to find the lambda function?
						flags |= ZEND_FUNC_TOO_DYNAMIC;
//???					}
				}
				break;
			case ZEND_UNSET_VAR:
				if (!(opline->extended_value & ZEND_QUICK_SET)) {
					flags |= ZEND_FUNC_TOO_DYNAMIC;
				}
				break;
			case ZEND_FETCH_R:
			case ZEND_FETCH_W:
			case ZEND_FETCH_RW:
			case ZEND_FETCH_FUNC_ARG:
			case ZEND_FETCH_IS:
			case ZEND_FETCH_UNSET:
				if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
					flags |= ZEND_FUNC_TOO_DYNAMIC;
				} else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
				            (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
				           !op_array->function_name) {
					flags |= ZEND_FUNC_TOO_DYNAMIC;
				}
				break;
		}
	}
	for (j = 0; j < op_array->last_live_range; j++) {
		BB_START(op_array->live_range[j].start);
		BB_START(op_array->live_range[j].end);
	}
	if (op_array->last_try_catch) {
		for (j = 0; j < op_array->last_try_catch; j++) {
			BB_START(op_array->try_catch_array[j].try_op);
			if (op_array->try_catch_array[j].catch_op) {
				BB_START(op_array->try_catch_array[j].catch_op);
			}
			if (op_array->try_catch_array[j].finally_op) {
				BB_START(op_array->try_catch_array[j].finally_op);
			}
			if (op_array->try_catch_array[j].finally_end) {
				BB_START(op_array->try_catch_array[j].finally_end);
			}
		}
	}

	cfg->blocks_count = blocks_count;

	/* Build CFG, Step 2: Build Array of Basic Blocks */
	cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
	if (!blocks) {
		return FAILURE;
	}

	for (i = 0, blocks_count = -1; i < op_array->last; i++) {
		if (block_map[i]) {
			if (blocks_count >= 0) {
				blocks[blocks_count].end = i - 1;
			}
			blocks_count++;
			blocks[blocks_count].flags = 0;
			blocks[blocks_count].start = i;
			blocks[blocks_count].successors[0] = -1;
			blocks[blocks_count].successors[1] = -1;
			blocks[blocks_count].predecessors_count = 0;
			blocks[blocks_count].predecessor_offset = -1;
			blocks[blocks_count].idom = -1;
			blocks[blocks_count].loop_header = -1;
			blocks[blocks_count].level = -1;
			blocks[blocks_count].children = -1;
			blocks[blocks_count].next_child = -1;
			block_map[i] = blocks_count;
		} else {
			block_map[i] = (uint32_t)-1;
		}
	}

	blocks[blocks_count].end = i - 1;
	blocks_count++;

	/* Build CFG, Step 3: Calculate successors */
	for (j = 0; j < blocks_count; j++) {
		zend_op *opline = op_array->opcodes + blocks[j].end;
		switch(opline->opcode) {
			case ZEND_FAST_RET:
			case ZEND_RETURN:
			case ZEND_RETURN_BY_REF:
			case ZEND_GENERATOR_RETURN:
			case ZEND_EXIT:
			case ZEND_THROW:
				break;
			case ZEND_DECLARE_ANON_CLASS:
			case ZEND_DECLARE_ANON_INHERITED_CLASS:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
				record_successor(blocks, j, 1, j + 1);
				break;
			case ZEND_JMP:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
				break;
			case ZEND_JMPZNZ:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
				record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
				break;
			case ZEND_JMPZ:
			case ZEND_JMPNZ:
			case ZEND_JMPZ_EX:
			case ZEND_JMPNZ_EX:
			case ZEND_JMP_SET:
			case ZEND_COALESCE:
			case ZEND_ASSERT_CHECK:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
				record_successor(blocks, j, 1, j + 1);
				break;
			case ZEND_CATCH:
				if (!opline->result.num) {
					record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
					record_successor(blocks, j, 1, j + 1);
				} else {
					record_successor(blocks, j, 0, j + 1);
				}
				break;
			case ZEND_FE_FETCH_R:
			case ZEND_FE_FETCH_RW:
				record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
				record_successor(blocks, j, 1, j + 1);
				break;
			case ZEND_FE_RESET_R:
			case ZEND_FE_RESET_RW:
			case ZEND_NEW:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
				record_successor(blocks, j, 1, j + 1);
				break;
			case ZEND_FAST_CALL:
				record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
				record_successor(blocks, j, 1, j + 1);
				break;
			default:
				record_successor(blocks, j, 0, j + 1);
				break;
		}
	}

	/* Build CFG, Step 4, Mark Reachable Basic Blocks */
	zend_mark_reachable_blocks(op_array, cfg, 0);

	if (func_flags) {
		*func_flags = flags;
	}

	return SUCCESS;
}