@@ -334,25 +334,89 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
334
334
}
335
335
/* }}} */
336
336
337
- static void shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , zend_uchar opcode ) {
338
- zend_long shift = zval_get_long (op2 );
337
+ static zend_result shift_operator_helper (gmp_binary_ui_op_t op , zval * return_value , zval * op1 , zval * op2 , zend_uchar opcode ) {
338
+ zend_long shift = 0 ;
339
+
340
+ if (UNEXPECTED (Z_TYPE_P (op2 ) != IS_LONG )) {
341
+ if (UNEXPECTED (!IS_GMP (op2 ))) {
342
+ // For PHP 8.3 and up use zend_try_get_long()
343
+ switch (Z_TYPE_P (op2 )) {
344
+ case IS_DOUBLE :
345
+ shift = zval_get_long (op2 );
346
+ if (UNEXPECTED (EG (exception ))) {
347
+ return FAILURE ;
348
+ }
349
+ break ;
350
+ case IS_STRING :
351
+ if (is_numeric_str_function (Z_STR_P (op2 ), & shift , NULL ) != IS_LONG ) {
352
+ goto valueof_op_failure ;
353
+ }
354
+ break ;
355
+ default :
356
+ goto typeof_op_failure ;
357
+ }
358
+ } else {
359
+ // TODO We shouldn't cast the GMP object to int here
360
+ shift = zval_get_long (op2 );
361
+ }
362
+ } else {
363
+ shift = Z_LVAL_P (op2 );
364
+ }
339
365
340
366
if (shift < 0 ) {
341
367
zend_throw_error (
342
368
zend_ce_value_error , "%s must be greater than or equal to 0" ,
343
369
opcode == ZEND_POW ? "Exponent" : "Shift"
344
370
);
345
371
ZVAL_UNDEF (return_value );
346
- return ;
372
+ return FAILURE ;
347
373
} else {
348
374
mpz_ptr gmpnum_op , gmpnum_result ;
349
375
gmp_temp_t temp ;
350
376
351
- FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
377
+ /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp()
378
+ * as we want to handle the emitted exception ourself. */
379
+ if (UNEXPECTED (!IS_GMP (op1 ))) {
380
+ if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_LONG )) {
381
+ goto typeof_op_failure ;
382
+ }
383
+ mpz_init (temp .num );
384
+ mpz_set_si (temp .num , Z_LVAL_P (op1 ));
385
+ temp .is_used = 1 ;
386
+ gmpnum_op = temp .num ;
387
+ } else {
388
+ gmpnum_op = GET_GMP_FROM_ZVAL (op1 );
389
+ temp .is_used = 0 ;
390
+ }
352
391
INIT_GMP_RETVAL (gmpnum_result );
353
392
op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
354
393
FREE_GMP_TEMP (temp );
394
+ return SUCCESS ;
395
+ }
396
+
397
+ typeof_op_failure : ;
398
+ /* Returning FAILURE without throwing an exception would emit the
399
+ * Unsupported operand types: GMP OP TypeOfOp2
400
+ * However, this leads to the engine trying to interpret the GMP object as an integer
401
+ * and doing the operation that way, which is not something we want. */
402
+ const char * op_sigil ;
403
+ switch (opcode ) {
404
+ case ZEND_POW :
405
+ op_sigil = "**" ;
406
+ break ;
407
+ case ZEND_SL :
408
+ op_sigil = "<<" ;
409
+ break ;
410
+ case ZEND_SR :
411
+ op_sigil = ">>" ;
412
+ break ;
413
+ EMPTY_SWITCH_DEFAULT_CASE ();
355
414
}
415
+ zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
416
+ return FAILURE ;
417
+ valueof_op_failure :
418
+ zend_value_error ("Number is not an integer string" );
419
+ return FAILURE ;
356
420
}
357
421
358
422
#define DO_BINARY_UI_OP_EX (op , uop , check_b_zero ) \
@@ -381,18 +445,15 @@ static zend_result gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op
381
445
case ZEND_MUL :
382
446
DO_BINARY_UI_OP (mpz_mul );
383
447
case ZEND_POW :
384
- shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
385
- return SUCCESS ;
448
+ return shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
386
449
case ZEND_DIV :
387
450
DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
388
451
case ZEND_MOD :
389
452
DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
390
453
case ZEND_SL :
391
- shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
392
- return SUCCESS ;
454
+ return shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
393
455
case ZEND_SR :
394
- shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
395
- return SUCCESS ;
456
+ return shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
396
457
case ZEND_BW_OR :
397
458
DO_BINARY_OP (mpz_ior );
398
459
case ZEND_BW_AND :
@@ -619,6 +680,13 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
619
680
case IS_STRING : {
620
681
return convert_zstr_to_gmp (gmpnumber , Z_STR_P (val ), base , arg_pos );
621
682
}
683
+ case IS_NULL :
684
+ /* Just reject null for operator overloading */
685
+ if (arg_pos == 0 ) {
686
+ zend_type_error ("Number must be of type GMP|string|int, %s given" , zend_zval_type_name (val ));
687
+ return FAILURE ;
688
+ }
689
+ ZEND_FALLTHROUGH ;
622
690
default : {
623
691
zend_long lval ;
624
692
if (!zend_parse_arg_long_slow (val , & lval , arg_pos )) {
0 commit comments