Improved ZEND_PROXY_CALL

This commit is contained in:
Dmitry Stogov 2015-04-09 19:39:10 +03:00
parent 0c829afc53
commit 83e749ff3b
2 changed files with 184 additions and 84 deletions

View file

@ -7560,59 +7560,91 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY)
ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY)
{
zval args;
zend_array *args;
zend_function *fbc = EX(func);
zend_object *obj = Z_OBJ(EX(This));
zend_object *object = Z_OBJ(EX(This));
zval *ret = EX(return_value);
zend_call_kind call_kind = EX_CALL_KIND();
zend_class_entry *scope = EX(called_scope);
uint32_t num_args = EX_NUM_ARGS();
zend_execute_data *call, *prev_execute_data = EX(prev_execute_data);
zend_execute_data *call;
array_init_size(&args, num_args);
args = emalloc(sizeof(zend_array));
zend_hash_init(args, num_args, NULL, ZVAL_PTR_DTOR, 0);
if (num_args) {
zval *p;
zend_hash_real_init(Z_ARRVAL(args), 1);
zval *p = ZEND_CALL_ARG(execute_data, 1);
zval *end = p + num_args;
p = ZEND_CALL_ARG(execute_data, 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) {
uint32_t i;
for (i = 0; i < num_args; ++i) {
zend_hash_real_init(args, 1);
ZEND_HASH_FILL_PACKED(args) {
do {
ZEND_HASH_FILL_ADD(p);
p++;
}
} while (p != end);
} ZEND_HASH_FILL_END();
}
zend_vm_stack_free_call_frame(execute_data);
call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, obj, prev_execute_data);
SAVE_OPLINE();
call = execute_data;
execute_data = EG(current_execute_data) = EX(prev_execute_data);
zend_vm_stack_free_call_frame(call);
call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, object, execute_data);
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args);
if (call->func->type == ZEND_USER_FUNCTION) {
ZVAL_ARR(ZEND_CALL_ARG(call, 2), args);
zend_free_proxy_call_func(fbc);
fbc = call->func;
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR));
EG(scope) = fbc->common.scope;
call->symbol_table = NULL;
i_init_func_execute_data(call, &call->func->op_array,
ret, (call->func->common.fn_flags & ZEND_ACC_STATIC) == 0);
i_init_func_execute_data(call, &fbc->op_array,
ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0);
zend_free_proxy_call_func(fbc);
/* the previously call to current execute_data already check zend_execute_ex */
ZEND_VM_ENTER();
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call);
}
} else {
zval retval;
ZEND_ASSERT(call->func->type == ZEND_INTERNAL_FUNCTION);
SAVE_OPLINE();
ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION);
EG(scope) = object ? NULL : fbc->common.scope;
EG(current_execute_data) = call;
if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
uint32_t i;
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
zval *p = ZEND_CALL_ARG(call, 1);
EG(current_execute_data) = call;
for (i = 0; i < num_args; ++i) {
zend_verify_internal_arg_type(fbc, i + 1, p);
if (UNEXPECTED(EG(exception) != NULL)) {
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
if (ret) {
ZVAL_UNDEF(ret);
}
ZEND_VM_C_GOTO(proxy_call_end);
}
p++;
}
}
if (ret == NULL) {
ZVAL_NULL(&retval);
ret = &retval;
}
Z_VAR_FLAGS_P(ret) = (call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
EG(current_execute_data) = call;
Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@ -7621,9 +7653,14 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY)
zend_execute_internal(call, ret);
}
execute_data = EG(current_execute_data) = call->prev_execute_data;
#if ZEND_DEBUG
ZEND_ASSERT(
!call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
#endif
zend_free_proxy_call_func(fbc);
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
@ -7631,17 +7668,30 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY)
if (ret == &retval) {
zval_ptr_dtor(ret);
}
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
if (ret != &retval) {
zval_ptr_dtor(ret);
}
HANDLE_EXCEPTION_LEAVE();
}
LOAD_OPLINE();
ZEND_VM_INC_OPCODE();
ZEND_VM_LEAVE();
}
ZEND_VM_C_LABEL(proxy_call_end):
execute_data = EG(current_execute_data);
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type)) {
ZEND_VM_RETURN();
}
LOAD_OPLINE();
if (object) {
OBJ_RELEASE(object);
}
EG(scope) = EX(func)->op_array.scope;
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
if (RETURN_VALUE_USED(opline)) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
ZEND_VM_INTERRUPT_CHECK();
ZEND_VM_NEXT_OPCODE();
}

