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