Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
237 lines (207 sloc) 6.34 KB
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#define USE_OPLINE const zend_op *opline = EX(opline);
#define GET_OP1_ZVAL_PTR_UNDEF(fetch) \
get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, execute_data)
#define GET_OP2_ZVAL_PTR_UNDEF(fetch) \
get_zval_ptr_undef(opline->op2_type, opline->op2, &free_op2, execute_data)
#define FREE_OP1 if (free_op1) { zval_ptr_dtor_nogc(free_op1); }
#define FREE_OP2 if (free_op2) { zval_ptr_dtor_nogc(free_op2); }
static
zval *get_zval_ptr_undef(zend_uchar op_type, znode_op op, zend_free_op *free_op,
zend_execute_data *execute_data) {
switch (op_type) {
case IS_TMP_VAR:
case IS_VAR: return (*free_op = EX_VAR(op.var));
case IS_CONST: return EX_CONSTANT(op);
case IS_CV: return EX_VAR(op.var);
default: return NULL;
}
}
/* ----------------------------------------------------------------------- */
#define UNARY_OPS(X) \
X(BW_NOT, __bw_not)
#define BINARY_OPS(X) \
X(ADD, __add) \
X(SUB, __sub) \
X(MUL, __mul) \
X(DIV, __div) \
X(MOD, __mod) \
X(POW, __pow) \
X(SL, __sl) \
X(SR, __sr) \
X(CONCAT, __concat) \
X(BW_OR, __bw_or) \
X(BW_AND, __bw_and) \
X(BW_XOR, __bw_xor) \
X(IS_IDENTICAL, __is_identical) \
X(IS_NOT_IDENTICAL, __is_not_identical) \
X(IS_EQUAL, __is_equal) \
X(IS_NOT_EQUAL, __is_not_equal) \
X(IS_SMALLER, __is_smaller) \
X(IS_SMALLER_OR_EQUAL, __is_smaller_or_equal) \
X(SPACESHIP, __cmp)
#define UNARY_ASSIGN_OPS(X) \
X(PRE_INC, __pre_inc) \
X(POST_INC, __post_inc) \
X(PRE_DEC, __pre_dec) \
X(POST_DEC, __post_dec)
#define BINARY_ASSIGN_OPS(X) \
X(ASSIGN, __assign) \
X(ASSIGN_ADD, __assign_add) \
X(ASSIGN_SUB, __assign_sub) \
X(ASSIGN_MUL, __assign_mul) \
X(ASSIGN_DIV, __assign_div) \
X(ASSIGN_MOD, __assign_mod) \
X(ASSIGN_POW, __assign_pow) \
X(ASSIGN_SL, __assign_sl) \
X(ASSIGN_SR, __assign_sr) \
X(ASSIGN_CONCAT, __assign_concat) \
X(ASSIGN_BW_OR, __assign_bw_or) \
X(ASSIGN_BW_AND, __assign_bw_and) \
X(ASSIGN_BW_XOR, __assign_bw_xor)
#define ALL_OPS(X) \
UNARY_OPS(X) \
BINARY_OPS(X) \
UNARY_ASSIGN_OPS(X) \
BINARY_ASSIGN_OPS(X)
/* greater/greater-equal are encoded as smaller/smaller-equal
* so they require special handling
*/
#define GREATER_OPS(X) \
X(IS_SMALLER, __is_greater) \
X(IS_SMALLER_OR_EQUAL, __is_greater_or_equal)
#define X(op, meth) static zend_string *s_##meth;
ALL_OPS(X)
GREATER_OPS(X)
#undef X
/* {{{ operator_method_name */
static inline zend_string* operator_method_name(zend_uchar opcode) {
switch (opcode) {
#define X(op, meth) case ZEND_##op: return s_##meth;
ALL_OPS(X)
#undef X
default:
ZEND_ASSERT(0);
return NULL;
}
}
/* }}} */
/* {{{ operator_get_method */
static zend_bool operator_get_method(zend_string *method, zval *obj,
zend_fcall_info *fci,
zend_fcall_info_cache *fcc) {
memset(fci, 0, sizeof(zend_fcall_info));
fci->size = sizeof(zend_fcall_info);
fci->object = Z_OBJ_P(obj);
ZVAL_STR(&(fci->function_name), method);
if (!zend_is_callable_ex(&(fci->function_name), fci->object,
IS_CALLABLE_CHECK_SILENT | IS_CALLABLE_STRICT,
NULL, fcc, NULL)) {
return 0;
}
/* Disallow dispatch via __call */
if (fcc->function_handler == Z_OBJCE_P(obj)->__call) { return 0; }
if (fcc->function_handler->type == ZEND_USER_FUNCTION) {
zend_op_array *oparray = (zend_op_array*)(fcc->function_handler);
if (oparray->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
return 0;
}
}
return 1;
}
/* }}} */
/* {{{ operator_is_greater_op */
static inline zend_bool operator_is_greater_op(const zend_op *opline, zend_string **pmethod) {
if (opline->extended_value == 1) {
switch (opline->opcode) {
#define X(op, meth) \
case ZEND_##op: *pmethod = s_##meth; return 1;
GREATER_OPS(X)
#undef X
}
}
return 0;
}
/* }}} */
/* {{{ op_handler */
static int op_handler(zend_execute_data *execute_data) {
USE_OPLINE
zend_free_op free_op1 = NULL, free_op2 = NULL;
zval *op1, *op2 = NULL;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_string *method = operator_method_name(opline->opcode);
if (opline->op1_type == IS_UNUSED) {
/* Assign op */
op1 = EX_VAR(opline->result.var);
} else {
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
}
ZVAL_DEREF(op1);
switch (opline->opcode) {
#define X(op, meth) \
case ZEND_##op:
BINARY_OPS(X)
BINARY_ASSIGN_OPS(X)
#undef X
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
}
if (operator_is_greater_op(opline, &method)) {
zval *tmp = op1;
zend_free_op free_tmp = free_op1;
op1 = op2; op2 = tmp;
free_op1 = free_op2; free_op2 = free_tmp;
}
if ((Z_TYPE_P(op1) != IS_OBJECT) ||
!operator_get_method(method, op1, &fci, &fcc)) {
/* Not an overloaded call */
return ZEND_USER_OPCODE_DISPATCH;
}
fci.retval = EX_VAR(opline->result.var);
fci.params = op2;
fci.param_count = op2 ? 1 : 0;
if (FAILURE == zend_call_function(&fci, &fcc)) {
php_error(E_WARNING, "Failed calling %s::%s()", Z_OBJCE_P(op1)->name, Z_STRVAL(fci.function_name));
ZVAL_NULL(fci.retval);
}
FREE_OP2
FREE_OP1
EX(opline) = opline + 1;
return ZEND_USER_OPCODE_CONTINUE;
}
/* }}} */
/* {{{ MINIT */
static PHP_MINIT_FUNCTION(operator) {
#define X(op, meth) \
s_##meth = zend_string_init(#meth, strlen(#meth), 1);
GREATER_OPS(X)
#undef X
#define X(op, meth) \
s_##meth = zend_string_init(#meth, strlen(#meth), 1); \
zend_set_user_opcode_handler(ZEND_##op, op_handler);
ALL_OPS(X)
#undef X
return SUCCESS;
}
/* }}} */
/* {{{ operator_module_entry
*/
static zend_module_entry operator_module_entry = {
STANDARD_MODULE_HEADER,
"operator",
NULL, /* functions */
PHP_MINIT(operator),
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
"7.2.0",
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_OPERATOR
ZEND_GET_MODULE(operator)
#endif