Skip to content

Commit 4d1a00d

Browse files
committed
Fix Marshal.load of multiple Symbols with an explicit encoding
* Fixes #1624 (cherry picked from commit c4635fd)
1 parent f1b015d commit 4d1a00d

File tree

2 files changed

+25
-7
lines changed

2 files changed

+25
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Bug fixes:
1313
* Fix `Array#[a, b] = "frozen string literal".freeze` (#2355).
1414
* `rb_funcall()` now releases the C-extension lock (similar to MRI).
1515
* Fix `rb_str_modify_expand` to preserve existing bytes (#2392).
16+
* Fix `Marshal.load` of multiple `Symbols` with an explicit encoding (#1624).
1617

1718
Compatibility:
1819

src/main/ruby/truffleruby/core/marshal.rb

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,18 @@ def add_object(obj)
553553
end
554554

555555
def add_symlink(obj)
556-
sz = @symlinks.size
556+
sz = @symbols.size
557+
@symbols[sz] = obj
558+
@symlinks[obj.__id__] = sz
559+
end
560+
561+
def reserve_symlink
562+
sz = @symbols.size
563+
@symbols[sz] = nil
564+
sz
565+
end
566+
567+
def assign_reserved_symlink(sz, obj)
557568
@symbols[sz] = obj
558569
@symlinks[obj.__id__] = sz
559570
end
@@ -848,7 +859,6 @@ def construct_regexp
848859

849860
def construct_struct
850861
name = get_symbol
851-
store_unique_object name
852862

853863
klass = const_lookup name, Class
854864
members = klass.members
@@ -859,8 +869,7 @@ def construct_struct
859869
construct_integer.times do |i|
860870
slot = get_symbol
861871
unless members[i].intern == slot
862-
raise TypeError, 'struct %s is not compatible (%p for %p)' %
863-
[klass, slot, members[i]]
872+
raise TypeError, "struct #{klass} is not compatible (#{slot.inspect} for #{members[i].inspect})"
864873
end
865874

866875
Primitive.object_hidden_var_set obj, slot, construct
@@ -872,6 +881,12 @@ def construct_struct
872881
def construct_symbol(ivar_index)
873882
data = get_byte_sequence
874883

884+
# We must use the next @symbols index for the Symbol being constructed.
885+
# However, constructing a Symbol might require to construct the :E or :encoding symbols,
886+
# and those must have a larger index (if they are serialized for the first time),
887+
# to be binary-compatible with CRuby and pass specs. So we reserve the index and assign it later.
888+
idx = reserve_symlink
889+
875890
# A Symbol has no instance variables (it's frozen),
876891
# but we need to know the encoding before building the Symbol
877892
if ivar_index and @has_ivar[ivar_index]
@@ -881,7 +896,7 @@ def construct_symbol(ivar_index)
881896
end
882897

883898
obj = data.to_sym
884-
store_unique_object obj
899+
assign_reserved_symlink idx, obj
885900

886901
obj
887902
end
@@ -910,7 +925,6 @@ def construct_user_defined(ivar_index)
910925

911926
def construct_user_marshal
912927
name = get_symbol
913-
store_unique_object name
914928

915929
klass = const_lookup name, Class
916930
obj = klass.allocate
@@ -976,7 +990,7 @@ def serialize(obj)
976990
raise ArgumentError, 'exceed depth limit' if @depth == 0
977991

978992
# How much depth we have left.
979-
@depth -= 1;
993+
@depth -= 1
980994

981995
if link = find_link(obj)
982996
str = Truffle::Type.binary_string("@#{serialize_integer(link)}")
@@ -1249,7 +1263,10 @@ def initialize(stream, depth, prc)
12491263
if @stream
12501264
@byte_array = stream.bytes
12511265
end
1266+
end
12521267

1268+
def inspect
1269+
"#<Marshal::StringState #{@stream[@consumed..-1].inspect}>"
12531270
end
12541271

12551272
def consume(bytes)

0 commit comments

Comments
 (0)