diff options
-rw-r--r-- | ChangeLog | 29 | ||||
-rw-r--r-- | bignum.c | 31 | ||||
-rw-r--r-- | include/ruby/intern.h | 1 | ||||
-rw-r--r-- | numeric.c | 66 | ||||
-rw-r--r-- | test/ruby/test_bignum.rb | 36 | ||||
-rw-r--r-- | test/ruby/test_integer.rb | 36 |
6 files changed, 164 insertions, 35 deletions
@@ -1,3 +1,32 @@ +Sat Dec 23 00:08:00 2012 Kenta Murata <[email protected]> + + * include/ruby/intern.h: add the prototype declaration of + rb_num_coerce_bit. + + * numeric.c (rb_num_coerce_bit): the new coerce function for bitwise + binary operation. + + * bignum.c (rb_big_and): use coerce to convert the argument, which isn't + a Fixnum nor a Bignum, to the corresponding Integer object so that + bitwise operations can support Integer-mimic objects. + [Bug #1792] [ruby-core:39491] + + * bignum.c (rb_big_or): ditto. + + * bignum.c (rb_big_xor): ditto. + + * numeric.c (bit_coerce): ditto. + + * numeric.c (fix_and): ditto. + + * numeric.c (fix_or): ditto. + + * numeric.c (fix_xor): ditto. + + * test/ruby/test_integer.rb: add tests for the above changes. + + * test/ruby/test_bignum.rb: ditto. + Sun Dec 23 00:04:54 2012 Nobuyoshi Nakada <[email protected]> * internal.h (QUOTE, QUOTE_ID): quote unprintable chars in strings and @@ -3201,18 +3201,6 @@ rb_big_pow(VALUE x, VALUE y) return DBL2NUM(pow(rb_big2dbl(x), d)); } -static inline VALUE -bit_coerce(VALUE x) -{ - while (!FIXNUM_P(x) && !RB_TYPE_P(x, T_BIGNUM)) { - rb_raise(rb_eTypeError, - "can't convert %s into Integer for bitwise arithmetic", - rb_obj_classname(x)); - x = rb_to_int(x); - } - return x; -} - static VALUE bigand_int(VALUE x, long y) { @@ -3272,8 +3260,13 @@ rb_big_and(VALUE xx, VALUE yy) long i, l1, l2; char sign; + if (!FIXNUM_P(yy) && !RB_TYPE_P(yy, T_BIGNUM)) { + return rb_num_coerce_bit(xx, yy, '&'); + } + x = xx; - y = bit_coerce(yy); + y = yy; + if (!RBIGNUM_SIGN(x)) { x = rb_big_clone(x); get2comp(x); @@ -3363,8 +3356,12 @@ rb_big_or(VALUE xx, VALUE yy) long i, l1, l2; char sign; + if (!FIXNUM_P(yy) && !RB_TYPE_P(yy, T_BIGNUM)) { + return rb_num_coerce_bit(xx, yy, '|'); + } + x = xx; - y = bit_coerce(yy); + y = yy; if (!RBIGNUM_SIGN(x)) { x = rb_big_clone(x); @@ -3455,8 +3452,12 @@ rb_big_xor(VALUE xx, VALUE yy) long i, l1, l2; char sign; + if (!FIXNUM_P(yy) && !RB_TYPE_P(yy, T_BIGNUM)) { + return rb_num_coerce_bit(xx, yy, '^'); + } + x = xx; - y = bit_coerce(yy); + y = yy; if (!RBIGNUM_SIGN(x)) { x = rb_big_clone(x); diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 9e77533517..6b8cfc1b31 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -519,6 +519,7 @@ NORETURN(void rb_num_zerodiv(void)); VALUE rb_num_coerce_bin(VALUE, VALUE, ID); VALUE rb_num_coerce_cmp(VALUE, VALUE, ID); VALUE rb_num_coerce_relop(VALUE, VALUE, ID); +VALUE rb_num_coerce_bit(VALUE, VALUE, ID); VALUE rb_num2fix(VALUE); VALUE rb_fix2str(VALUE, int); VALUE rb_dbl_cmp(double, double); @@ -3165,15 +3165,29 @@ fix_rev(VALUE num) return ~num | FIXNUM_FLAG; } -static VALUE -bit_coerce(VALUE x) -{ - while (!FIXNUM_P(x) && !RB_TYPE_P(x, T_BIGNUM)) { - rb_raise(rb_eTypeError, - "can't convert %s into Integer for bitwise arithmetic", - rb_obj_classname(x)); +static int +bit_coerce(VALUE *x, VALUE *y, int err) +{ + if (!FIXNUM_P(*y) && !RB_TYPE_P(*y, T_BIGNUM)) { + do_coerce(x, y, err); + if (!FIXNUM_P(*x) && !RB_TYPE_P(*x, T_BIGNUM) + && !FIXNUM_P(*y) && !RB_TYPE_P(*y, T_BIGNUM)) { + if (!err) return FALSE; + rb_raise(rb_eTypeError, + "%s can't be coerced into %s for bitwise arithmetic", + rb_special_const_p(*y) ? + RSTRING_PTR(rb_inspect(*y)) : rb_obj_classname(*y), + rb_obj_classname(*x)); + } } - return x; + return TRUE; +} + +VALUE +rb_num_coerce_bit(VALUE x, VALUE y, ID func) +{ + bit_coerce(&x, &y, TRUE); + return rb_funcall(x, func, 1, y); } /* @@ -3186,13 +3200,17 @@ bit_coerce(VALUE x) static VALUE fix_and(VALUE x, VALUE y) { - long val; + if (FIXNUM_P(y)) { + long val = FIX2LONG(x) & FIX2LONG(y); + return LONG2NUM(val); + } - if (!FIXNUM_P(y = bit_coerce(y))) { + if (RB_TYPE_P(y, T_BIGNUM)) { return rb_big_and(y, x); } - val = FIX2LONG(x) & FIX2LONG(y); - return LONG2NUM(val); + + bit_coerce(&x, &y, TRUE); + return rb_funcall(x, rb_intern("&"), 1, y); } /* @@ -3205,13 +3223,17 @@ fix_and(VALUE x, VALUE y) static VALUE fix_or(VALUE x, VALUE y) { - long val; + if (FIXNUM_P(y)) { + long val = FIX2LONG(x) | FIX2LONG(y); + return LONG2NUM(val); + } - if (!FIXNUM_P(y = bit_coerce(y))) { + if (RB_TYPE_P(y, T_BIGNUM)) { return rb_big_or(y, x); } - val = FIX2LONG(x) | FIX2LONG(y); - return LONG2NUM(val); + + bit_coerce(&x, &y, TRUE); + return rb_funcall(x, rb_intern("|"), 1, y); } /* @@ -3224,13 +3246,17 @@ fix_or(VALUE x, VALUE y) static VALUE fix_xor(VALUE x, VALUE y) { - long val; + if (FIXNUM_P(y)) { + long val = FIX2LONG(x) ^ FIX2LONG(y); + return LONG2NUM(val); + } - if (!FIXNUM_P(y = bit_coerce(y))) { + if (RB_TYPE_P(y, T_BIGNUM)) { return rb_big_xor(y, x); } - val = FIX2LONG(x) ^ FIX2LONG(y); - return LONG2NUM(val); + + bit_coerce(&x, &y, TRUE); + return rb_funcall(x, rb_intern("^"), 1, y); } static VALUE fix_lshift(long, unsigned long); diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index 1ff23e2a00..4af3748344 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -654,4 +654,40 @@ class TestBignum < Test::Unit::TestCase def test_frozen assert_equal(true, (2**100).frozen?) end + + def test_bitwise_and_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { T1024 & obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(T1024 & 10, T1024 & obj) + end + + def test_bitwise_or_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { T1024 | obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(T1024 | 10, T1024 | obj) + end + + def test_bitwise_xor_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { T1024 ^ obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(T1024 ^ 10, T1024 ^ obj) + end end diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index 3f4add7f15..655f8bb1a5 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -205,4 +205,40 @@ class TestInteger < Test::Unit::TestCase assert_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1)) assert_equal(Bignum, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1).class) end + + def test_bitwise_and_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { 3 & obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(3 & 10, 3 & obj) + end + + def test_bitwise_or_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { 3 | obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(3 | 10, 3 | obj) + end + + def test_bitwise_xor_with_integer_mimic_object + def (obj = Object.new).to_int + 10 + end + assert_raise(TypeError, '[ruby-core:39491]') { 3 ^ obj } + + def obj.coerce(other) + [other, 10] + end + assert_equal(3 ^ 10, 3 ^ obj) + end end |