summaryrefslogtreecommitdiff
path: root/Zend/zend_operators.c
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-03-31 18:03:30 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-03-31 20:49:39 +0200
commitf182309e87cd45de0d110516c6af4b05da3ce266 (patch)
tree5cd029cf6254a1354954a18ad26bb93773206ec9 /Zend/zend_operators.c
parentdaf45f68d25af0e7e909214719f3fbe0306abb7a (diff)
downloadphp-git-f182309e87cd45de0d110516c6af4b05da3ce266.tar.gz
Refactor operator implementations
Instead of looping, use straight-line code with the following layout: 1. Try to apply the base operation on the dereferenced operands. 2. Try overloaded object operations. 3. Try to convert operands to number, else error out. 4. Apply the base operation on the converted operands. This makes the code easier to reason about and fixes some edge-case bugs: 1. We should only try invoking operator overloading once prior to type conversion. Previously it was invoked both before and after type conversion. 2. We should not modify any values if an exception is thrown. Previously we sometimes modified the LHS of a compound assignment operator. 3. If conversion of the first operand fails, we no longer try to convert the second operand. I think the previous behavior here was fine as well, but this still seems a more typical. This will also make some followup changes I have in mind simpler.
Diffstat (limited to 'Zend/zend_operators.c')
-rw-r--r--Zend/zend_operators.c584
1 files changed, 288 insertions, 296 deletions
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index 54298a00a9..af21693f3b 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -145,7 +145,7 @@ ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len) /* {
/* }}} */
-static void ZEND_FASTCALL _convert_scalar_to_number(zval *op, zend_bool silent, zend_bool check) /* {{{ */
+ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */
{
try_again:
switch (Z_TYPE_P(op)) {
@@ -157,11 +157,8 @@ try_again:
zend_string *str;
str = Z_STR_P(op);
- if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), silent ? 1 : -1)) == 0) {
+ if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), 1)) == 0) {
ZVAL_LONG(op, 0);
- if (!silent) {
- zend_error(E_WARNING, "A non-numeric value encountered");
- }
}
zend_string_release_ex(str, 0);
break;
@@ -185,9 +182,6 @@ try_again:
zval dst;
convert_object_to_type(op, &dst, _IS_NUMBER);
- if (check && UNEXPECTED(EG(exception))) {
- return;
- }
zval_ptr_dtor(op);
if (Z_TYPE(dst) == IS_LONG || Z_TYPE(dst) == IS_DOUBLE) {
@@ -201,14 +195,8 @@ try_again:
}
/* }}} */
-ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */
-{
- _convert_scalar_to_number(op, 1, 0);
-}
-/* }}} */
-
-/* {{{ _zendi_convert_scalar_to_number_ex */
-static zend_always_inline zval* _zendi_convert_scalar_to_number_ex(zval *op, zval *holder, zend_bool silent) /* {{{ */
+/* {{{ _zendi_convert_scalar_to_number */
+static zend_never_inline zval* ZEND_FASTCALL _zendi_convert_scalar_to_number_silent(zval *op, zval *holder) /* {{{ */
{
switch (Z_TYPE_P(op)) {
case IS_NULL:
@@ -219,11 +207,8 @@ static zend_always_inline zval* _zendi_convert_scalar_to_number_ex(zval *op, zva
ZVAL_LONG(holder, 1);
return holder;
case IS_STRING:
- if ((Z_TYPE_INFO_P(holder) = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL_P(holder), &Z_DVAL_P(holder), silent ? 1 : -1)) == 0) {
+ if ((Z_TYPE_INFO_P(holder) = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL_P(holder), &Z_DVAL_P(holder), 1)) == 0) {
ZVAL_LONG(holder, 0);
- if (!silent) {
- zend_error(E_WARNING, "A non-numeric value encountered");
- }
}
return holder;
case IS_RESOURCE:
@@ -244,25 +229,52 @@ static zend_always_inline zval* _zendi_convert_scalar_to_number_ex(zval *op, zva
}
/* }}} */
-/* {{{ _zendi_convert_scalar_to_number */
-static zend_never_inline zval* ZEND_FASTCALL _zendi_convert_scalar_to_number_silent(zval *op, zval *holder) /* {{{ */
+static zend_never_inline int ZEND_FASTCALL _zendi_try_convert_scalar_to_number(zval *op, zval *holder) /* {{{ */
{
- return _zendi_convert_scalar_to_number_ex(op, holder, 1);
+ switch (Z_TYPE_P(op)) {
+ case IS_NULL:
+ case IS_FALSE:
+ ZVAL_LONG(holder, 0);
+ return SUCCESS;
+ case IS_TRUE:
+ ZVAL_LONG(holder, 1);
+ return SUCCESS;
+ case IS_STRING:
+ if ((Z_TYPE_INFO_P(holder) = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL_P(holder), &Z_DVAL_P(holder), -1)) == 0) {
+ ZVAL_LONG(holder, 0);
+ zend_error(E_WARNING, "A non-numeric value encountered");
+ if (UNEXPECTED(EG(exception))) {
+ return FAILURE;
+ }
+ }
+ return SUCCESS;
+ case IS_RESOURCE:
+ ZVAL_LONG(holder, Z_RES_HANDLE_P(op));
+ return SUCCESS;
+ case IS_OBJECT:
+ convert_object_to_type(op, holder, _IS_NUMBER);
+ if (UNEXPECTED(EG(exception))) {
+ return FAILURE;
+ }
+ if (UNEXPECTED(Z_TYPE_P(holder) != IS_LONG && Z_TYPE_P(holder) != IS_DOUBLE)) {
+ ZVAL_LONG(holder, 1);
+ }
+ return SUCCESS;
+ default:
+ return FAILURE;
+ }
}
/* }}} */
-/* {{{ _zendi_convert_scalar_to_number_noisy */
-static zend_never_inline zval* ZEND_FASTCALL _zendi_convert_scalar_to_number_noisy(zval *op, zval *holder) /* {{{ */
+static zend_always_inline int zendi_try_convert_scalar_to_number(zval *op, zval *holder)
{
- return _zendi_convert_scalar_to_number_ex(op, holder, 0);
+ if (Z_TYPE_P(op) == IS_LONG || Z_TYPE_P(op) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(holder, op);
+ return SUCCESS;
+ } else {
+ return _zendi_try_convert_scalar_to_number(op, holder);
+ }
}
-/* }}} */
-
-#define zendi_convert_scalar_to_number_noisy(op, holder, result) \
- ((Z_TYPE_P(op) == IS_LONG || Z_TYPE_P(op) == IS_DOUBLE) ? (op) : \
- (((op) == result) \
- ? (_convert_scalar_to_number((op), /* silent */ 0, 1), (op)) : \
- _zendi_convert_scalar_to_number_noisy((op), holder)))
#define convert_op1_op2_long(op1, op1_lval, op2, op2_lval, result, op, op_func) \
do { \
@@ -941,42 +953,35 @@ static zend_always_inline int add_function_fast(zval *result, zval *op1, zval *o
static zend_never_inline int ZEND_FASTCALL add_function_slow(zval *result, zval *op1, zval *op2) /* {{{ */
{
- zval op1_copy, op2_copy;
- int converted = 0;
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (add_function_fast(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ }
- while (1) {
- if (Z_ISREF_P(op1)) {
- op1 = Z_REFVAL_P(op1);
- } else if (Z_ISREF_P(op2)) {
- op2 = Z_REFVAL_P(op2);
- } else if (!converted) {
- ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_ADD, add_function);
-
- if (EXPECTED(op1 != op2)) {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = zendi_convert_scalar_to_number_noisy(op2, &op2_copy, result);
- } else {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = op1;
- }
- if (EG(exception)) {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- return FAILURE;
- }
- converted = 1;
- } else {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_ADD, add_function);
+
+ zval op1_copy, op2_copy;
+ if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE)
+ || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) {
+ if (!EG(exception)) {
zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE; /* unknown datatype */
}
- if (add_function_fast(result, op1, op2) == SUCCESS) {
- return SUCCESS;
+ if (result != op1) {
+ ZVAL_UNDEF(result);
}
+ return FAILURE;
}
+
+ if (result == op1) {
+ zval_ptr_dtor(result);
+ }
+
+ if (add_function_fast(result, &op1_copy, &op2_copy) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_ASSERT(0 && "Operation must succeed");
} /* }}} */
ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* {{{ */
@@ -1013,42 +1018,35 @@ static zend_always_inline int sub_function_fast(zval *result, zval *op1, zval *o
static zend_never_inline int ZEND_FASTCALL sub_function_slow(zval *result, zval *op1, zval *op2) /* {{{ */
{
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (sub_function_fast(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB, sub_function);
+
zval op1_copy, op2_copy;
- int converted = 0;
- while (1) {
- if (Z_ISREF_P(op1)) {
- op1 = Z_REFVAL_P(op1);
- } else if (Z_ISREF_P(op2)) {
- op2 = Z_REFVAL_P(op2);
- } else if (!converted) {
- ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB, sub_function);
-
- if (EXPECTED(op1 != op2)) {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = zendi_convert_scalar_to_number_noisy(op2, &op2_copy, result);
- } else {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = op1;
- }
- if (EG(exception)) {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- return FAILURE;
- }
- converted = 1;
- } else {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
+ if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE)
+ || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) {
+ if (!EG(exception)) {
zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE; /* unknown datatype */
}
- if (sub_function_fast(result, op1, op2) == SUCCESS) {
- return SUCCESS;
+ if (result != op1) {
+ ZVAL_UNDEF(result);
}
+ return FAILURE;
}
+ if (result == op1) {
+ zval_ptr_dtor(result);
+ }
+
+ if (sub_function_fast(result, &op1_copy, &op2_copy) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_ASSERT(0 && "Operation must succeed");
}
/* }}} */
@@ -1062,249 +1060,243 @@ ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2) /* {
}
/* }}} */
-ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2) /* {{{ */
+static zend_always_inline int mul_function_fast(zval *result, zval *op1, zval *op2) /* {{{ */
{
- zval op1_copy, op2_copy;
- int converted = 0;
+ zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
- while (1) {
- zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
+ if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
+ zend_long overflow;
+ ZEND_SIGNED_MULTIPLY_LONG(
+ Z_LVAL_P(op1), Z_LVAL_P(op2),
+ Z_LVAL_P(result), Z_DVAL_P(result), overflow);
+ Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
+ ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) * Z_DVAL_P(op2));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) * ((double)Z_LVAL_P(op2)));
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
- if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
- zend_long overflow;
+static zend_never_inline int ZEND_FASTCALL mul_function_slow(zval *result, zval *op1, zval *op2) /* {{{ */
+{
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (mul_function_fast(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ }
- ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1),Z_LVAL_P(op2), Z_LVAL_P(result),Z_DVAL_P(result),overflow);
- Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
- return SUCCESS;
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_MUL, mul_function);
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
- ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
- return SUCCESS;
+ zval op1_copy, op2_copy;
+ if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE)
+ || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) {
+ if (!EG(exception)) {
+ zend_throw_error(NULL, "Unsupported operand types");
+ }
+ if (result != op1) {
+ ZVAL_UNDEF(result);
+ }
+ return FAILURE;
+ }
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
- ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) * Z_DVAL_P(op2));
- return SUCCESS;
+ if (result == op1) {
+ zval_ptr_dtor(result);
+ }
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
- ZVAL_DOUBLE(result, Z_DVAL_P(op1) * ((double)Z_LVAL_P(op2)));
- return SUCCESS;
+ if (mul_function_fast(result, &op1_copy, &op2_copy) == SUCCESS) {
+ return SUCCESS;
+ }
- } else {
- if (Z_ISREF_P(op1)) {
- op1 = Z_REFVAL_P(op1);
- } else if (Z_ISREF_P(op2)) {
- op2 = Z_REFVAL_P(op2);
- } else if (!converted) {
- ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_MUL, mul_function);
+ ZEND_ASSERT(0 && "Operation must succeed");
+}
+/* }}} */
- if (EXPECTED(op1 != op2)) {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = zendi_convert_scalar_to_number_noisy(op2, &op2_copy, result);
- } else {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = op1;
- }
- if (EG(exception)) {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- return FAILURE;
- }
- converted = 1;
- } else {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE; /* unknown datatype */
- }
- }
+ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2) /* {{{ */
+{
+ if (mul_function_fast(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ } else {
+ return mul_function_slow(result, op1, op2);
}
}
/* }}} */
-ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2) /* {{{ */
+static int ZEND_FASTCALL pow_function_base(zval *result, zval *op1, zval *op2) /* {{{ */
{
- zval op1_copy, op2_copy;
- int converted = 0;
-
- while (1) {
- zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
-
- if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
- if (Z_LVAL_P(op2) >= 0) {
- zend_long l1 = 1, l2 = Z_LVAL_P(op1), i = Z_LVAL_P(op2);
-
- if (i == 0) {
- ZVAL_LONG(result, 1L);
- return SUCCESS;
- } else if (l2 == 0) {
- ZVAL_LONG(result, 0);
- return SUCCESS;
- }
+ zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
- while (i >= 1) {
- zend_long overflow;
- double dval = 0.0;
+ if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
+ if (Z_LVAL_P(op2) >= 0) {
+ zend_long l1 = 1, l2 = Z_LVAL_P(op1), i = Z_LVAL_P(op2);
- if (i % 2) {
- --i;
- ZEND_SIGNED_MULTIPLY_LONG(l1, l2, l1, dval, overflow);
- if (overflow) {
- ZVAL_DOUBLE(result, dval * pow(l2, i));
- return SUCCESS;
- }
- } else {
- i /= 2;
- ZEND_SIGNED_MULTIPLY_LONG(l2, l2, l2, dval, overflow);
- if (overflow) {
- ZVAL_DOUBLE(result, (double)l1 * pow(dval, i));
- return SUCCESS;
- }
- }
- }
- /* i == 0 */
- ZVAL_LONG(result, l1);
- } else {
- ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
+ if (i == 0) {
+ ZVAL_LONG(result, 1L);
+ return SUCCESS;
+ } else if (l2 == 0) {
+ ZVAL_LONG(result, 0);
+ return SUCCESS;
}
- return SUCCESS;
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
- ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
- return SUCCESS;
+ while (i >= 1) {
+ zend_long overflow;
+ double dval = 0.0;
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
- ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
- return SUCCESS;
-
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
- ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
- return SUCCESS;
-
- } else {
- if (Z_ISREF_P(op1)) {
- op1 = Z_REFVAL_P(op1);
- } else if (Z_ISREF_P(op2)) {
- op2 = Z_REFVAL_P(op2);
- } else if (!converted) {
- ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_POW, pow_function);
-
- if (Z_TYPE_P(op1) == IS_ARRAY || Z_TYPE_P(op2) == IS_ARRAY) {
- if (result != op1) {
- ZVAL_UNDEF(result);
+ if (i % 2) {
+ --i;
+ ZEND_SIGNED_MULTIPLY_LONG(l1, l2, l1, dval, overflow);
+ if (overflow) {
+ ZVAL_DOUBLE(result, dval * pow(l2, i));
+ return SUCCESS;
}
- zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE;
- }
-
- if (EXPECTED(op1 != op2)) {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = zendi_convert_scalar_to_number_noisy(op2, &op2_copy, result);
} else {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = op1;
- }
-
- if (EG(exception)) {
- if (result != op1) {
- ZVAL_UNDEF(result);
+ i /= 2;
+ ZEND_SIGNED_MULTIPLY_LONG(l2, l2, l2, dval, overflow);
+ if (overflow) {
+ ZVAL_DOUBLE(result, (double)l1 * pow(dval, i));
+ return SUCCESS;
}
- return FAILURE;
}
- converted = 1;
- } else {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE;
}
+ /* i == 0 */
+ ZVAL_LONG(result, l1);
+ } else {
+ ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
}
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
+ ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
+ ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
+ ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
+ return SUCCESS;
+ } else {
+ return FAILURE;
}
}
/* }}} */
-#ifdef __clang__
-__attribute__((no_sanitize("float-divide-by-zero")))
-#endif
-ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2) /* {{{ */
+ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (pow_function_base(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_POW, pow_function);
+
zval op1_copy, op2_copy;
- int converted = 0;
+ if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE)
+ || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) {
+ if (!EG(exception)) {
+ zend_throw_error(NULL, "Unsupported operand types");
+ }
+ if (result != op1) {
+ ZVAL_UNDEF(result);
+ }
+ return FAILURE;
+ }
- while (1) {
- zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
+ if (result == op1) {
+ zval_ptr_dtor(result);
+ }
- if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
- if (Z_LVAL_P(op2) == 0) {
- zend_error(E_WARNING, "Division by zero");
- ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1) / (double) Z_LVAL_P(op2)));
- return SUCCESS;
- } else if (Z_LVAL_P(op2) == -1 && Z_LVAL_P(op1) == ZEND_LONG_MIN) {
- /* Prevent overflow error/crash */
- ZVAL_DOUBLE(result, (double) ZEND_LONG_MIN / -1);
- return SUCCESS;
- }
- if (Z_LVAL_P(op1) % Z_LVAL_P(op2) == 0) { /* integer */
- ZVAL_LONG(result, Z_LVAL_P(op1) / Z_LVAL_P(op2));
- } else {
- ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1)) / Z_LVAL_P(op2));
- }
- return SUCCESS;
+ if (pow_function_base(result, &op1_copy, &op2_copy) == SUCCESS) {
+ return SUCCESS;
+ }
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
- if (Z_DVAL_P(op2) == 0) {
- zend_error(E_WARNING, "Division by zero");
- }
- ZVAL_DOUBLE(result, Z_DVAL_P(op1) / Z_DVAL_P(op2));
- return SUCCESS;
+ ZEND_ASSERT(0 && "Operation must succeed");
+}
+/* }}} */
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
- if (Z_LVAL_P(op2) == 0) {
- zend_error(E_WARNING, "Division by zero");
- }
- ZVAL_DOUBLE(result, Z_DVAL_P(op1) / (double)Z_LVAL_P(op2));
- return SUCCESS;
+#ifdef __clang__
+__attribute__((no_sanitize("float-divide-by-zero")))
+#endif
+static int ZEND_FASTCALL div_function_base(zval *result, zval *op1, zval *op2) /* {{{ */
+{
+ zend_uchar type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
- } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
- if (Z_DVAL_P(op2) == 0) {
- zend_error(E_WARNING, "Division by zero");
- }
- ZVAL_DOUBLE(result, (double)Z_LVAL_P(op1) / Z_DVAL_P(op2));
+ if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_LONG))) {
+ if (Z_LVAL_P(op2) == 0) {
+ zend_error(E_WARNING, "Division by zero");
+ ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1) / (double) Z_LVAL_P(op2)));
return SUCCESS;
-
+ } else if (Z_LVAL_P(op2) == -1 && Z_LVAL_P(op1) == ZEND_LONG_MIN) {
+ /* Prevent overflow error/crash */
+ ZVAL_DOUBLE(result, (double) ZEND_LONG_MIN / -1);
+ return SUCCESS;
+ }
+ if (Z_LVAL_P(op1) % Z_LVAL_P(op2) == 0) { /* integer */
+ ZVAL_LONG(result, Z_LVAL_P(op1) / Z_LVAL_P(op2));
} else {
- if (Z_ISREF_P(op1)) {
- op1 = Z_REFVAL_P(op1);
- } else if (Z_ISREF_P(op2)) {
- op2 = Z_REFVAL_P(op2);
- } else if (!converted) {
- ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_DIV, div_function);
+ ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1)) / Z_LVAL_P(op2));
+ }
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
+ if (Z_DVAL_P(op2) == 0) {
+ zend_error(E_WARNING, "Division by zero");
+ }
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) / Z_DVAL_P(op2));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
+ if (Z_LVAL_P(op2) == 0) {
+ zend_error(E_WARNING, "Division by zero");
+ }
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) / (double)Z_LVAL_P(op2));
+ return SUCCESS;
+ } else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
+ if (Z_DVAL_P(op2) == 0) {
+ zend_error(E_WARNING, "Division by zero");
+ }
+ ZVAL_DOUBLE(result, (double)Z_LVAL_P(op1) / Z_DVAL_P(op2));
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
- if (EXPECTED(op1 != op2)) {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = zendi_convert_scalar_to_number_noisy(op2, &op2_copy, result);
- } else {
- op1 = zendi_convert_scalar_to_number_noisy(op1, &op1_copy, result);
- op2 = op1;
- }
- if (EG(exception)) {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- return FAILURE;
- }
- converted = 1;
- } else {
- if (result != op1) {
- ZVAL_UNDEF(result);
- }
- zend_throw_error(NULL, "Unsupported operand types");
- return FAILURE; /* unknown datatype */
- }
+ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2) /* {{{ */
+{
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (div_function_base(result, op1, op2) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_DIV, div_function);
+
+ zval op1_copy, op2_copy;
+ if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE)
+ || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) {
+ if (!EG(exception)) {
+ zend_throw_error(NULL, "Unsupported operand types");
}
+ if (result != op1) {
+ ZVAL_UNDEF(result);
+ }
+ return FAILURE;
+ }
+
+ if (result == op1) {
+ zval_ptr_dtor(result);
}
+
+ if (div_function_base(result, &op1_copy, &op2_copy) == SUCCESS) {
+ return SUCCESS;
+ }
+
+ ZEND_ASSERT(0 && "Operation must succeed");
}
/* }}} */