Created
August 2, 2012 20:35
-
-
Save headius/3240405 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # progam name used by Colin Bartlett: jruby-test-long-overflow-patches.rb | |
| # colinb2r at GitHub, created 2012-07-29, amended 2012-07-31, 2012-08-02; | |
| # Are the following 2 statements correct? | |
| # | |
| # 1. The Java specification means that we can rely on Integer: | |
| # "+", "-" to "overflow" in a well-defined way; | |
| # "*" to "overflow" in a well-defined way; | |
| # "/" to "overflow" in a well-defined way. | |
| # | |
| # 2. https://www.securecoding.cert.org/confluence/display/java/NUM00-J.+Detect+or+prevent+integer+overflow | |
| # states that according to | |
| # ß4.2.1, "Integral Types and Values" [JLS 2005] | |
| # at | |
| # http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.1 | |
| # the representation of fixed bits Java integers is "N-bit signed two's-complement" | |
| # where N= 8, 16, 32, 64. | |
| # This means that if we want to we can detect the sign of a Java "N-bit" integer | |
| # by checking the "sign bit" of the integer. | |
| # | |
| # | |
| # Note that the article at www.securecoding.cert.org says there are | |
| # three main techniques for detecting unintended integer overflow: | |
| # * Precondition testing: Check the inputs to each arithmetic operator | |
| # to ensure that overflow cannot occur. | |
| # * Upcasting: Cast the inputs to the next larger primitive integer type | |
| # and perform the arithmetic in the larger size. | |
| # * BigInteger: Convert the inputs into objects of type BigInteger | |
| # and perform all arithmetic using BigInteger methods. | |
| # | |
| # But there is a fourth possibility for Java integer "+", "-", "*", "/": | |
| # calculate the result, then check the inputs and result for an overflow. | |
| # This isn't useful for "/", but it seems to give faster overflow checks | |
| # for "+", "-", "*". | |
| # | |
| # And the check for multiplication works (with only a bit of fiddling) | |
| # even when the overflow result of "*" is not well-defined! | |
| puts | |
| puts "Test some JRuby Java long integer overflow problems." | |
| puts "This runs some JRuby methods which try to test if JRuby" | |
| puts "is correctly detecting some Java long integer overflows." | |
| puts | |
| puts "Testing against MRI-1.9.1 results are 39 ok, 0 not ok." | |
| puts "Testing against jruby-1.6.7.2 results are 28 ok, 11 not ok." | |
| puts "Testing against jruby-1.7.0.preview1 results are 28 ok, 11 not ok." | |
| puts " Only difference is JRuby 1.6.6 and 1.7.0 fail on different op_mul tests." | |
| puts "Testing against jruby-1.7.0.preview1-patched-2012-08-02_t_16-09_UTC" | |
| puts " results are 36 ok, 3 not ok." | |
| puts " The 3 not ok are for: RubyRange.java#each, RubyRange.java#fixnumStep." | |
| puts " Range#each problem: http://jira.codehaus.org/browse/JRUBY-6779" | |
| puts | |
| puts "program=#{$0}; date_time=#{Time.now.to_s}" | |
| puts "RUBY: PLATFORM=#{RUBY_PLATFORM}; VERSION=#{RUBY_VERSION};" \ | |
| " PATCHLEVEL=#{RUBY_PATCHLEVEL}; RELEASE_DATE= #{RUBY_RELEASE_DATE};" | |
| # calculate minimum and maximum Java long integer values | |
| minv = 65536 | |
| minv *= minv * minv * (-minv / 2) | |
| maxv = -(minv + 1) | |
| puts | |
| puts "minv = #{minv}, maxv = #{maxv};" | |
| # initialize test result counts and utility test methods | |
| $numOK = $numNotOK = $numExceptions = 0 | |
| # | |
| def inzpect(vv) | |
| # for Integers show class as well as value | |
| rv = "" | |
| case vv | |
| when Array then | |
| rv << "[" | |
| isFirst = true | |
| vv.each do |v| | |
| if isFirst then isFirst = false else rv << ", " end | |
| rv << inzpect(v) | |
| end | |
| rv << "]" | |
| when Range then | |
| rv << inzpect(vv.begin) << | |
| (if vv.exclude_end? then "..." else ".." end) << | |
| inzpect(vv.end) | |
| else | |
| rv = vv.class.to_s + ":" | |
| rv << vv.inspect | |
| end | |
| rv | |
| end | |
| # | |
| def process_test_result(actual_result, expected_result, | |
| test_text = nil, exceptionv = nil) | |
| # Returns nil if actual_result and expected_result values are same | |
| # *and* actual_result.class == expected_result.class. | |
| # Otherwise returns information about the not equal values. | |
| # If test_text != nil also prints result of test | |
| # and increases appropriate count by 1. | |
| rv = nil | |
| if exceptionv then | |
| rv = exceptionv.inspect | |
| elsif actual_result.kind_of?(Array) && expected_result.kind_of?(Array) then | |
| ii2 = if expected_result.size < actual_result.size then | |
| expected_result.size | |
| else | |
| actual_result.size | |
| end | |
| ii2.times do |ii| | |
| if (rv = process_test_result(actual_result[ii], expected_result[ii])) then | |
| rv[0, 0] = "A != E at #{ii}: " | |
| if actual_result.size != actual_result.size then | |
| rv[0, 0] = "sizes: A=#{actual_result.size} !=" \ | |
| " E=#{expected_result.size}; " | |
| end | |
| break | |
| end | |
| end | |
| if rv.nil? && actual_result.size != expected_result.size then | |
| rv = "sizes: A=#{actual_result.size} != E=#{expected_result.size}" | |
| end | |
| elsif actual_result.class == expected_result.class && | |
| actual_result == expected_result then | |
| rv = nil | |
| else | |
| rv = inzpect(actual_result) << " != " << inzpect(expected_result) | |
| end | |
| if test_text then | |
| test_text += " #=> " | |
| if exceptionv then | |
| $numExceptions += 1 | |
| msgv = "#EXCPT " << test_text << exceptionv.inspect | |
| elsif rv then | |
| $numNotOK += 1 | |
| msgv = "#NotOK " << test_text << rv | |
| else | |
| $numOK += 1 | |
| msgv = "# OK " << test_text << inzpect(actual_result) | |
| end | |
| puts msgv | |
| end | |
| return rv | |
| end | |
| ######################### Tests | |
| puts | |
| puts "#Test RubyFixnum.java op_mul" | |
| # | |
| def test_op_mul(x, y, ev ) | |
| txtv = inzpect(x) + " * " + inzpect(y) | |
| av = xceptv = nil | |
| (av = x * y) rescue (xceptv = $!) | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| a = minv + 6; b = 3; c = a + a + a | |
| test_op_mul( a, b, c ) | |
| test_op_mul( b, a, c ) | |
| b = -b; c = -c | |
| test_op_mul( a, b, c ) | |
| test_op_mul( b, a, c ) | |
| a = maxv - 6; b = 3; c = a + a + a | |
| test_op_mul( a, b, c ) | |
| test_op_mul( b, a, c ) | |
| b = -b; c = -c | |
| test_op_mul( a, b, c ) | |
| test_op_mul( b, a, c ) | |
| # | |
| test_op_mul( minv, 0, 0 ) | |
| test_op_mul( minv, 1, minv ) | |
| test_op_mul( minv, -1, -minv ) | |
| test_op_mul( 0, minv, 0 ) | |
| puts "# next test failed by: 1.7.0.preview1 which returns Bignum not Fixnum" | |
| test_op_mul( 1, minv, minv ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2 " | |
| test_op_mul( -1, minv, -minv ) | |
| puts | |
| puts "#Test RubyFixnum.java idivLong" | |
| # | |
| def test_idivLong(x, y, ev ) | |
| txtv = inzpect(x) + " / " + inzpect(y) | |
| av = xceptv = nil | |
| (av = x / y) rescue (xceptv = $!) | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| test_idivLong( minv, 1, minv ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| test_idivLong( minv, -1, -minv ) | |
| puts | |
| puts "#Test RubyFixnum.java divmodFixnum" | |
| # | |
| def test_divmodFixnum(x, y, ev ) | |
| txtv = inzpect(x) + " divmod " + inzpect(y) | |
| av = xceptv = nil | |
| (av = x.divmod(y)) rescue (xceptv = $!) | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| test_divmodFixnum( minv, 1, [ minv, 0] ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| test_divmodFixnum( minv, -1, [-minv, 0] ) | |
| puts | |
| puts "#Test RubyInteger.java succ" | |
| # | |
| def test_succ(x, ev ) | |
| txtv = inzpect(x) + ".succ" | |
| av = xceptv = nil | |
| (av = x.succ) rescue (xceptv = $!) | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| test_succ( maxv - 1, maxv - 0 ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| test_succ( maxv - 0, maxv + 1 ) | |
| puts | |
| puts "#Test RubyRange.java to_a" | |
| # | |
| def test_to_a(rng, ev ) | |
| txtv = "(" + inzpect(rng) + ").to_a" | |
| av = xceptv = nil | |
| (av = rng.to_a) rescue (xceptv = $!) | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| ev = [maxv - 1] | |
| test_to_a( maxv - 1 .. maxv - 1, ev ) | |
| ev = [maxv - 1] | |
| test_to_a( maxv - 1 ... maxv - 0, ev ) | |
| puts "# rather to my surprise next test is passed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| puts "# but I think I now understand why they pass it " | |
| ev = [maxv - 0] | |
| test_to_a( maxv - 0 .. maxv - 0, ev ) | |
| puts | |
| puts "#Test RubyRange.java each" | |
| puts "#* use Range#each to give array of yielded values, compare with expected array;" | |
| puts "#* Range#each might have infinite loop, so this limits number of iterations;" | |
| # | |
| def test_each(rng, maxCount, ev ) | |
| txtv = "(" + inzpect(rng) + ").each" | |
| av = xceptv = nil | |
| begin | |
| av = [] | |
| kt = 0 | |
| rng.each do |iv| | |
| av << iv | |
| kt += 1 | |
| break if kt >= maxCount | |
| end | |
| rescue | |
| xceptv = $! | |
| end | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| maxCount = 8 | |
| ev = [maxv - 1] | |
| test_each( maxv - 1 .. maxv - 1, maxCount, ev ) | |
| ev = [maxv - 1] | |
| test_each( maxv - 1 ... maxv - 0, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [maxv - 0] | |
| test_each( maxv - 0 .. maxv - 0, maxCount, ev ) | |
| puts | |
| puts "#Test RubyRange.java fixnumStep" | |
| puts "#* use Range#step to give array of yielded values, compare with expected array;" | |
| puts "#* Range#step might have infinite loop, so this limits number of iterations;" | |
| # | |
| def test_range_fixnumStep(rng, stepv, maxCount, ev ) | |
| txtv = "(" + inzpect(rng) + ").step(" + inzpect(stepv) + ")" | |
| av = xceptv = nil | |
| begin | |
| av = [] | |
| kt = 0 | |
| rng.step(stepv) do |iv| | |
| av << iv | |
| kt += 1 | |
| break if kt >= maxCount | |
| end | |
| rescue | |
| xceptv = $! | |
| end | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| maxCount = 8 | |
| # note that in Range#step(stepv) stepv can't be 0 or negative | |
| puts "#" | |
| ev = [maxv - 1] | |
| test_range_fixnumStep( maxv - 1 .. maxv - 1, 1, maxCount, ev ) | |
| test_range_fixnumStep( maxv - 1 ... maxv - 0, 1, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [maxv - 1, maxv - 0] | |
| test_range_fixnumStep( maxv - 1 .. maxv - 0, 1, maxCount, ev ) | |
| puts "#" | |
| ev = [maxv - 9, maxv - 6] | |
| test_range_fixnumStep( maxv - 9 .. maxv - 5, 3, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [maxv - 5, maxv - 2] | |
| test_range_fixnumStep( maxv - 5 .. maxv - 1, 3, maxCount, ev ) | |
| puts | |
| puts "#Test RubyNumeric.java fixnumStep" | |
| puts "#* use Fixnum#step to give array of yielded values, compare with expected array;" | |
| puts "#* Fixnum#step might have infinite loop, so this limits number of iterations;" | |
| # | |
| def test_numeric_fixnumStep(startv, stopv, stepv, maxCount, ev ) | |
| txtv = inzpect(startv) + ".step(" + inzpect(stopv) + ", " + inzpect(stepv) + ")" | |
| av = xceptv = nil | |
| begin | |
| av = [] | |
| kt = 0 | |
| startv.step( stopv, stepv ) do |iv| | |
| av << iv | |
| kt += 1 | |
| break if kt >= maxCount | |
| end | |
| rescue | |
| xceptv = $! | |
| end | |
| process_test_result(av, ev, txtv, xceptv) | |
| end | |
| # | |
| maxCount = 8 | |
| puts "#" | |
| ev = [maxv - 1] | |
| test_numeric_fixnumStep( maxv - 1, maxv - 1, 1, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [maxv - 1, maxv - 0] | |
| test_numeric_fixnumStep( maxv - 1, maxv - 0, 1, maxCount, ev ) | |
| puts "#" | |
| ev = [maxv - 9, maxv - 6] | |
| test_numeric_fixnumStep( maxv - 9, maxv - 5, 3, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [maxv - 5, maxv - 2] | |
| test_numeric_fixnumStep( maxv - 5, maxv - 1, 3, maxCount, ev ) | |
| # | |
| puts "#" | |
| ev = [minv + 1] | |
| test_numeric_fixnumStep( minv + 1, minv + 1, -1, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [minv + 1, minv + 0] | |
| test_numeric_fixnumStep( minv + 1, minv + 0, -1, maxCount, ev ) | |
| puts "#" | |
| ev = [minv + 9, minv + 6] | |
| test_numeric_fixnumStep( minv + 9, minv + 5, -3, maxCount, ev ) | |
| puts "# next test failed by: 1.6.6, 1.6.7.2, 1.7.0.preview1 " | |
| ev = [minv + 5, minv + 2] | |
| test_numeric_fixnumStep( minv + 5, minv + 1, -3, maxCount, ev ) | |
| puts | |
| puts "# numOK= #{$numOK}, numNotOK= #{$numNotOK}, numExceptions= #{$numExceptions};" | |
| puts | |
| exit unless RUBY_PLATFORM.to_s.downcase.index("java") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment