From b49ef617c06e6430632cff379b8665f1b8145860 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 18 Apr 2019 18:15:33 +0800 Subject: [PATCH] Better operator support for Complex. --- src/complex.cc | 4 +- src/complex.hh | 33 +++++++++-- src/operators.cc | 96 ++++++++++---------------------- src/php_arma.hh | 15 +++-- tests/002-complex-operators.phpt | 3 +- 5 files changed, 67 insertions(+), 84 deletions(-) diff --git a/src/complex.cc b/src/complex.cc index bce5443..fb98b91 100644 --- a/src/complex.cc +++ b/src/complex.cc @@ -98,9 +98,7 @@ namespace php_arma template PHP_ARMA_METHOD(complex, neg, T) { - auto current = zval_get_scalar(getThis()); - - zval_set_scalar(return_value, -current); + operators::neg(getThis(), return_value); } template diff --git a/src/complex.hh b/src/complex.hh index 4e06acd..6d5e87b 100644 --- a/src/complex.hh +++ b/src/complex.hh @@ -13,16 +13,28 @@ #ifdef PHP_ARMA_OPERATORS +// For non-assignment operators, first operand can be of type double. #define PHP_ARMA_COMPLEX_OPERATOR(type, func) \ - if (instanceof_function(_ce, complex::ce)) { \ - return complex::operators::func(zv1, zv2, retval); \ + if (instanceof_function(ce, complex::ce)) { \ + zval tmp; \ + ZVAL_UNDEF(&tmp); \ + if (zval_is_scalar(zv1)) { \ + auto real = zval_get_scalar(zv1); \ + zval_set_scalar(&tmp, std::complex(real)); \ + zv1 = &tmp; \ + } \ + auto v = complex::operators::func(zv1, zv2, rv); \ + if (Z_TYPE(tmp) != IS_UNDEF) { \ + zval_ptr_dtor_nogc(&tmp); \ + } \ + return v; \ } #define PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(type, func) \ - if (instanceof_function(_ce, complex::ce)) { \ + if (instanceof_function(ce, complex::ce)) { \ auto v = complex::operators::func(zv1, zv2, zv1); \ if (EX(opline)->result_type != IS_UNUSED) { \ - ZVAL_COPY(retval, zv1); \ + ZVAL_COPY(rv, zv1); \ } \ return v; \ } @@ -172,6 +184,14 @@ namespace php_arma return false; } + zend_always_inline + static void neg(zval *zv, zval *retval) + { + auto v = zval_get_scalar(zv); + + zval_set_scalar(retval, -v); + } + zend_always_inline static bool mul(zval *zv1, zval *zv2, zval *retval) { @@ -185,6 +205,11 @@ namespace php_arma zval_set_scalar(retval, v1 * zval_get_scalar(zv2)); return true; } + if (Z_TYPE_P(zv2) == IS_LONG && Z_LVAL_P(zv2) == -1) { + // Negation operator is compiled as multiplication to -1. + neg(zv1, retval); + return true; + } return false; } diff --git a/src/operators.cc b/src/operators.cc index 4c1b4d9..31ec3e9 100644 --- a/src/operators.cc +++ b/src/operators.cc @@ -9,7 +9,7 @@ #include "complex.hh" #if PHP_VERSION_ID >= 70300 -// See https://github.com/php/php-src/blob/PHP-7.3/UPGRADING.INTERNALS#L61 section 3.1.b. +// 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 @@ -63,13 +63,17 @@ namespace php_arma if (op2) { ZVAL_DEREF(op2); } + if ((op1 && Z_TYPE_P(op1) != IS_OBJECT) && (op2 && Z_TYPE_P(op2) != IS_OBJECT)) { + return ZEND_USER_OPCODE_DISPATCH; + } if (UNEXPECTED(opcode_is_greater(opline))) { std::swap(op1, op2); std::swap(free_op1, free_op2); } - if (!handler(op1, op2, EX_VAR(opline->result.var))) { + auto ce = Z_TYPE_P(op1) == IS_OBJECT ? Z_OBJCE_P(op1) : Z_OBJCE_P(op2); + if (!handler(op1, op2, EX_VAR(opline->result.var), ce)) { return ZEND_USER_OPCODE_DISPATCH; } @@ -85,12 +89,8 @@ namespace php_arma int add_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, add) PHP_ARMA_OPERATOR_END(); @@ -100,12 +100,8 @@ namespace php_arma int add_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, add) PHP_ARMA_OPERATOR_END(); @@ -115,12 +111,8 @@ namespace php_arma int sub_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, sub) PHP_ARMA_OPERATOR_END(); @@ -130,12 +122,8 @@ namespace php_arma int sub_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, sub) PHP_ARMA_OPERATOR_END(); @@ -145,12 +133,8 @@ namespace php_arma int mul_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, mul) PHP_ARMA_OPERATOR_END(); @@ -160,12 +144,8 @@ namespace php_arma int mul_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, mul) PHP_ARMA_OPERATOR_END(); @@ -175,12 +155,8 @@ namespace php_arma int div_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, div) PHP_ARMA_OPERATOR_END(); @@ -190,12 +166,8 @@ namespace php_arma int div_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, div) PHP_ARMA_OPERATOR_END(); @@ -205,12 +177,8 @@ namespace php_arma int pow_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, pow) PHP_ARMA_OPERATOR_END(); @@ -220,12 +188,8 @@ namespace php_arma int pow_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, pow) PHP_ARMA_OPERATOR_END(); @@ -235,12 +199,8 @@ namespace php_arma int bw_not_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { - return false; - } - - PHP_ARMA_OPERATOR_BEGIN(zv1, complex_ce) + return op_handler(execute_data, [] (auto zv1, auto zv2, auto rv, auto ce) { + PHP_ARMA_OPERATOR_BEGIN(complex_ce) PHP_ARMA_COMPLEX_OPERATOR(double, conj) PHP_ARMA_OPERATOR_END(); diff --git a/src/php_arma.hh b/src/php_arma.hh index 52c702a..f28b04a 100644 --- a/src/php_arma.hh +++ b/src/php_arma.hh @@ -69,14 +69,13 @@ extern zend_module_entry arma_module_entry; /// Helper macros for handling operator overloading. -#define PHP_ARMA_OPERATOR_BEGIN(zv, parent_ce) \ - { \ - auto _ce = Z_OBJCE_P(zv); \ - if (instanceof_function(_ce, parent_ce)) { +#define PHP_ARMA_OPERATOR_BEGIN(parent_ce) \ + { \ + if (instanceof_function(ce, parent_ce)) { -#define PHP_ARMA_OPERATOR_END() \ - return false; \ - } \ +#define PHP_ARMA_OPERATOR_END() \ + return false; \ + } \ } #endif // PHP_ARMA_OPERATORS @@ -233,7 +232,7 @@ namespace php_arma zend_always_inline zend_object *object_create(zend_class_entry *ce, const zend_object_handlers *handlers) { - auto zobj = reinterpret_cast(emalloc( + auto zobj = reinterpret_cast(emalloc( sizeof(zend_object) + zend_object_properties_size(ce))); zobj->handlers = handlers; zend_object_std_init(zobj, ce); diff --git a/tests/002-complex-operators.phpt b/tests/002-complex-operators.phpt index a4ad629..5e27783 100644 --- a/tests/002-complex-operators.phpt +++ b/tests/002-complex-operators.phpt @@ -19,10 +19,11 @@ $bar = new Arma\CxDouble(2.3, 2.6); batch_assert('operator overloading of Arma\\Complex', [$foo->add($bar), $foo + $bar], [$foo->sub($bar), $foo - $bar], + [$foo->neg(), -$foo], [$foo->mul($bar), $foo * $bar], [$foo->div($bar), $foo / $bar], [$foo->pow($bar), $foo ** $bar], - [$foo->conj(), ~$foo] + [$foo->conj(), ~$foo] ); $foo1 = $foo->add($bar);