From f77f2a2d746e6aa3dedc697220e880f12ca26e04 Mon Sep 17 00:00:00 2001 From: Igor Novokshonov Date: Tue, 8 Dec 2020 14:22:44 +0100 Subject: [PATCH 1/7] Ruby 2.7: Better support of Integer#[] with range arguments --- .../core/integer/element_reference_tags.txt | 12 ---- src/main/ruby/truffleruby/core/integer.rb | 71 ++++++++++++++++++- test/mri/excludes/TestInteger.rb | 1 - 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/spec/tags/core/integer/element_reference_tags.txt b/spec/tags/core/integer/element_reference_tags.txt index 8f3bb6b33cad..2047c01342b5 100644 --- a/spec/tags/core/integer/element_reference_tags.txt +++ b/spec/tags/core/integer/element_reference_tags.txt @@ -1,13 +1 @@ -fails:Integer#[] fixnum when index and length passed returns specified number of bits from specified position -fails:Integer#[] fixnum when index and length passed ensures n[i, len] equals to (n >> i) & ((1 << len) - 1) -fails:Integer#[] fixnum when index and length passed moves start position to the most significant bits when negative index passed -fails:Integer#[] fixnum when index and length passed ignores negative length -fails:Integer#[] fixnum when range passed returns bits specified by range -fails:Integer#[] fixnum when range passed ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1) -fails:Integer#[] fixnum when range passed ensures n[i..] equals to (n >> i) -fails:Integer#[] fixnum when range passed moves lower boundary to the most significant bits when negative value passed -fails:Integer#[] fixnum when range passed ignores negative upper boundary -fails:Integer#[] fixnum when range passed ignores upper boundary smaller than lower boundary fails:Integer#[] fixnum when range passed raises FloatDomainError if any boundary is infinity -fails:Integer#[] fixnum when range passed when passed (..i) returns 0 if all i bits equal 0 -fails:Integer#[] fixnum when range passed when passed (..i) raises ArgumentError if any of i bit equals 1 diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 84e99cf478c1..7c3827ffb73f 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -38,6 +38,7 @@ Object.deprecate_constant :Fixnum, :Bignum class Integer < Numeric + FIXNUM_MAX = 0x3fffffff # Have a copy in Integer of the Numeric version, as MRI does alias_method :remainder, :remainder @@ -59,9 +60,73 @@ def **(o) redo_coerced :**, o end - def [](index) - index = Primitive.rb_to_int(index) - index < 0 ? 0 : (self >> index) & 1 + def [](index, len = undefined) + def cmp(a, b) + return FIXNUM_MAX if Primitive.nil?(b) + return a - b + end + def fix_aref(num, idx) + val = Primitive.rb_num2long(num) + idx = Primitive.rb_to_int idx + if !Truffle::Type.fits_into_long?(idx) + if idx < 0 || val >= 0 + return 0 + else + return 1 + end + end + return 0 if idx < 0 + return 1 if (val & (1 << idx) > 0) + return 0 + end + if index.kind_of?(Range) + exclude_end = index.exclude_end? + lm = index.begin + rm = index.end + if Primitive.nil?(lm) + if !Primitive.nil?(rm) && rm >= 0 + rm += 1 if !exclude_end + mask = (1 << rm) - 1 + if (self & mask) == 0 + return 0 + else + raise ArgumentError, + "The beginless range for Integer#[] results in infinity" + end + else + return 0 + end + end + num = self >> lm + cmp = cmp(lm, rm) + if !Primitive.nil?(rm) && cmp < 0 + len = rm - lm + + len += 1 if !exclude_end + + mask = (1 << len) - 1 + num = num & mask + elsif cmp == 0 + return 0 if exclude_end + num = self + arg = lm + return Truffle::Type.fits_into_long?(num) ? fix_aref(num, arg) : self[lm] + + end + num + + else + index = Primitive.rb_to_int(index) + if Primitive.undefined?(len) + + return index < 0 ? 0 : (self >> index) & 1 + else + num = self >> index + mask = (1 << len) - 1 + + return num & mask + end + end end def allbits?(mask) diff --git a/test/mri/excludes/TestInteger.rb b/test/mri/excludes/TestInteger.rb index cb20385870c9..ea5228f9447d 100644 --- a/test/mri/excludes/TestInteger.rb +++ b/test/mri/excludes/TestInteger.rb @@ -12,4 +12,3 @@ exclude :test_pow, "needs investigation" exclude :test_truncate, "needs investigation" exclude :test_Integer_with_invalid_exception, "needs investigation" -exclude :test_aref, "needs investigation" From cda845a2daf6f7f4af59c1afef2af1f6ee516c2c Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Wed, 9 Dec 2020 17:12:19 +0100 Subject: [PATCH 2/7] Ruby 2.7: Better support of Integer#[] with range arguments - Fix linting issues --- src/main/ruby/truffleruby/core/integer.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 7c3827ffb73f..54679e10d1b4 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -61,11 +61,11 @@ def **(o) end def [](index, len = undefined) - def cmp(a, b) + cmp = -> (a, b) { return FIXNUM_MAX if Primitive.nil?(b) return a - b - end - def fix_aref(num, idx) + } + fix_aref = -> (num, idx) { val = Primitive.rb_num2long(num) idx = Primitive.rb_to_int idx if !Truffle::Type.fits_into_long?(idx) @@ -78,7 +78,8 @@ def fix_aref(num, idx) return 0 if idx < 0 return 1 if (val & (1 << idx) > 0) return 0 - end + } + if index.kind_of?(Range) exclude_end = index.exclude_end? lm = index.begin @@ -91,14 +92,15 @@ def fix_aref(num, idx) return 0 else raise ArgumentError, - "The beginless range for Integer#[] results in infinity" + 'The beginless range for Integer#[] results in infinity' end else return 0 end end num = self >> lm - cmp = cmp(lm, rm) + cmp = cmp.call(lm, rm) + if !Primitive.nil?(rm) && cmp < 0 len = rm - lm @@ -110,15 +112,14 @@ def fix_aref(num, idx) return 0 if exclude_end num = self arg = lm - return Truffle::Type.fits_into_long?(num) ? fix_aref(num, arg) : self[lm] + return Truffle::Type.fits_into_long?(num) ? fix_aref.call(num, arg) : self[lm] end num - else + #not range index = Primitive.rb_to_int(index) if Primitive.undefined?(len) - return index < 0 ? 0 : (self >> index) & 1 else num = self >> index From 5a7bbb87fbdd7773ab0e67ed161ae74aef325197 Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Mon, 14 Dec 2020 18:37:33 +0100 Subject: [PATCH 3/7] Ruby 2.7: first approach --- src/main/ruby/truffleruby/core/integer.rb | 102 +++++++++------------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 54679e10d1b4..4de0f4fe7ea3 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -61,75 +61,57 @@ def **(o) end def [](index, len = undefined) - cmp = -> (a, b) { - return FIXNUM_MAX if Primitive.nil?(b) - return a - b - } - fix_aref = -> (num, idx) { - val = Primitive.rb_num2long(num) - idx = Primitive.rb_to_int idx - if !Truffle::Type.fits_into_long?(idx) - if idx < 0 || val >= 0 - return 0 - else - return 1 - end - end - return 0 if idx < 0 - return 1 if (val & (1 << idx) > 0) - return 0 - } - if index.kind_of?(Range) - exclude_end = index.exclude_end? - lm = index.begin - rm = index.end - if Primitive.nil?(lm) - if !Primitive.nil?(rm) && rm >= 0 - rm += 1 if !exclude_end - mask = (1 << rm) - 1 - if (self & mask) == 0 - return 0 - else - raise ArgumentError, - 'The beginless range for Integer#[] results in infinity' - end - else - return 0 - end - end - num = self >> lm - cmp = cmp.call(lm, rm) + handle_range(index) + else + handle_aref(index, len) + end + end - if !Primitive.nil?(rm) && cmp < 0 - len = rm - lm + private def handle_aref(index, len) + index = Primitive.rb_to_int(index) + if Primitive.undefined?(len) + index < 0 ? 0 : (self >> index) & 1 + else + num = self >> index + mask = (1 << len) - 1 - len += 1 if !exclude_end + num & mask + end + end - mask = (1 << len) - 1 - num = num & mask - elsif cmp == 0 - return 0 if exclude_end - num = self - arg = lm - return Truffle::Type.fits_into_long?(num) ? fix_aref.call(num, arg) : self[lm] + private def handle_range(range) + normalized_range = normalize_range(range) + puts range - end - num - else - #not range - index = Primitive.rb_to_int(index) - if Primitive.undefined?(len) - return index < 0 ? 0 : (self >> index) & 1 - else - num = self >> index - mask = (1 << len) - 1 + start = normalized_range.begin + length = normalized_range.end - normalized_range.begin + 1 - return num & mask - end + num = self >> start + mask = (1 << length) - 1 + result = num & mask + if Primitive.nil?(range.begin) + raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result > 0 + 0 + else + result end end + private def normalize_range(index) + raise FloatDomainError , 'Infinity' if index.begin == Float::INFINITY || index.end == Float::INFINITY + raise FloatDomainError , '-Infinity' if index.begin == -Float::INFINITY || index.end == -Float::INFINITY + + start, length = Primitive.range_normalized_start_length(index, size) + puts start, length + + return Range.new(start, index.end, index.exclude_end?) if Primitive.nil?(index.begin) + return Range.new(index.begin, index.begin + length, index.exclude_end?) if Primitive.nil?(index.end) + return normalize_range(Range.new(index.begin, nil, index.exclude_end?)) if index.end < 0 + return normalize_range(Range.new(index.begin, nil, index.exclude_end?)) if index.end < index.begin + index + end + def allbits?(mask) mask = Primitive.rb_to_int(mask) (self & mask) == mask From 61f27031aff2baf77567d40c27c052f1c1473c84 Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Mon, 14 Dec 2020 22:41:37 +0100 Subject: [PATCH 4/7] Ruby 2.7: rewrite some logic --- .../core/integer/element_reference_tags.txt | 1 - src/main/ruby/truffleruby/core/integer.rb | 43 +++++++++++-------- 2 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 spec/tags/core/integer/element_reference_tags.txt diff --git a/spec/tags/core/integer/element_reference_tags.txt b/spec/tags/core/integer/element_reference_tags.txt deleted file mode 100644 index 2047c01342b5..000000000000 --- a/spec/tags/core/integer/element_reference_tags.txt +++ /dev/null @@ -1 +0,0 @@ -fails:Integer#[] fixnum when range passed raises FloatDomainError if any boundary is infinity diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 4de0f4fe7ea3..992876f5995a 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -81,35 +81,40 @@ def [](index, len = undefined) end private def handle_range(range) - normalized_range = normalize_range(range) - puts range + validate_range(range) - start = normalized_range.begin - length = normalized_range.end - normalized_range.begin + 1 + if !Primitive.nil?(range.begin) && !Primitive.nil?(range.end) + len = range.end - range.begin + num = self >> range.begin + mask = mask(range, len) - num = self >> start - mask = (1 << length) - 1 - result = num & mask - if Primitive.nil?(range.begin) - raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result > 0 - 0 + return num if range.end < 0 + return num if range.end < range.begin + num & mask + elsif Primitive.nil? range.end + start = range.begin + + return self >> start else + result = self & mask(range, range.end) + raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result != 0 + result end + end - private def normalize_range(index) + private def validate_range(index) raise FloatDomainError , 'Infinity' if index.begin == Float::INFINITY || index.end == Float::INFINITY raise FloatDomainError , '-Infinity' if index.begin == -Float::INFINITY || index.end == -Float::INFINITY + end - start, length = Primitive.range_normalized_start_length(index, size) - puts start, length - - return Range.new(start, index.end, index.exclude_end?) if Primitive.nil?(index.begin) - return Range.new(index.begin, index.begin + length, index.exclude_end?) if Primitive.nil?(index.end) - return normalize_range(Range.new(index.begin, nil, index.exclude_end?)) if index.end < 0 - return normalize_range(Range.new(index.begin, nil, index.exclude_end?)) if index.end < index.begin - index + private def mask(range, idx) + if range.exclude_end? + (1 << idx) - 1 + else + (1 << (idx +1)) - 1 + end end def allbits?(mask) From 13822600c54f78210e1ce65fd0e325e913c4cef6 Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Mon, 14 Dec 2020 22:43:42 +0100 Subject: [PATCH 5/7] Ruby 2.7: rewrite some logic --- test/mri/excludes/TestInteger.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/mri/excludes/TestInteger.rb b/test/mri/excludes/TestInteger.rb index ea5228f9447d..4c3b198ec0d5 100644 --- a/test/mri/excludes/TestInteger.rb +++ b/test/mri/excludes/TestInteger.rb @@ -12,3 +12,4 @@ exclude :test_pow, "needs investigation" exclude :test_truncate, "needs investigation" exclude :test_Integer_with_invalid_exception, "needs investigation" +exclude :test_aref, "needs investigation" \ No newline at end of file From a260037002dd63e05212e98d36d444c494aa4004 Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Wed, 16 Dec 2020 00:13:54 +0100 Subject: [PATCH 6/7] Ruby 2.7: extend logic --- spec/ruby/core/integer/element_reference_spec.rb | 8 +------- src/main/ruby/truffleruby/core/integer.rb | 12 ++++-------- test/mri/excludes/TestInteger.rb | 3 +-- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/spec/ruby/core/integer/element_reference_spec.rb b/spec/ruby/core/integer/element_reference_spec.rb index 4c236a11e79b..7197ecdc0323 100644 --- a/spec/ruby/core/integer/element_reference_spec.rb +++ b/spec/ruby/core/integer/element_reference_spec.rb @@ -131,16 +131,10 @@ 0b000001[-3, 4].should == 0b1000 end - it "ignores negative upper boundary" do - 0b101001101[1..-1].should == 0b10100110 - 0b101001101[1..-2].should == 0b10100110 - 0b101001101[1..-3].should == 0b10100110 - end - it "ignores upper boundary smaller than lower boundary" do 0b101001101[4..1].should == 0b10100 0b101001101[4..2].should == 0b10100 - 0b101001101[4..3].should == 0b10100 + 0b101001101[-4..-5].should == 0b1010011010000 end it "raises FloatDomainError if any boundary is infinity" do diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 992876f5995a..9f2163adc52d 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -88,20 +88,16 @@ def [](index, len = undefined) num = self >> range.begin mask = mask(range, len) - return num if range.end < 0 - return num if range.end < range.begin - num & mask + range.end < range.begin ? num : num & mask elsif Primitive.nil? range.end - start = range.begin - return self >> start + return self >> range.begin else result = self & mask(range, range.end) - raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result != 0 + raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result != 0 && range.end >= 0 - result + 0 end - end private def validate_range(index) diff --git a/test/mri/excludes/TestInteger.rb b/test/mri/excludes/TestInteger.rb index 4c3b198ec0d5..71d3b1d31df1 100644 --- a/test/mri/excludes/TestInteger.rb +++ b/test/mri/excludes/TestInteger.rb @@ -11,5 +11,4 @@ exclude :test_floor, "needs investigation" exclude :test_pow, "needs investigation" exclude :test_truncate, "needs investigation" -exclude :test_Integer_with_invalid_exception, "needs investigation" -exclude :test_aref, "needs investigation" \ No newline at end of file +exclude :test_Integer_with_invalid_exception, "needs investigation" \ No newline at end of file From f42732618dc801dd11582059f91c7bca9f5b6e4d Mon Sep 17 00:00:00 2001 From: "gogainda@yandex.ru" Date: Wed, 16 Dec 2020 17:57:46 +0100 Subject: [PATCH 7/7] Ruby 2.7: address review comments --- src/main/ruby/truffleruby/core/integer.rb | 27 ++++++++--------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/ruby/truffleruby/core/integer.rb b/src/main/ruby/truffleruby/core/integer.rb index 9f2163adc52d..caa7a40c05e3 100644 --- a/src/main/ruby/truffleruby/core/integer.rb +++ b/src/main/ruby/truffleruby/core/integer.rb @@ -38,7 +38,6 @@ Object.deprecate_constant :Fixnum, :Bignum class Integer < Numeric - FIXNUM_MAX = 0x3fffffff # Have a copy in Integer of the Numeric version, as MRI does alias_method :remainder, :remainder @@ -81,35 +80,27 @@ def [](index, len = undefined) end private def handle_range(range) - validate_range(range) + raise FloatDomainError , 'Infinity' if range.begin == Float::INFINITY || range.end == Float::INFINITY + raise FloatDomainError , '-Infinity' if range.begin == -Float::INFINITY || range.end == -Float::INFINITY if !Primitive.nil?(range.begin) && !Primitive.nil?(range.end) len = range.end - range.begin + len += 1 if !range.exclude_end? num = self >> range.begin - mask = mask(range, len) + mask = (1 << len) - 1 range.end < range.begin ? num : num & mask elsif Primitive.nil? range.end return self >> range.begin else - result = self & mask(range, range.end) - raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if result != 0 && range.end >= 0 - - 0 - end - end + len = range.end + len += 1 if !range.exclude_end? + mask = (1 << len) - 1 - private def validate_range(index) - raise FloatDomainError , 'Infinity' if index.begin == Float::INFINITY || index.end == Float::INFINITY - raise FloatDomainError , '-Infinity' if index.begin == -Float::INFINITY || index.end == -Float::INFINITY - end + raise ArgumentError, 'The beginless range for Integer#[] results in infinity' if self & mask != 0 && range.end >= 0 - private def mask(range, idx) - if range.exclude_end? - (1 << idx) - 1 - else - (1 << (idx +1)) - 1 + 0 end end