View file

@ -1774,59 +1774,91 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER(
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval args;
zend_array *args;
zend_function *fbc = EX(func);
zend_object *obj = Z_OBJ(EX(This));
zend_object *object = Z_OBJ(EX(This));
zval *ret = EX(return_value);
zend_call_kind call_kind = EX_CALL_KIND();
zend_class_entry *scope = EX(called_scope);
uint32_t num_args = EX_NUM_ARGS();
zend_execute_data *call, *prev_execute_data = EX(prev_execute_data);
zend_execute_data *call;
array_init_size(&args, num_args);
args = emalloc(sizeof(zend_array));
zend_hash_init(args, num_args, NULL, ZVAL_PTR_DTOR, 0);
if (num_args) {
zval *p;
zend_hash_real_init(Z_ARRVAL(args), 1);
zval *p = ZEND_CALL_ARG(execute_data, 1);
zval *end = p + num_args;
p = ZEND_CALL_ARG(execute_data, 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) {
uint32_t i;
for (i = 0; i < num_args; ++i) {
zend_hash_real_init(args, 1);
ZEND_HASH_FILL_PACKED(args) {
do {
ZEND_HASH_FILL_ADD(p);
p++;
}
} while (p != end);
} ZEND_HASH_FILL_END();
}
zend_vm_stack_free_call_frame(execute_data);
call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, obj, prev_execute_data);
SAVE_OPLINE();
call = execute_data;
execute_data = EG(current_execute_data) = EX(prev_execute_data);
zend_vm_stack_free_call_frame(call);
call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, object, execute_data);
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args);
if (call->func->type == ZEND_USER_FUNCTION) {
ZVAL_ARR(ZEND_CALL_ARG(call, 2), args);
zend_free_proxy_call_func(fbc);
fbc = call->func;
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR));
EG(scope) = fbc->common.scope;
call->symbol_table = NULL;
i_init_func_execute_data(call, &call->func->op_array,
ret, (call->func->common.fn_flags & ZEND_ACC_STATIC) == 0);
i_init_func_execute_data(call, &fbc->op_array,
ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0);
zend_free_proxy_call_func(fbc);
/* the previously call to current execute_data already check zend_execute_ex */
ZEND_VM_ENTER();
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call);
}
} else {
zval retval;
ZEND_ASSERT(call->func->type == ZEND_INTERNAL_FUNCTION);
SAVE_OPLINE();
ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION);
EG(scope) = object ? NULL : fbc->common.scope;
EG(current_execute_data) = call;
if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
uint32_t i;
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
zval *p = ZEND_CALL_ARG(call, 1);
EG(current_execute_data) = call;
for (i = 0; i < num_args; ++i) {
zend_verify_internal_arg_type(fbc, i + 1, p);
if (UNEXPECTED(EG(exception) != NULL)) {
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
if (ret) {
ZVAL_UNDEF(ret);
}
goto proxy_call_end;
}
p++;
}
}
if (ret == NULL) {
ZVAL_NULL(&retval);
ret = &retval;
}
Z_VAR_FLAGS_P(ret) = (call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
EG(current_execute_data) = call;
Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@ -1835,9 +1867,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O
zend_execute_internal(call, ret);
}
execute_data = EG(current_execute_data) = call->prev_execute_data;
#if ZEND_DEBUG
ZEND_ASSERT(
!call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
#endif
zend_free_proxy_call_func(fbc);
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
@ -1845,19 +1882,32 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O
if (ret == &retval) {
zval_ptr_dtor(ret);
}
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
if (ret != &retval) {
zval_ptr_dtor(ret);
}
HANDLE_EXCEPTION_LEAVE();
}
LOAD_OPLINE();
ZEND_VM_INC_OPCODE();
ZEND_VM_LEAVE();
}
proxy_call_end:
execute_data = EG(current_execute_data);
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type)) {
ZEND_VM_RETURN();
}
LOAD_OPLINE();
if (object) {
OBJ_RELEASE(object);
}
EG(scope) = EX(func)->op_array.scope;
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
if (RETURN_VALUE_USED(opline)) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
ZEND_VM_INTERRUPT_CHECK();
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{