@@ -334,25 +334,71 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
334334}
335335/* }}} */
336336
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+ goto typeof_op_failure ;
343+ } else {
344+ // TODO We shouldn't cast the GMP object to int here
345+ shift = zval_get_long (op2 );
346+ }
347+ } else {
348+ shift = Z_LVAL_P (op2 );
349+ }
339350
340351 if (shift < 0 ) {
341352 zend_throw_error (
342353 zend_ce_value_error , "%s must be greater than or equal to 0" ,
343354 opcode == ZEND_POW ? "Exponent" : "Shift"
344355 );
345356 ZVAL_UNDEF (return_value );
346- return ;
357+ return FAILURE ;
347358 } else {
348359 mpz_ptr gmpnum_op , gmpnum_result ;
349360 gmp_temp_t temp ;
350361
351- FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
362+ /* Inline FETCH_GMP_ZVAL(gmpnum_op, op1, temp, 1); as cannot use RETURN_THROWS() */
363+ if (UNEXPECTED (!IS_GMP (op1 ))) {
364+ if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_LONG )) {
365+ goto typeof_op_failure ;
366+ }
367+ mpz_init (temp .num );
368+ mpz_set_si (temp .num , Z_LVAL_P (op1 ));
369+ temp .is_used = 1 ;
370+ gmpnum_op = temp .num ;
371+ } else {
372+ gmpnum_op = GET_GMP_FROM_ZVAL (op1 );
373+ temp .is_used = 0 ;
374+ }
352375 INIT_GMP_RETVAL (gmpnum_result );
353376 op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
354377 FREE_GMP_TEMP (temp );
378+ return SUCCESS ;
355379 }
380+
381+ typeof_op_failure :
382+ ; /* Blank statement */
383+ /* Returning FAILURE without throwing an exception would emit the
384+ * Unsupported operand types: GMP OP TypeOfOp2
385+ * However, this leads to the engine trying to interpret the GMP object as an integer
386+ * and doing the operation that way, which is not something we want. */
387+ const char * op_sigil ;
388+ switch (opcode ) {
389+ case ZEND_POW :
390+ op_sigil = "**" ;
391+ break ;
392+ case ZEND_SL :
393+ op_sigil = "<<" ;
394+ break ;
395+ case ZEND_SR :
396+ op_sigil = ">>" ;
397+ break ;
398+ EMPTY_SWITCH_DEFAULT_CASE ();
399+ }
400+ zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
401+ return FAILURE ;
356402}
357403
358404#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
381427 case ZEND_MUL :
382428 DO_BINARY_UI_OP (mpz_mul );
383429 case ZEND_POW :
384- shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
385- return SUCCESS ;
430+ return shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
386431 case ZEND_DIV :
387432 DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
388433 case ZEND_MOD :
389434 DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
390435 case ZEND_SL :
391- shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
392- return SUCCESS ;
436+ return shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
393437 case ZEND_SR :
394- shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
395- return SUCCESS ;
438+ return shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
396439 case ZEND_BW_OR :
397440 DO_BINARY_OP (mpz_ior );
398441 case ZEND_BW_AND :
0 commit comments