From 0d853a2370e83b1e8039f553a9b717602e9f6a22 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Sep 2024 20:24:01 +0100 Subject: [PATCH 01/11] ext/gmp: Add behavioural tests for operator overloading --- .../tests/overloading_cmp_op_with_null.phpt | 53 +++++++++ ext/gmp/tests/overloading_with_array.phpt | 93 +++++++++++++++ ext/gmp/tests/overloading_with_null.phpt | 76 +++++++++++++ ...verloading_with_object_not_stringable.phpt | 100 +++++++++++++++++ .../overloading_with_object_stringable.phpt | 106 ++++++++++++++++++ ext/gmp/tests/overloading_with_resource.phpt | 93 +++++++++++++++ 6 files changed, 521 insertions(+) create mode 100644 ext/gmp/tests/overloading_cmp_op_with_null.phpt create mode 100644 ext/gmp/tests/overloading_with_array.phpt create mode 100644 ext/gmp/tests/overloading_with_null.phpt create mode 100644 ext/gmp/tests/overloading_with_object_not_stringable.phpt create mode 100644 ext/gmp/tests/overloading_with_object_stringable.phpt create mode 100644 ext/gmp/tests/overloading_with_resource.phpt diff --git a/ext/gmp/tests/overloading_cmp_op_with_null.phpt b/ext/gmp/tests/overloading_cmp_op_with_null.phpt new file mode 100644 index 0000000000000..d84ef967868e1 --- /dev/null +++ b/ext/gmp/tests/overloading_cmp_op_with_null.phpt @@ -0,0 +1,53 @@ +--TEST-- +GMP comparison operator overloading supports null +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num > null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num <= null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num >= null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num == null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num <=> null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +int(1) diff --git a/ext/gmp/tests/overloading_with_array.phpt b/ext/gmp/tests/overloading_with_array.phpt new file mode 100644 index 0000000000000..834860c79a3c3 --- /dev/null +++ b/ext/gmp/tests/overloading_with_array.phpt @@ -0,0 +1,93 @@ +--TEST-- +GMP operator overloading does not support [] +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> []); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +object(GMP)#3 (1) { + ["num"]=> + string(1) "1" +} +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} diff --git a/ext/gmp/tests/overloading_with_null.phpt b/ext/gmp/tests/overloading_with_null.phpt new file mode 100644 index 0000000000000..4087e499f46e5 --- /dev/null +++ b/ext/gmp/tests/overloading_with_null.phpt @@ -0,0 +1,76 @@ +--TEST-- +GMP operator overloading does not support null +--EXTENSIONS-- +gmp +--XFAIL-- +Test showcasing segfaulting behaviour +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> null); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +SEGFAULT diff --git a/ext/gmp/tests/overloading_with_object_not_stringable.phpt b/ext/gmp/tests/overloading_with_object_not_stringable.phpt new file mode 100644 index 0000000000000..a3db01c6933ff --- /dev/null +++ b/ext/gmp/tests/overloading_with_object_not_stringable.phpt @@ -0,0 +1,100 @@ +--TEST-- +GMP operator overloading does not support non-stringable objects +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given + +Warning: Object of class stdClass could not be converted to int in %s on line %d +object(GMP)#4 (1) { + ["num"]=> + string(2) "42" +} +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given + +Warning: Object of class stdClass could not be converted to int in %s on line %d +object(GMP)#3 (1) { + ["num"]=> + string(2) "84" +} + +Warning: Object of class stdClass could not be converted to int in %s on line %d +object(GMP)#3 (1) { + ["num"]=> + string(2) "21" +} diff --git a/ext/gmp/tests/overloading_with_object_stringable.phpt b/ext/gmp/tests/overloading_with_object_stringable.phpt new file mode 100644 index 0000000000000..01bf7df6ea705 --- /dev/null +++ b/ext/gmp/tests/overloading_with_object_stringable.phpt @@ -0,0 +1,106 @@ +--TEST-- +GMP operator overloading does not support stringable objects +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> $o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given + +Warning: Object of class T could not be converted to int in %s on line %d +object(GMP)#4 (1) { + ["num"]=> + string(2) "42" +} +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given + +Warning: Object of class T could not be converted to int in %s on line %d +object(GMP)#3 (1) { + ["num"]=> + string(2) "84" +} + +Warning: Object of class T could not be converted to int in %s on line %d +object(GMP)#3 (1) { + ["num"]=> + string(2) "21" +} diff --git a/ext/gmp/tests/overloading_with_resource.phpt b/ext/gmp/tests/overloading_with_resource.phpt new file mode 100644 index 0000000000000..05f52492a6405 --- /dev/null +++ b/ext/gmp/tests/overloading_with_resource.phpt @@ -0,0 +1,93 @@ +--TEST-- +GMP operator overloading does not support resources +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> STDERR); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +object(GMP)#3 (1) { + ["num"]=> + string(5) "74088" +} +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +object(GMP)#2 (1) { + ["num"]=> + string(3) "336" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "5" +} From e1a8d5415d5e941d9d26ca837067751560ce7779 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Sep 2024 20:27:23 +0100 Subject: [PATCH 02/11] ext/gmp: Fix segfault when null is encountered on an overloaded operator And improve the behaviour for other not supported types --- ext/gmp/gmp.c | 62 +++++++++++++++---- ext/gmp/tests/overloading_with_array.phpt | 16 ++--- ext/gmp/tests/overloading_with_null.phpt | 23 ++++++- ...verloading_with_object_not_stringable.phpt | 16 ++--- .../overloading_with_object_stringable.phpt | 16 ++--- ext/gmp/tests/overloading_with_resource.phpt | 16 ++--- 6 files changed, 103 insertions(+), 46 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 42a4a446867c2..80ca4e821b77b 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -199,7 +199,7 @@ typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong); typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr); typedef gmp_ulong (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong); -static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator); +static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator); static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero); static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op); @@ -356,10 +356,7 @@ static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zva } #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \ - gmp_zval_binary_ui_op( \ - result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); \ - if (UNEXPECTED(EG(exception))) { return FAILURE; } \ - return SUCCESS; + return gmp_zval_binary_ui_op(result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); #define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0) #define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0) @@ -437,7 +434,7 @@ static int gmp_compare(zval *op1, zval *op2) /* {{{ */ /* An error/exception occurs if one of the operands is not a numeric string * or an object which is different from GMP */ if (EG(exception)) { - return 1; + return ZEND_UNCOMPARABLE; } /* result can only be a zend_long if gmp_cmp hasn't thrown an Error */ ZEND_ASSERT(Z_TYPE(result) == IS_LONG); @@ -619,7 +616,16 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui case IS_STRING: { return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos); } - default: { + case IS_NULL: + /* For operator overloading just reject null */ + if (arg_pos == 0) { + return FAILURE; + } + ZEND_FALLTHROUGH; + case IS_DOUBLE: + case IS_FALSE: + case IS_TRUE: + { zend_long lval; if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) { if (arg_pos == 0) { @@ -635,6 +641,12 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui mpz_set_si(gmpnumber, lval); return SUCCESS; } + default: + if (arg_pos != 0) { + zend_argument_type_error(arg_pos, + "must be of type GMP|string|int, %s given", zend_zval_type_name(val)); + } + return FAILURE; } } /* }}} */ @@ -702,18 +714,45 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operat /* {{{ gmp_zval_binary_ui_op Execute GMP binary operation. */ -static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator) +static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator) { mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result; gmp_temp_t temp_a, temp_b; - FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1); + /* Inline version of FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1); + * because we cannot use RETURN_THROWS() here */ + if (IS_GMP(a_arg)) { + gmpnum_a = GET_GMP_FROM_ZVAL(a_arg); + temp_a.is_used = 0; + } else { + mpz_init(temp_a.num); + if (convert_to_gmp(temp_a.num, a_arg, 0, is_operator ? 0 : 1) == FAILURE) { + mpz_clear(temp_a.num); + return FAILURE; + } + temp_a.is_used = 1; + gmpnum_a = temp_a.num; + } if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) { gmpnum_b = NULL; temp_b.is_used = 0; } else { - FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2); + /* Inline version of FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2); + * because we cannot use RETURN_THROWS() here */ + if (IS_GMP(b_arg)) { + gmpnum_b = GET_GMP_FROM_ZVAL(b_arg); + temp_b.is_used = 0; + } else { + mpz_init(temp_b.num); + if (convert_to_gmp(temp_b.num, b_arg, 0, is_operator ? 0 : 2) == FAILURE) { + mpz_clear(temp_b.num); + FREE_GMP_TEMP(temp_a); + return FAILURE; + } + temp_b.is_used = 1; + gmpnum_b = temp_b.num; + } } if (check_b_zero) { @@ -732,7 +771,7 @@ static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval * } FREE_GMP_TEMP(temp_a); FREE_GMP_TEMP(temp_b); - RETURN_THROWS(); + return FAILURE; } } @@ -746,6 +785,7 @@ static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval * FREE_GMP_TEMP(temp_a); FREE_GMP_TEMP(temp_b); + return SUCCESS; } /* }}} */ diff --git a/ext/gmp/tests/overloading_with_array.phpt b/ext/gmp/tests/overloading_with_array.phpt index 834860c79a3c3..c5fa602ce31fe 100644 --- a/ext/gmp/tests/overloading_with_array.phpt +++ b/ext/gmp/tests/overloading_with_array.phpt @@ -71,18 +71,18 @@ try { ?> --EXPECT-- -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given +TypeError: Unsupported operand types: GMP + array +TypeError: Unsupported operand types: GMP - array +TypeError: Unsupported operand types: GMP * array +TypeError: Unsupported operand types: GMP / array +TypeError: Unsupported operand types: GMP % array object(GMP)#3 (1) { ["num"]=> string(1) "1" } -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given -TypeError: Number must be of type GMP|string|int, array given +TypeError: Unsupported operand types: GMP | array +TypeError: Unsupported operand types: GMP & array +TypeError: Unsupported operand types: GMP ^ array object(GMP)#2 (1) { ["num"]=> string(2) "42" diff --git a/ext/gmp/tests/overloading_with_null.phpt b/ext/gmp/tests/overloading_with_null.phpt index 4087e499f46e5..9a4a5ad2371f8 100644 --- a/ext/gmp/tests/overloading_with_null.phpt +++ b/ext/gmp/tests/overloading_with_null.phpt @@ -2,8 +2,6 @@ GMP operator overloading does not support null --EXTENSIONS-- gmp ---XFAIL-- -Test showcasing segfaulting behaviour --FILE-- --EXPECT-- -SEGFAULT +int(42) +int(42) +int(0) +DivisionByZeroError: Division by zero +DivisionByZeroError: Modulo by zero +object(GMP)#2 (1) { + ["num"]=> + string(1) "1" +} +int(42) +int(0) +int(42) +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} \ No newline at end of file diff --git a/ext/gmp/tests/overloading_with_object_not_stringable.phpt b/ext/gmp/tests/overloading_with_object_not_stringable.phpt index a3db01c6933ff..605ceb6929db4 100644 --- a/ext/gmp/tests/overloading_with_object_not_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_not_stringable.phpt @@ -72,20 +72,20 @@ try { ?> --EXPECTF-- -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Unsupported operand types: GMP + stdClass +TypeError: Unsupported operand types: GMP - stdClass +TypeError: Unsupported operand types: GMP * stdClass +TypeError: Unsupported operand types: GMP / stdClass +TypeError: Unsupported operand types: GMP % stdClass Warning: Object of class stdClass could not be converted to int in %s on line %d object(GMP)#4 (1) { ["num"]=> string(2) "42" } -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given -TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Unsupported operand types: GMP | stdClass +TypeError: Unsupported operand types: GMP & stdClass +TypeError: Unsupported operand types: GMP ^ stdClass Warning: Object of class stdClass could not be converted to int in %s on line %d object(GMP)#3 (1) { diff --git a/ext/gmp/tests/overloading_with_object_stringable.phpt b/ext/gmp/tests/overloading_with_object_stringable.phpt index 01bf7df6ea705..b7c192810861b 100644 --- a/ext/gmp/tests/overloading_with_object_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_stringable.phpt @@ -78,20 +78,20 @@ try { ?> --EXPECTF-- -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given +TypeError: Unsupported operand types: GMP + T +TypeError: Unsupported operand types: GMP - T +TypeError: Unsupported operand types: GMP * T +TypeError: Unsupported operand types: GMP / T +TypeError: Unsupported operand types: GMP % T Warning: Object of class T could not be converted to int in %s on line %d object(GMP)#4 (1) { ["num"]=> string(2) "42" } -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given -TypeError: Number must be of type GMP|string|int, T given +TypeError: Unsupported operand types: GMP | T +TypeError: Unsupported operand types: GMP & T +TypeError: Unsupported operand types: GMP ^ T Warning: Object of class T could not be converted to int in %s on line %d object(GMP)#3 (1) { diff --git a/ext/gmp/tests/overloading_with_resource.phpt b/ext/gmp/tests/overloading_with_resource.phpt index 05f52492a6405..3af9fdd6134a6 100644 --- a/ext/gmp/tests/overloading_with_resource.phpt +++ b/ext/gmp/tests/overloading_with_resource.phpt @@ -71,18 +71,18 @@ try { ?> --EXPECT-- -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given +TypeError: Unsupported operand types: GMP + resource +TypeError: Unsupported operand types: GMP - resource +TypeError: Unsupported operand types: GMP * resource +TypeError: Unsupported operand types: GMP / resource +TypeError: Unsupported operand types: GMP % resource object(GMP)#3 (1) { ["num"]=> string(5) "74088" } -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given -TypeError: Number must be of type GMP|string|int, resource given +TypeError: Unsupported operand types: GMP | resource +TypeError: Unsupported operand types: GMP & resource +TypeError: Unsupported operand types: GMP ^ resource object(GMP)#2 (1) { ["num"]=> string(3) "336" From a4bc271bfa806cc90feab1d17d2600c22948981b Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Sep 2024 21:56:41 +0100 Subject: [PATCH 03/11] Revert "ext/gmp: Fix segfault when null is encountered on an overloaded operator" This reverts commit 9cea1a28fca6653f99cecab8aa65e84359e2df27. --- ext/gmp/gmp.c | 62 ++++--------------- ext/gmp/tests/overloading_with_array.phpt | 16 ++--- ext/gmp/tests/overloading_with_null.phpt | 23 +------ ...verloading_with_object_not_stringable.phpt | 16 ++--- .../overloading_with_object_stringable.phpt | 16 ++--- ext/gmp/tests/overloading_with_resource.phpt | 16 ++--- 6 files changed, 46 insertions(+), 103 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 80ca4e821b77b..42a4a446867c2 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -199,7 +199,7 @@ typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong); typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr); typedef gmp_ulong (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong); -static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator); +static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator); static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero); static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op); @@ -356,7 +356,10 @@ static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zva } #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \ - return gmp_zval_binary_ui_op(result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); + gmp_zval_binary_ui_op( \ + result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); \ + if (UNEXPECTED(EG(exception))) { return FAILURE; } \ + return SUCCESS; #define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0) #define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0) @@ -434,7 +437,7 @@ static int gmp_compare(zval *op1, zval *op2) /* {{{ */ /* An error/exception occurs if one of the operands is not a numeric string * or an object which is different from GMP */ if (EG(exception)) { - return ZEND_UNCOMPARABLE; + return 1; } /* result can only be a zend_long if gmp_cmp hasn't thrown an Error */ ZEND_ASSERT(Z_TYPE(result) == IS_LONG); @@ -616,16 +619,7 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui case IS_STRING: { return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos); } - case IS_NULL: - /* For operator overloading just reject null */ - if (arg_pos == 0) { - return FAILURE; - } - ZEND_FALLTHROUGH; - case IS_DOUBLE: - case IS_FALSE: - case IS_TRUE: - { + default: { zend_long lval; if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) { if (arg_pos == 0) { @@ -641,12 +635,6 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui mpz_set_si(gmpnumber, lval); return SUCCESS; } - default: - if (arg_pos != 0) { - zend_argument_type_error(arg_pos, - "must be of type GMP|string|int, %s given", zend_zval_type_name(val)); - } - return FAILURE; } } /* }}} */ @@ -714,45 +702,18 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operat /* {{{ gmp_zval_binary_ui_op Execute GMP binary operation. */ -static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator) +static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator) { mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result; gmp_temp_t temp_a, temp_b; - /* Inline version of FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1); - * because we cannot use RETURN_THROWS() here */ - if (IS_GMP(a_arg)) { - gmpnum_a = GET_GMP_FROM_ZVAL(a_arg); - temp_a.is_used = 0; - } else { - mpz_init(temp_a.num); - if (convert_to_gmp(temp_a.num, a_arg, 0, is_operator ? 0 : 1) == FAILURE) { - mpz_clear(temp_a.num); - return FAILURE; - } - temp_a.is_used = 1; - gmpnum_a = temp_a.num; - } + FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1); if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) { gmpnum_b = NULL; temp_b.is_used = 0; } else { - /* Inline version of FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2); - * because we cannot use RETURN_THROWS() here */ - if (IS_GMP(b_arg)) { - gmpnum_b = GET_GMP_FROM_ZVAL(b_arg); - temp_b.is_used = 0; - } else { - mpz_init(temp_b.num); - if (convert_to_gmp(temp_b.num, b_arg, 0, is_operator ? 0 : 2) == FAILURE) { - mpz_clear(temp_b.num); - FREE_GMP_TEMP(temp_a); - return FAILURE; - } - temp_b.is_used = 1; - gmpnum_b = temp_b.num; - } + FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2); } if (check_b_zero) { @@ -771,7 +732,7 @@ static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, } FREE_GMP_TEMP(temp_a); FREE_GMP_TEMP(temp_b); - return FAILURE; + RETURN_THROWS(); } } @@ -785,7 +746,6 @@ static inline zend_result gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, FREE_GMP_TEMP(temp_a); FREE_GMP_TEMP(temp_b); - return SUCCESS; } /* }}} */ diff --git a/ext/gmp/tests/overloading_with_array.phpt b/ext/gmp/tests/overloading_with_array.phpt index c5fa602ce31fe..834860c79a3c3 100644 --- a/ext/gmp/tests/overloading_with_array.phpt +++ b/ext/gmp/tests/overloading_with_array.phpt @@ -71,18 +71,18 @@ try { ?> --EXPECT-- -TypeError: Unsupported operand types: GMP + array -TypeError: Unsupported operand types: GMP - array -TypeError: Unsupported operand types: GMP * array -TypeError: Unsupported operand types: GMP / array -TypeError: Unsupported operand types: GMP % array +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given object(GMP)#3 (1) { ["num"]=> string(1) "1" } -TypeError: Unsupported operand types: GMP | array -TypeError: Unsupported operand types: GMP & array -TypeError: Unsupported operand types: GMP ^ array +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given +TypeError: Number must be of type GMP|string|int, array given object(GMP)#2 (1) { ["num"]=> string(2) "42" diff --git a/ext/gmp/tests/overloading_with_null.phpt b/ext/gmp/tests/overloading_with_null.phpt index 9a4a5ad2371f8..4087e499f46e5 100644 --- a/ext/gmp/tests/overloading_with_null.phpt +++ b/ext/gmp/tests/overloading_with_null.phpt @@ -2,6 +2,8 @@ GMP operator overloading does not support null --EXTENSIONS-- gmp +--XFAIL-- +Test showcasing segfaulting behaviour --FILE-- --EXPECT-- -int(42) -int(42) -int(0) -DivisionByZeroError: Division by zero -DivisionByZeroError: Modulo by zero -object(GMP)#2 (1) { - ["num"]=> - string(1) "1" -} -int(42) -int(0) -int(42) -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} \ No newline at end of file +SEGFAULT diff --git a/ext/gmp/tests/overloading_with_object_not_stringable.phpt b/ext/gmp/tests/overloading_with_object_not_stringable.phpt index 605ceb6929db4..a3db01c6933ff 100644 --- a/ext/gmp/tests/overloading_with_object_not_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_not_stringable.phpt @@ -72,20 +72,20 @@ try { ?> --EXPECTF-- -TypeError: Unsupported operand types: GMP + stdClass -TypeError: Unsupported operand types: GMP - stdClass -TypeError: Unsupported operand types: GMP * stdClass -TypeError: Unsupported operand types: GMP / stdClass -TypeError: Unsupported operand types: GMP % stdClass +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given Warning: Object of class stdClass could not be converted to int in %s on line %d object(GMP)#4 (1) { ["num"]=> string(2) "42" } -TypeError: Unsupported operand types: GMP | stdClass -TypeError: Unsupported operand types: GMP & stdClass -TypeError: Unsupported operand types: GMP ^ stdClass +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given +TypeError: Number must be of type GMP|string|int, stdClass given Warning: Object of class stdClass could not be converted to int in %s on line %d object(GMP)#3 (1) { diff --git a/ext/gmp/tests/overloading_with_object_stringable.phpt b/ext/gmp/tests/overloading_with_object_stringable.phpt index b7c192810861b..01bf7df6ea705 100644 --- a/ext/gmp/tests/overloading_with_object_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_stringable.phpt @@ -78,20 +78,20 @@ try { ?> --EXPECTF-- -TypeError: Unsupported operand types: GMP + T -TypeError: Unsupported operand types: GMP - T -TypeError: Unsupported operand types: GMP * T -TypeError: Unsupported operand types: GMP / T -TypeError: Unsupported operand types: GMP % T +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given Warning: Object of class T could not be converted to int in %s on line %d object(GMP)#4 (1) { ["num"]=> string(2) "42" } -TypeError: Unsupported operand types: GMP | T -TypeError: Unsupported operand types: GMP & T -TypeError: Unsupported operand types: GMP ^ T +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given +TypeError: Number must be of type GMP|string|int, T given Warning: Object of class T could not be converted to int in %s on line %d object(GMP)#3 (1) { diff --git a/ext/gmp/tests/overloading_with_resource.phpt b/ext/gmp/tests/overloading_with_resource.phpt index 3af9fdd6134a6..05f52492a6405 100644 --- a/ext/gmp/tests/overloading_with_resource.phpt +++ b/ext/gmp/tests/overloading_with_resource.phpt @@ -71,18 +71,18 @@ try { ?> --EXPECT-- -TypeError: Unsupported operand types: GMP + resource -TypeError: Unsupported operand types: GMP - resource -TypeError: Unsupported operand types: GMP * resource -TypeError: Unsupported operand types: GMP / resource -TypeError: Unsupported operand types: GMP % resource +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given object(GMP)#3 (1) { ["num"]=> string(5) "74088" } -TypeError: Unsupported operand types: GMP | resource -TypeError: Unsupported operand types: GMP & resource -TypeError: Unsupported operand types: GMP ^ resource +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given +TypeError: Number must be of type GMP|string|int, resource given object(GMP)#2 (1) { ["num"]=> string(3) "336" From c3f81b4d89dd61ffc75cf6c1e5d8aee3450c2a12 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Sep 2024 21:59:33 +0100 Subject: [PATCH 04/11] ext/gmp: Fix segfault when null is encountered on an overloaded operator --- ext/gmp/gmp.c | 7 +++++++ ext/gmp/tests/overloading_with_null.phpt | 23 ++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 42a4a446867c2..9a564095b666f 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -619,6 +619,13 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui case IS_STRING: { return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos); } + case IS_NULL: + /* Just reject null for operator overloading */ + if (arg_pos == 0) { + zend_type_error("Number must be of type GMP|string|int, %s given", zend_zval_type_name(val)); + return FAILURE; + } + ZEND_FALLTHROUGH; default: { zend_long lval; if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) { diff --git a/ext/gmp/tests/overloading_with_null.phpt b/ext/gmp/tests/overloading_with_null.phpt index 4087e499f46e5..4cfdb234adad8 100644 --- a/ext/gmp/tests/overloading_with_null.phpt +++ b/ext/gmp/tests/overloading_with_null.phpt @@ -2,8 +2,6 @@ GMP operator overloading does not support null --EXTENSIONS-- gmp ---XFAIL-- -Test showcasing segfaulting behaviour --FILE-- --EXPECT-- -SEGFAULT +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +object(GMP)#3 (1) { + ["num"]=> + string(1) "1" +} +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +TypeError: Number must be of type GMP|string|int, null given +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} From b8655f391eaa5d20bf7d0176a686440238510490 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Sep 2024 22:52:57 +0100 Subject: [PATCH 05/11] ext/gmp: Throw TypeError for unusable types for <<, >>, and ** ops --- ext/gmp/gmp.c | 63 ++++++++++++++++--- ext/gmp/tests/overloading_with_array.phpt | 15 +---- ext/gmp/tests/overloading_with_null.phpt | 15 +---- ...verloading_with_object_not_stringable.phpt | 23 ++----- .../overloading_with_object_stringable.phpt | 21 +------ ext/gmp/tests/overloading_with_resource.phpt | 15 +---- 6 files changed, 69 insertions(+), 83 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 9a564095b666f..a17307377033c 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -334,8 +334,19 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */ } /* }}} */ -static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar opcode) { - zend_long shift = zval_get_long(op2); +static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, zend_uchar opcode) { + zend_long shift = 0; + + if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { + if (UNEXPECTED(!IS_GMP(op2))) { + goto typeof_op_failure; + } else { + // TODO We shouldn't cast the GMP object to int here + shift = zval_get_long(op2); + } + } else { + shift = Z_LVAL_P(op2); + } if (shift < 0) { zend_throw_error( @@ -343,16 +354,51 @@ static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zva opcode == ZEND_POW ? "Exponent" : "Shift" ); ZVAL_UNDEF(return_value); - return; + return FAILURE; } else { mpz_ptr gmpnum_op, gmpnum_result; gmp_temp_t temp; - FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1); + /* Inline FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1); as cannot use RETURN_THROWS() */ + if (UNEXPECTED(!IS_GMP(op1))) { + if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { + goto typeof_op_failure; + } + mpz_init(temp.num); + mpz_set_si(temp.num, Z_LVAL_P(op1)); + temp.is_used = 1; + gmpnum_op = temp.num; + } else { + gmpnum_op = GET_GMP_FROM_ZVAL(op1); + temp.is_used = 0; + } INIT_GMP_RETVAL(gmpnum_result); op(gmpnum_result, gmpnum_op, (gmp_ulong) shift); FREE_GMP_TEMP(temp); + return SUCCESS; } + +typeof_op_failure: + ; /* Blank statement */ + /* Returning FAILURE without throwing an exception would emit the + * Unsupported operand types: GMP OP TypeOfOp2 + * However, this leads to the engine trying to interpret the GMP object as an integer + * and doing the operation that way, which is not something we want. */ + const char *op_sigil; + switch (opcode) { + case ZEND_POW: + op_sigil = "**"; + break; + case ZEND_SL: + op_sigil = "<<"; + break; + case ZEND_SR: + op_sigil = ">>"; + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + zend_type_error("Unsupported operand types: %s %s %s", zend_zval_type_name(op1), op_sigil, zend_zval_type_name(op2)); + return FAILURE; } #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \ @@ -381,18 +427,15 @@ static zend_result gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op case ZEND_MUL: DO_BINARY_UI_OP(mpz_mul); case ZEND_POW: - shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode); - return SUCCESS; + return shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode); case ZEND_DIV: DO_BINARY_UI_OP_EX(mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1); case ZEND_MOD: DO_BINARY_UI_OP_EX(mpz_mod, gmp_mpz_mod_ui, 1); case ZEND_SL: - shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode); - return SUCCESS; + return shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode); case ZEND_SR: - shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode); - return SUCCESS; + return shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode); case ZEND_BW_OR: DO_BINARY_OP(mpz_ior); case ZEND_BW_AND: diff --git a/ext/gmp/tests/overloading_with_array.phpt b/ext/gmp/tests/overloading_with_array.phpt index 834860c79a3c3..a23df9381dcde 100644 --- a/ext/gmp/tests/overloading_with_array.phpt +++ b/ext/gmp/tests/overloading_with_array.phpt @@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, array given TypeError: Number must be of type GMP|string|int, array given TypeError: Number must be of type GMP|string|int, array given TypeError: Number must be of type GMP|string|int, array given -object(GMP)#3 (1) { - ["num"]=> - string(1) "1" -} +TypeError: Unsupported operand types: GMP ** array TypeError: Number must be of type GMP|string|int, array given TypeError: Number must be of type GMP|string|int, array given TypeError: Number must be of type GMP|string|int, array given -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} +TypeError: Unsupported operand types: GMP << array +TypeError: Unsupported operand types: GMP >> array diff --git a/ext/gmp/tests/overloading_with_null.phpt b/ext/gmp/tests/overloading_with_null.phpt index 4cfdb234adad8..95f8153e4e7a5 100644 --- a/ext/gmp/tests/overloading_with_null.phpt +++ b/ext/gmp/tests/overloading_with_null.phpt @@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, null given TypeError: Number must be of type GMP|string|int, null given TypeError: Number must be of type GMP|string|int, null given TypeError: Number must be of type GMP|string|int, null given -object(GMP)#3 (1) { - ["num"]=> - string(1) "1" -} +TypeError: Unsupported operand types: GMP ** null TypeError: Number must be of type GMP|string|int, null given TypeError: Number must be of type GMP|string|int, null given TypeError: Number must be of type GMP|string|int, null given -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} +TypeError: Unsupported operand types: GMP << null +TypeError: Unsupported operand types: GMP >> null diff --git a/ext/gmp/tests/overloading_with_object_not_stringable.phpt b/ext/gmp/tests/overloading_with_object_not_stringable.phpt index a3db01c6933ff..c8fb924824dd4 100644 --- a/ext/gmp/tests/overloading_with_object_not_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_not_stringable.phpt @@ -71,30 +71,15 @@ try { } ?> ---EXPECTF-- +--EXPECT-- TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given - -Warning: Object of class stdClass could not be converted to int in %s on line %d -object(GMP)#4 (1) { - ["num"]=> - string(2) "42" -} +TypeError: Unsupported operand types: GMP ** stdClass TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given TypeError: Number must be of type GMP|string|int, stdClass given - -Warning: Object of class stdClass could not be converted to int in %s on line %d -object(GMP)#3 (1) { - ["num"]=> - string(2) "84" -} - -Warning: Object of class stdClass could not be converted to int in %s on line %d -object(GMP)#3 (1) { - ["num"]=> - string(2) "21" -} +TypeError: Unsupported operand types: GMP << stdClass +TypeError: Unsupported operand types: GMP >> stdClass diff --git a/ext/gmp/tests/overloading_with_object_stringable.phpt b/ext/gmp/tests/overloading_with_object_stringable.phpt index 01bf7df6ea705..4425661b98924 100644 --- a/ext/gmp/tests/overloading_with_object_stringable.phpt +++ b/ext/gmp/tests/overloading_with_object_stringable.phpt @@ -83,24 +83,9 @@ TypeError: Number must be of type GMP|string|int, T given TypeError: Number must be of type GMP|string|int, T given TypeError: Number must be of type GMP|string|int, T given TypeError: Number must be of type GMP|string|int, T given - -Warning: Object of class T could not be converted to int in %s on line %d -object(GMP)#4 (1) { - ["num"]=> - string(2) "42" -} +TypeError: Unsupported operand types: GMP ** T TypeError: Number must be of type GMP|string|int, T given TypeError: Number must be of type GMP|string|int, T given TypeError: Number must be of type GMP|string|int, T given - -Warning: Object of class T could not be converted to int in %s on line %d -object(GMP)#3 (1) { - ["num"]=> - string(2) "84" -} - -Warning: Object of class T could not be converted to int in %s on line %d -object(GMP)#3 (1) { - ["num"]=> - string(2) "21" -} +TypeError: Unsupported operand types: GMP << T +TypeError: Unsupported operand types: GMP >> T diff --git a/ext/gmp/tests/overloading_with_resource.phpt b/ext/gmp/tests/overloading_with_resource.phpt index 05f52492a6405..50b7af5d32746 100644 --- a/ext/gmp/tests/overloading_with_resource.phpt +++ b/ext/gmp/tests/overloading_with_resource.phpt @@ -76,18 +76,9 @@ TypeError: Number must be of type GMP|string|int, resource given TypeError: Number must be of type GMP|string|int, resource given TypeError: Number must be of type GMP|string|int, resource given TypeError: Number must be of type GMP|string|int, resource given -object(GMP)#3 (1) { - ["num"]=> - string(5) "74088" -} +TypeError: Unsupported operand types: GMP ** resource TypeError: Number must be of type GMP|string|int, resource given TypeError: Number must be of type GMP|string|int, resource given TypeError: Number must be of type GMP|string|int, resource given -object(GMP)#2 (1) { - ["num"]=> - string(3) "336" -} -object(GMP)#2 (1) { - ["num"]=> - string(1) "5" -} +TypeError: Unsupported operand types: GMP << resource +TypeError: Unsupported operand types: GMP >> resource From 4141ba05405b151cfd786df93eba2c2d2925b6db Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 25 Sep 2024 16:32:54 +0100 Subject: [PATCH 06/11] Address review comments --- ext/gmp/gmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index a17307377033c..f59fcc8e0d5a8 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -359,7 +359,8 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val mpz_ptr gmpnum_op, gmpnum_result; gmp_temp_t temp; - /* Inline FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1); as cannot use RETURN_THROWS() */ + /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp() + * as we want to handle the emitted exception ourself. */ if (UNEXPECTED(!IS_GMP(op1))) { if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { goto typeof_op_failure; @@ -378,8 +379,7 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val return SUCCESS; } -typeof_op_failure: - ; /* Blank statement */ +typeof_op_failure: ; /* Returning FAILURE without throwing an exception would emit the * Unsupported operand types: GMP OP TypeOfOp2 * However, this leads to the engine trying to interpret the GMP object as an integer @@ -388,13 +388,13 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val switch (opcode) { case ZEND_POW: op_sigil = "**"; - break; + break; case ZEND_SL: op_sigil = "<<"; - break; + break; case ZEND_SR: op_sigil = ">>"; - break; + break; EMPTY_SWITCH_DEFAULT_CASE(); } zend_type_error("Unsupported operand types: %s %s %s", zend_zval_type_name(op1), op_sigil, zend_zval_type_name(op2)); From 3efb1ebffd7ef3f6147654017ec8e1346f11fa65 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 25 Oct 2024 18:06:48 +0100 Subject: [PATCH 07/11] Add test for numeric int --- .../tests/overloading_with_int_string.phpt | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 ext/gmp/tests/overloading_with_int_string.phpt diff --git a/ext/gmp/tests/overloading_with_int_string.phpt b/ext/gmp/tests/overloading_with_int_string.phpt new file mode 100644 index 0000000000000..252916ef9a531 --- /dev/null +++ b/ext/gmp/tests/overloading_with_int_string.phpt @@ -0,0 +1,117 @@ +--TEST-- +GMP operator overloading does support int strings +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> "2"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +object(GMP)#2 (1) { + ["num"]=> + string(2) "44" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "40" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "84" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "21" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(4) "1764" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "2" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "40" +} +object(GMP)#2 (1) { + ["num"]=> + string(3) "168" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "10" +} From 0f2ec43300078bb38fa5cf13d76f1ebaa1948d87 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 25 Oct 2024 18:09:07 +0100 Subject: [PATCH 08/11] Add float string behaviour --- .../tests/overloading_with_float_string.phpt | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 ext/gmp/tests/overloading_with_float_string.phpt diff --git a/ext/gmp/tests/overloading_with_float_string.phpt b/ext/gmp/tests/overloading_with_float_string.phpt new file mode 100644 index 0000000000000..f715d5740b382 --- /dev/null +++ b/ext/gmp/tests/overloading_with_float_string.phpt @@ -0,0 +1,93 @@ +--TEST-- +GMP operator overloading does not support float strings +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> "2.0"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +object(GMP)#3 (1) { + ["num"]=> + string(4) "1764" +} +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +object(GMP)#2 (1) { + ["num"]=> + string(3) "168" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "10" +} From bc24f275b2aed243e74870793022902b7e2aa86e Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 25 Oct 2024 18:13:56 +0100 Subject: [PATCH 09/11] Add non-numeric string test --- .../overloading_with_non_numeric_string.phpt | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 ext/gmp/tests/overloading_with_non_numeric_string.phpt diff --git a/ext/gmp/tests/overloading_with_non_numeric_string.phpt b/ext/gmp/tests/overloading_with_non_numeric_string.phpt new file mode 100644 index 0000000000000..3068c31dd8af1 --- /dev/null +++ b/ext/gmp/tests/overloading_with_non_numeric_string.phpt @@ -0,0 +1,93 @@ +--TEST-- +GMP operator overloading does not support non-numeric strings +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> "string"); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +object(GMP)#3 (1) { + ["num"]=> + string(1) "1" +} +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} From 36f3060551a7fc395c7186f11dc111a5d91f803d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 25 Oct 2024 18:21:36 +0100 Subject: [PATCH 10/11] Add float tests --- ext/gmp/tests/overloading_with_float.phpt | 117 ++++++++++++++++ .../overloading_with_float_fractional.phpt | 132 ++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 ext/gmp/tests/overloading_with_float.phpt create mode 100644 ext/gmp/tests/overloading_with_float_fractional.phpt diff --git a/ext/gmp/tests/overloading_with_float.phpt b/ext/gmp/tests/overloading_with_float.phpt new file mode 100644 index 0000000000000..f2bcfa7d51485 --- /dev/null +++ b/ext/gmp/tests/overloading_with_float.phpt @@ -0,0 +1,117 @@ +--TEST-- +GMP operator overloading does support float with no fractional +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> 42.0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +object(GMP)#2 (1) { + ["num"]=> + string(2) "84" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(4) "1764" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "1" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(69) "150130937545296572356771972164254457814047970568738777235893533016064" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(15) "184717953466368" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} diff --git a/ext/gmp/tests/overloading_with_float_fractional.phpt b/ext/gmp/tests/overloading_with_float_fractional.phpt new file mode 100644 index 0000000000000..fc078eeec3e98 --- /dev/null +++ b/ext/gmp/tests/overloading_with_float_fractional.phpt @@ -0,0 +1,132 @@ +--TEST-- +GMP operator overloading support for float with fractional is deprecated +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump($num - 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num * 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num / 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num % 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num ** 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + var_dump($num | 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num & 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num ^ 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num << 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($num >> 42.5); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(2) "84" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(4) "1764" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(1) "1" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(69) "150130937545296572356771972164254457814047970568738777235893533016064" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(2) "42" +} + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} +object(GMP)#2 (1) { + ["num"]=> + string(15) "184717953466368" +} +object(GMP)#2 (1) { + ["num"]=> + string(1) "0" +} From be6bdedb8adbc1868f50eaf272f21aa04ff56e54 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 25 Oct 2024 21:50:03 +0100 Subject: [PATCH 11/11] Remove BC breaks from previous implementation --- ext/gmp/gmp.c | 20 ++++++++++++++++++- .../tests/overloading_with_float_string.phpt | 15 +++----------- .../overloading_with_non_numeric_string.phpt | 15 +++----------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index f59fcc8e0d5a8..c9603c8fb21e8 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -339,7 +339,22 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { if (UNEXPECTED(!IS_GMP(op2))) { - goto typeof_op_failure; + // For PHP 8.3 and up use zend_try_get_long() + switch (Z_TYPE_P(op2)) { + case IS_DOUBLE: + shift = zval_get_long(op2); + if (UNEXPECTED(EG(exception))) { + return FAILURE; + } + break; + case IS_STRING: + if (is_numeric_str_function(Z_STR_P(op2), &shift, NULL) != IS_LONG) { + goto valueof_op_failure; + } + break; + default: + goto typeof_op_failure; + } } else { // TODO We shouldn't cast the GMP object to int here shift = zval_get_long(op2); @@ -399,6 +414,9 @@ typeof_op_failure: ; } zend_type_error("Unsupported operand types: %s %s %s", zend_zval_type_name(op1), op_sigil, zend_zval_type_name(op2)); return FAILURE; +valueof_op_failure: + zend_value_error("Number is not an integer string"); + return FAILURE; } #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \ diff --git a/ext/gmp/tests/overloading_with_float_string.phpt b/ext/gmp/tests/overloading_with_float_string.phpt index f715d5740b382..b01c99e969c23 100644 --- a/ext/gmp/tests/overloading_with_float_string.phpt +++ b/ext/gmp/tests/overloading_with_float_string.phpt @@ -76,18 +76,9 @@ ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string -object(GMP)#3 (1) { - ["num"]=> - string(4) "1764" -} ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string -object(GMP)#2 (1) { - ["num"]=> - string(3) "168" -} -object(GMP)#2 (1) { - ["num"]=> - string(2) "10" -} +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string diff --git a/ext/gmp/tests/overloading_with_non_numeric_string.phpt b/ext/gmp/tests/overloading_with_non_numeric_string.phpt index 3068c31dd8af1..2f6c2d072342c 100644 --- a/ext/gmp/tests/overloading_with_non_numeric_string.phpt +++ b/ext/gmp/tests/overloading_with_non_numeric_string.phpt @@ -76,18 +76,9 @@ ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string -object(GMP)#3 (1) { - ["num"]=> - string(1) "1" -} ValueError: Number is not an integer string ValueError: Number is not an integer string ValueError: Number is not an integer string -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} -object(GMP)#2 (1) { - ["num"]=> - string(2) "42" -} +ValueError: Number is not an integer string +ValueError: Number is not an integer string +ValueError: Number is not an integer string