なかだです。

At Wed, 18 Jul 2007 12:42:11 +0900,
Tanaka Akira wrote in [ruby-dev:31244]:
> -O0 で、以下のようにすると SEGV になります。
(ry

こんなところでどうでしょうか。


Index: bignum.c =================================================================== --- bignum.c (revision 12812) +++ bignum.c (working copy) @@ -1530,5 +1537,15 @@ bdigbitsize(BDIGIT x) } -static VALUE rb_big_rshift(VALUE,VALUE); +static VALUE big_lshift(VALUE, unsigned int); +static VALUE big_rshift(VALUE, unsigned int); + +static VALUE big_shift(VALUE x, int n) +{ + if (n < 0) + return big_lshift(x, (unsigned int)n); + else if (n > 0) + return big_rshift(x, (unsigned int)n); + return x; +} /* @@ -1559,5 +1576,5 @@ rb_big_quo(VALUE x, VALUE y) ex += bdigbitsize(BDIGITS(x)[RBIGNUM(x)->len - 1]); ex -= 2 * DBL_BIGDIG * BITSPERDIG; - if (ex) x = rb_big_rshift(x, INT2FIX(ex)); + if (ex) x = big_shift(x, ex); switch (TYPE(y)) { @@ -1568,5 +1585,5 @@ rb_big_quo(VALUE x, VALUE y) ey += bdigbitsize(BDIGITS(y)[RBIGNUM(y)->len - 1]); ey -= DBL_BIGDIG * BITSPERDIG; - if (ey) y = rb_big_rshift(y, INT2FIX(ey)); + if (ey) y = big_shift(y, ey); bignum: bigdivrem(x, y, &z, 0); @@ -1659,4 +1676,5 @@ rb_big_pow(VALUE x, VALUE y) double d; SIGNED_VALUE yy; + int odd = 0; if (y == INT2FIX(0)) return INT2FIX(1); @@ -1669,4 +1687,5 @@ rb_big_pow(VALUE x, VALUE y) rb_warn("in a**b, b may be too big"); d = rb_big2dbl(y); + odd = rb_big_odd_p(y); break; @@ -1679,4 +1698,5 @@ rb_big_pow(VALUE x, VALUE y) if (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024) { rb_warn("in a**b, b may be too big"); + odd = yy & 1; d = (double)yy; break; @@ -1696,5 +1716,12 @@ rb_big_pow(VALUE x, VALUE y) return rb_num_coerce_bin(x, y); } - return rb_float_new(pow(rb_big2dbl(x), d)); + d = pow(rb_big2dbl(x), d); +#ifdef __linux__ + if (isnan(d)) { + d = (RBIGNUM(x)->sign || !odd) ? +1 : -1; + d /= 0.0; + } +#endif + return rb_float_new(d); } @@ -1871,4 +1898,17 @@ rb_big_xor(VALUE xx, VALUE yy) } +static VALUE +check_shift_width(VALUE y, VALUE x) +{ + long len = RBIGNUM(y)->len; + if (len) { + len = 1 << (len - 1) * BITSPERDIG; + if (len / BITSPERDIG > RBIGNUM(x)->len) { + return RBIGNUM(x)->sign ? INT2FIX(0) : INT2FIX(-1); + } + } + return Qnil; +} + /* * call-seq: @@ -1881,6 +1921,30 @@ VALUE rb_big_lshift(VALUE x, VALUE y) { + int shift; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (!RBIGNUM(y)->sign) { + VALUE t = check_shift_width(y, x); + if (!NIL_P(t)) return t; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (shift < 0) return big_rshift(x, -shift); + return big_lshift(x, shift); +} + +static VALUE +big_lshift(VALUE x, unsigned int shift) +{ BDIGIT *xds, *zds; - int shift = NUM2INT(y); int s1 = shift/BITSPERDIG; int s2 = shift%BITSPERDIG; @@ -1889,5 +1953,4 @@ rb_big_lshift(VALUE x, VALUE y) long len, i; - if (shift < 0) return rb_big_rshift(x, INT2FIX(-shift)); len = RBIGNUM(x)->len; z = bignew(len+s1+1, RBIGNUM(x)->sign); @@ -1913,9 +1976,33 @@ rb_big_lshift(VALUE x, VALUE y) */ -static VALUE +VALUE rb_big_rshift(VALUE x, VALUE y) { + int shift; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (RBIGNUM(y)->sign) { + VALUE t = check_shift_width(y, x); + if (!NIL_P(t)) return t; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (shift < 0) return big_lshift(x, -shift); + return big_rshift(x, shift); +} + +static VALUE +big_rshift(VALUE x, unsigned int shift) +{ BDIGIT *xds, *zds; - int shift = NUM2INT(y); long s1 = shift/BITSPERDIG; long s2 = shift%BITSPERDIG; @@ -1925,6 +2012,4 @@ rb_big_rshift(VALUE x, VALUE y) volatile VALUE save_x; - if (shift < 0) return rb_big_lshift(x, INT2FIX(-shift)); - if (s1 > RBIGNUM(x)->len) { if (RBIGNUM(x)->sign) Index: numeric.c =================================================================== --- numeric.c (revision 12812) +++ numeric.c (working copy) @@ -2592,5 +2600,6 @@ fix_xor(VALUE x, VALUE y) } -static VALUE fix_rshift(VALUE, VALUE); +static VALUE fix_lshift(long, unsigned long); +static VALUE fix_rshift(long, unsigned long); /* @@ -2602,15 +2611,23 @@ static VALUE fix_rshift(VALUE, VALUE); static VALUE -fix_lshift(VALUE x, VALUE y) +rb_fix_lshift(VALUE x, VALUE y) { long val, width; val = NUM2LONG(x); - width = NUM2LONG(y); + if (!FIXNUM_P(y)) + return rb_big_lshift(rb_int2big(val), y); + width = FIX2LONG(y); if (width < 0) - return fix_rshift(x, LONG2FIX(-width)); + return fix_rshift(val, (unsigned long)-width); + return fix_lshift(val, width); +} + +static VALUE +fix_lshift(long val, unsigned long width) +{ if (width > (sizeof(VALUE)*CHAR_BIT-1) || ((unsigned long)val)>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) { - return rb_big_lshift(rb_int2big(val), y); + return rb_big_lshift(rb_int2big(val), LONG2NUM(width)); } val = val << width; @@ -2626,13 +2643,21 @@ fix_lshift(VALUE x, VALUE y) static VALUE -fix_rshift(VALUE x, VALUE y) +rb_fix_rshift(VALUE x, VALUE y) { long i, val; - i = NUM2LONG(y); - if (i < 0) - return fix_lshift(x, LONG2FIX(-i)); - if (i == 0) return x; val = FIX2LONG(x); + if (!FIXNUM_P(y)) + return rb_big_rshift(rb_int2big(val), y); + i = FIX2LONG(y); + if (i == 0) return x; + if (i < 0) + return fix_lshift(val, (unsigned long)-i); + return fix_rshift(val, i); +} + +static VALUE +fix_rshift(long val, unsigned long i) +{ if (i >= sizeof(long)*CHAR_BIT-1) { if (val < 0) return INT2FIX(-1); @@ -2886,6 +2911,4 @@ static VALUE int_dotimes(VALUE num) { - VALUE val; - RETURN_ENUMERATOR(num, 0, 0); @@ -3106,6 +3129,6 @@ Init_Numeric(void) rb_define_method(rb_cFixnum, "[]", fix_aref, 1); - rb_define_method(rb_cFixnum, "<<", fix_lshift, 1); - rb_define_method(rb_cFixnum, ">>", fix_rshift, 1); + rb_define_method(rb_cFixnum, "<<", rb_fix_lshift, 1); + rb_define_method(rb_cFixnum, ">>", rb_fix_rshift, 1); rb_define_method(rb_cFixnum, "to_f", fix_to_f, 0); Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 12812) +++ include/ruby/intern.h (working copy) @@ -115,4 +116,5 @@ VALUE rb_big_or(VALUE, VALUE); VALUE rb_big_xor(VALUE, VALUE); VALUE rb_big_lshift(VALUE, VALUE); +VALUE rb_big_rshift(VALUE, VALUE); /* class.c */ VALUE rb_class_boot(VALUE); Index: test/ruby/test_integer.rb =================================================================== --- test/ruby/test_integer.rb (revision 12812) +++ test/ruby/test_integer.rb (working copy) @@ -234,4 +234,8 @@ class TestInteger < Test::Unit::TestCase } } + assert_equal(0, 1 << -0x40000000) + assert_equal(0, 1 << -0x40000000) + assert_equal(0, 1 << -0x80000000) + assert_equal(0, 1 << -0x80000001) end @@ -249,4 +253,5 @@ class TestInteger < Test::Unit::TestCase } } + assert_equal((1 << 0x40000000), (1 >> -0x40000000)) end
-- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