Skip to content

Instantly share code, notes, and snippets.

@headius
Created August 2, 2012 20:35
Show Gist options
  • Select an option

  • Save headius/3240405 to your computer and use it in GitHub Desktop.

Select an option

Save headius/3240405 to your computer and use it in GitHub Desktop.
# 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