// // php-armadillo/operators.cc // // @Author CismonX // #include "operators.hh" #include "complex.hh" #include "base.hh" #include "dense.hh" #include "mat.hh" #include "subview_mat.hh" #include "mapval.hh" #if PHP_VERSION_ID >= 70300 // See https://github.com/php/php-src/blob/PHP-7.3/UPGRADING.INTERNALS section 3.1.b. #define EX_CONSTANT(op) RT_CONSTANT(EX(opline), op) #endif #define PHP_ARMA_OP_HANDLER_FUNC \ [](auto zv1, auto zv2, auto rv, auto ce) namespace php_arma { zend_always_inline zval *get_zval_ptr_undef( zend_uchar op_type, const 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 nullptr; } } template zend_always_inline int op_handler(zend_execute_data *execute_data, F&& handler) { const zend_op *opline = EX(opline); zend_free_op free_op1 = nullptr; zend_free_op free_op2 = nullptr; auto op1 = get_zval_ptr_undef(opline->op1_type, &opline->op1, &free_op1, execute_data); auto op2 = get_zval_ptr_undef(opline->op2_type, &opline->op2, &free_op2, execute_data); zend_class_entry *ce = nullptr; if (EXPECTED(op1)) { ZVAL_DEREF(op1); if (Z_TYPE_P(op1) == IS_OBJECT) { ce = Z_OBJCE_P(op1); } } if (op2) { ZVAL_DEREF(op2); if (ce == nullptr) { if (Z_TYPE_P(op2) == IS_OBJECT) { ce = Z_OBJCE_P(op2); } else { return ZEND_USER_OPCODE_DISPATCH; } } } zval *result = opline->result_type == IS_UNUSED ? nullptr : EX_VAR(opline->result.var); if (ce == nullptr || !handler(op1, op2, result, ce)) { return ZEND_USER_OPCODE_DISPATCH; } if (free_op2) { zval_ptr_dtor_nogc(free_op2); } if (free_op1) { zval_ptr_dtor_nogc(free_op1); } EX(opline) = opline + 1; return ZEND_USER_OPCODE_CONTINUE; } int add_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(add); PHP_ARMA_BASE_OPERATOR(add); return false; }); } int add_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(add); PHP_ARMA_BASE_ASSIGN_OPERATOR(add); return false; }); } int sub_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(sub); PHP_ARMA_BASE_OPERATOR(sub); return false; }); } int sub_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(sub); PHP_ARMA_BASE_ASSIGN_OPERATOR(sub); return false; }); } int mul_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(mul); PHP_ARMA_BASE_OPERATOR(mul); PHP_ARMA_MAPVAL_OPERATOR(mul); return false; }); } int mul_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(mul); PHP_ARMA_BASE_ASSIGN_OPERATOR(mul); return false; }); } int div_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(div); PHP_ARMA_BASE_OPERATOR(div); return false; }); } int div_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(div); PHP_ARMA_BASE_ASSIGN_OPERATOR(div); return false; }); } int mod_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_BASE_OPERATOR(dotMul); return false; }); } int mod_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_BASE_ASSIGN_OPERATOR(dotMul); return false; }); } int pow_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(pow); return false; }); } int pow_assign_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_ASSIGN_OPERATOR(pow); return false; }); } int bw_not_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_COMPLEX_OPERATOR(conj); return false; }); } int is_equal_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_DENSE_OPERATOR(equals); return false; }); } int is_not_equal_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_DENSE_OPERATOR(not_equals); return false; }); } int is_smaller_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_DENSE_OPERATOR(smaller_than); return false; }); } int is_smaller_or_equal_handler(zend_execute_data *execute_data) { return op_handler(execute_data, PHP_ARMA_OP_HANDLER_FUNC { PHP_ARMA_DENSE_OPERATOR(not_greater_than); return false; }); } #if PHP_VERSION_ID >= 70400 int assign_op_handler(zend_execute_data *execute_data) { switch (EX(opline)->extended_value) { case ZEND_ADD: return add_assign_handler(execute_data); case ZEND_SUB: return sub_assign_handler(execute_data); case ZEND_MUL: return mul_assign_handler(execute_data); case ZEND_DIV: return div_assign_handler(execute_data); case ZEND_MOD: return mod_assign_handler(execute_data); case ZEND_POW: return pow_assign_handler(execute_data); default: return ZEND_USER_OPCODE_DISPATCH; } } #endif template zend_always_inline void set_op_handlers(Ts&&... op_handlers) { for (auto [opcode, handler] : { op_handlers... }) { zend_set_user_opcode_handler(opcode, handler); } } void operators_init() { set_op_handlers( std::make_tuple(ZEND_ADD, add_handler), std::make_tuple(ZEND_SUB, sub_handler), std::make_tuple(ZEND_MUL, mul_handler), std::make_tuple(ZEND_DIV, div_handler), std::make_tuple(ZEND_MOD, mod_handler), std::make_tuple(ZEND_POW, pow_handler), #if PHP_VERSION_ID >= 70400 std::make_tuple(ZEND_ASSIGN_OP, assign_op_handler), #else std::make_tuple(ZEND_ASSIGN_ADD, add_assign_handler), std::make_tuple(ZEND_ASSIGN_SUB, sub_assign_handler), std::make_tuple(ZEND_ASSIGN_MUL, mul_assign_handler), std::make_tuple(ZEND_ASSIGN_DIV, div_assign_handler), std::make_tuple(ZEND_ASSIGN_MOD, mod_assign_handler), std::make_tuple(ZEND_ASSIGN_POW, pow_assign_handler), #endif std::make_tuple(ZEND_BW_NOT, bw_not_handler), std::make_tuple(ZEND_IS_EQUAL, is_equal_handler), std::make_tuple(ZEND_IS_NOT_EQUAL, is_not_equal_handler), std::make_tuple(ZEND_IS_SMALLER, is_smaller_handler), std::make_tuple(ZEND_IS_SMALLER_OR_EQUAL, is_smaller_or_equal_handler) ); } }