diff --git a/src/ChibiRuby/MRubyState.Object.cs b/src/ChibiRuby/MRubyState.Object.cs index bb325add..c366f2d9 100644 --- a/src/ChibiRuby/MRubyState.Object.cs +++ b/src/ChibiRuby/MRubyState.Object.cs @@ -30,6 +30,12 @@ public RString NewString(ref Utf8StringWriter> format) public RArray NewArray(params ReadOnlySpan values) => new(values, ArrayClass); + internal RArray NewArray(RArray source, int start, int length) => + source.CopySubSequence(start, length, ArrayClass); + + internal RArray NewSharedArray(RArray source, int start, int length) => + source.SharedSubSequence(start, length, ArrayClass); + public RHash NewHash(int capacity = 4) => new( capacity, HashKeyEqualityComparer, @@ -473,6 +479,16 @@ internal void SpliceArray(RArray array, int start, int length, MRubyValue args) } var originalLength = array.Length; + if (length == newValues.Length && start >= 0 && end <= originalLength) + { + array.MakeModifiable(originalLength); + if (length > 0) + { + newValues.CopyTo(array.AsSpan(start, length)); + } + return; + } + if (start >= originalLength) { length = start + newValues.Length; diff --git a/src/ChibiRuby/MRubyState.Vm.cs b/src/ChibiRuby/MRubyState.Vm.cs index d836d263..b4e8fb7b 100644 --- a/src/ChibiRuby/MRubyState.Vm.cs +++ b/src/ChibiRuby/MRubyState.Vm.cs @@ -740,8 +740,8 @@ static void GetConstSlowPath(MRubyState state, ref MRubyValue registerA, ref MRu var valueB = Unsafe.Add(ref registerA, 1); switch (registerA.Object) { - case RArray array when valueB.IsInteger && array.Class == ArrayClass: - registerA = array[(int)valueB.IntegerValue]; + case RArray array when valueB.IsFixnum && array.Class == ArrayClass: + registerA = array[(int)valueB.FixnumValue]; goto Next; case RHash hash when hash.Class == HashClass: registerA = hash.GetValueOrDefault(valueB, this); @@ -799,9 +799,9 @@ static void GetConstSlowPath(MRubyState state, ref MRubyValue registerA, ref MRu var setVal = Unsafe.Add(ref registerA, 2); switch (registerA.Object) { - case RArray array when keyVal.IsInteger && array.Class == ArrayClass + case RArray array when keyVal.IsFixnum && array.Class == ArrayClass && !array.HasFlag(MRubyObjectFlags.Frozen): - array[(int)keyVal.IntegerValue] = setVal; + array.Set((int)keyVal.FixnumValue, setVal); registerA = setVal; goto Next; case RHash hash when hash.Class == HashClass @@ -2345,7 +2345,7 @@ static void BlkPush(MRubyState state, ref MRubyCallInfo callInfo, Span(); - array[bbb.C] = Unsafe.Add(ref registers, bbb.A); + array.Set(bbb.C, Unsafe.Add(ref registers, bbb.A)); goto Next; } case OpCode.APost: @@ -2366,8 +2366,7 @@ static void BlkPush(MRubyState state, ref MRubyCallInfo callInfo, Span 0) { @@ -3060,4 +3059,4 @@ void _Raise(ReadOnlySpan args) } } } -} \ No newline at end of file +} diff --git a/src/ChibiRuby/RProc.cs b/src/ChibiRuby/RProc.cs index 3c55dec9..f1fbada6 100644 --- a/src/ChibiRuby/RProc.cs +++ b/src/ChibiRuby/RProc.cs @@ -145,4 +145,3 @@ public override int GetHashCode() return HashCode.Combine(Upper, Scope); } } - diff --git a/tests/ChibiRuby.Tests/ruby/test/array.rb b/tests/ChibiRuby.Tests/ruby/test/array.rb index 3fe91b8b..09e24f97 100644 --- a/tests/ChibiRuby.Tests/ruby/test/array.rb +++ b/tests/ChibiRuby.Tests/ruby/test/array.rb @@ -262,6 +262,10 @@ class SubArray < Array assert_equal([1,2,3,4], a) assert_equal([1,2,3,4], b) + + a.shift + a.push(5) + assert_equal([2,3,4,5], a) end assert('Array#replace', '15.2.12.5.23') do @@ -368,6 +372,29 @@ class SubArray < Array assert_nil(a.slice(10, -3)) assert_equal([], a.slice(10..7)) assert_equal(b, a) + + # Slices are allowed to share storage internally, but writes on either side + # must detach before mutating. + parent = [0, 1, 2, 3, 4] + child = parent.slice(1, 3) + nested = child.slice(1, 2) + assert_equal([2, 3], nested) + parent[2] = 20 + assert_equal([1, 2, 3], child) + nested[0] = 30 + assert_equal([0, 1, 20, 3, 4], parent) + assert_equal([1, 2, 3], child) + assert_equal([30, 3], nested) + + shifted = [0, 1, 2, 3, 4] + shifted.shift + shifted_dup = shifted.dup + shifted_slice = shifted.slice(1, 2) + assert_equal([1, 2, 3, 4], shifted_dup) + assert_equal([2, 3], shifted_slice) + shifted_dup[0] = 10 + shifted_slice[0] = 20 + assert_equal([1, 2, 3, 4], shifted) end assert('Array#unshift', '15.2.12.5.30') do