From 7dfb4f6b38d9efedafab7d2d98b9333715561256 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 14 Jul 2014 10:33:11 +0400 Subject: [PATCH] Implemented builtin instruction for type check functions - is_*() --- Zend/zend_compile.c | 112 +++++++++++++++++++ Zend/zend_vm_def.h | 51 +++++++++ Zend/zend_vm_execute.h | 244 +++++++++++++++++++++++++++++++++++++---- Zend/zend_vm_opcodes.c | 2 +- Zend/zend_vm_opcodes.h | 1 + 5 files changed, 389 insertions(+), 21 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 56d0d979cd..90d36b19dc 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2751,6 +2751,54 @@ static int zend_do_convert_strlen(zend_op *init_opline, znode *result TSRMLS_DC) } /* }}} */ +static int zend_do_convert_type_check(zend_op *init_opline, znode *result, zend_uint type TSRMLS_DC) /* {{{ */ +{ + zend_op *opline = init_opline + 1; + zend_op *send = NULL; + int level = 0; + + do { + switch (opline->opcode) { + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + if (level == 0) { + if (opline->opcode == ZEND_SEND_UNPACK) { + return 0; + } + send = opline; + } + break; + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_NEW: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_FCALL: + level++; + break; + case ZEND_DO_FCALL: + level--; + break; + } + opline++; + } while (send == NULL); + + MAKE_NOP(init_opline); + send->opcode = ZEND_TYPE_CHECK; + send->extended_value = type; + SET_UNUSED(send->op2); + send->result.var = get_temporary_variable(CG(active_op_array)); + send->result_type = IS_TMP_VAR; + GET_NODE(result, send->result); + return 1; +} +/* }}} */ + void zend_do_end_function_call(znode *function_name, znode *result, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2797,6 +2845,70 @@ void zend_do_end_function_call(znode *function_name, znode *result, int is_metho return; } } + } else if (func->common.function_name->len == sizeof("is_null")-1 && + memcmp(func->common.function_name->val, "is_null", sizeof("is_null")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_NULL)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_bool")-1 && + memcmp(func->common.function_name->val, "is_bool", sizeof("is_bool")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, _IS_BOOL)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_long")-1 && + memcmp(func->common.function_name->val, "is_long", sizeof("is_long")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_LONG)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_float")-1 && + memcmp(func->common.function_name->val, "is_float", sizeof("is_float")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_DOUBLE)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_string")-1 && + memcmp(func->common.function_name->val, "is_string", sizeof("is_string")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_STRING)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_array")-1 && + memcmp(func->common.function_name->val, "is_array", sizeof("is_array")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_ARRAY)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_object")-1 && + memcmp(func->common.function_name->val, "is_object", sizeof("is_object")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_OBJECT)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } + } else if (func->common.function_name->len == sizeof("is_resouce")-1 && + memcmp(func->common.function_name->val, "is_resource", sizeof("is_resource")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_type_check(opline, result, IS_RESOURCE)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } } } } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index dfe6651f3b..93c6795fb9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5884,4 +5884,55 @@ ZEND_VM_C_LABEL(strlen_error): ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + switch (opline->extended_value) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_ARRAY: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == opline->extended_value); + break; + case _IS_BOOL: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE); + break; + case IS_OBJECT: + if (Z_TYPE_P(value) == opline->extended_value) { + if (Z_OBJ_HT_P(value)->get_class_entry == NULL) { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } else { + zend_class_entry *ce = Z_OBJCE_P(value); + if (ce->name->len == sizeof("__PHP_Incomplete_Class") - 1 + && !strncmp(ce->name->val, "__PHP_Incomplete_Class", ce->name->len)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } else { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } + } + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + break; + case IS_RESOURCE: + if (Z_TYPE_P(value) == opline->extended_value) { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value) TSRMLS_CC); + ZVAL_BOOL(EX_VAR(opline->result.var), type_name != NULL); + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + EMPTY_SWITCH_DEFAULT_CASE() + + } + FREE_OP1(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_EXPORT_HANDLER(zend_do_fcall, ZEND_DO_FCALL) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ecd15ffe9a..dab0fc5e5e 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3399,6 +3399,57 @@ strlen_error: ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + + + SAVE_OPLINE(); + value = opline->op1.zv; + switch (opline->extended_value) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_ARRAY: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == opline->extended_value); + break; + case _IS_BOOL: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE); + break; + case IS_OBJECT: + if (Z_TYPE_P(value) == opline->extended_value) { + if (Z_OBJ_HT_P(value)->get_class_entry == NULL) { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } else { + zend_class_entry *ce = Z_OBJCE_P(value); + if (ce->name->len == sizeof("__PHP_Incomplete_Class") - 1 + && !strncmp(ce->name->val, "__PHP_Incomplete_Class", ce->name->len)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } else { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } + } + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + break; + case IS_RESOURCE: + if (Z_TYPE_P(value) == opline->extended_value) { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value) TSRMLS_CC); + ZVAL_BOOL(EX_VAR(opline->result.var), type_name != NULL); + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + EMPTY_SWITCH_DEFAULT_CASE() + + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8880,6 +8931,57 @@ strlen_error: ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + switch (opline->extended_value) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_ARRAY: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == opline->extended_value); + break; + case _IS_BOOL: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE); + break; + case IS_OBJECT: + if (Z_TYPE_P(value) == opline->extended_value) { + if (Z_OBJ_HT_P(value)->get_class_entry == NULL) { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } else { + zend_class_entry *ce = Z_OBJCE_P(value); + if (ce->name->len == sizeof("__PHP_Incomplete_Class") - 1 + && !strncmp(ce->name->val, "__PHP_Incomplete_Class", ce->name->len)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } else { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } + } + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + break; + case IS_RESOURCE: + if (Z_TYPE_P(value) == opline->extended_value) { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value) TSRMLS_CC); + ZVAL_BOOL(EX_VAR(opline->result.var), type_name != NULL); + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + EMPTY_SWITCH_DEFAULT_CASE() + + } + zval_dtor(free_op1.var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -14377,6 +14479,57 @@ strlen_error: ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + switch (opline->extended_value) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_ARRAY: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == opline->extended_value); + break; + case _IS_BOOL: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE); + break; + case IS_OBJECT: + if (Z_TYPE_P(value) == opline->extended_value) { + if (Z_OBJ_HT_P(value)->get_class_entry == NULL) { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } else { + zend_class_entry *ce = Z_OBJCE_P(value); + if (ce->name->len == sizeof("__PHP_Incomplete_Class") - 1 + && !strncmp(ce->name->val, "__PHP_Incomplete_Class", ce->name->len)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } else { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } + } + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + break; + case IS_RESOURCE: + if (Z_TYPE_P(value) == opline->extended_value) { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value) TSRMLS_CC); + ZVAL_BOOL(EX_VAR(opline->result.var), type_name != NULL); + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + EMPTY_SWITCH_DEFAULT_CASE() + + } + zval_ptr_dtor_nogc(free_op1.var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -31488,6 +31641,57 @@ strlen_error: ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + + + SAVE_OPLINE(); + value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); + switch (opline->extended_value) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_ARRAY: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == opline->extended_value); + break; + case _IS_BOOL: + ZVAL_BOOL(EX_VAR(opline->result.var), Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE); + break; + case IS_OBJECT: + if (Z_TYPE_P(value) == opline->extended_value) { + if (Z_OBJ_HT_P(value)->get_class_entry == NULL) { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } else { + zend_class_entry *ce = Z_OBJCE_P(value); + if (ce->name->len == sizeof("__PHP_Incomplete_Class") - 1 + && !strncmp(ce->name->val, "__PHP_Incomplete_Class", ce->name->len)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } else { + ZVAL_TRUE(EX_VAR(opline->result.var)); + } + } + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + break; + case IS_RESOURCE: + if (Z_TYPE_P(value) == opline->extended_value) { + const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value) TSRMLS_CC); + ZVAL_BOOL(EX_VAR(opline->result.var), type_name != NULL); + } else { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + EMPTY_SWITCH_DEFAULT_CASE() + + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -43821,31 +44025,31 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_TYPE_CHECK_SPEC_CONST_HANDLER, + ZEND_TYPE_CHECK_SPEC_CONST_HANDLER, + ZEND_TYPE_CHECK_SPEC_CONST_HANDLER, + ZEND_TYPE_CHECK_SPEC_CONST_HANDLER, + ZEND_TYPE_CHECK_SPEC_CONST_HANDLER, + ZEND_TYPE_CHECK_SPEC_TMP_HANDLER, + ZEND_TYPE_CHECK_SPEC_TMP_HANDLER, + ZEND_TYPE_CHECK_SPEC_TMP_HANDLER, + ZEND_TYPE_CHECK_SPEC_TMP_HANDLER, + ZEND_TYPE_CHECK_SPEC_TMP_HANDLER, + ZEND_TYPE_CHECK_SPEC_VAR_HANDLER, + ZEND_TYPE_CHECK_SPEC_VAR_HANDLER, + ZEND_TYPE_CHECK_SPEC_VAR_HANDLER, + ZEND_TYPE_CHECK_SPEC_VAR_HANDLER, + ZEND_TYPE_CHECK_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_TYPE_CHECK_SPEC_CV_HANDLER, + ZEND_TYPE_CHECK_SPEC_CV_HANDLER, + ZEND_TYPE_CHECK_SPEC_CV_HANDLER, + ZEND_TYPE_CHECK_SPEC_CV_HANDLER, + ZEND_TYPE_CHECK_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 7ae371e068..f99a34a021 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -145,7 +145,7 @@ const char *zend_vm_opcodes_map[169] = { "ZEND_SEND_USER", "ZEND_STRLEN", NULL, - NULL, + "ZEND_TYPE_CHECK", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 1c20fc6228..c4be566726 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -145,6 +145,7 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode); #define ZEND_SEND_ARRAY 119 #define ZEND_SEND_USER 120 #define ZEND_STRLEN 121 +#define ZEND_TYPE_CHECK 123 #define ZEND_PRE_INC_OBJ 132 #define ZEND_PRE_DEC_OBJ 133 #define ZEND_POST_INC_OBJ 134 -- 2.40.0