Skip to content

Commit 633ad63

Browse files
committed
[GR-20446] Fix some MRI Marshal tests
PullRequest: truffleruby/3632
2 parents d2baccc + 001f82d commit 633ad63

File tree

29 files changed

+932
-266
lines changed

29 files changed

+932
-266
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Compatibility:
8282
* Implement `Thread#native_thread_id` method (#2733, @horakivo).
8383
* Modify `Struct#{inspect,to_s}` to match MRI when the struct is nested inside of an anonymous class or module (@st0012, @nirvdrum).
8484
* `Fiber.current` and `Fiber#transfer` are available without `require 'fiber'` like in CRuby 3.1 (#2733, @eregon).
85+
* Add `freeze` keyword argument to `Marshal.load` (#2733, @andrykonchin).
8586

8687
Performance:
8788

spec/ruby/.mspec.constants

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ UserArray
204204
UserCustomConstructorString
205205
UserDefined
206206
UserDefinedImmediate
207+
UserDefinedString
207208
UserDefinedWithIvar
208209
UserHash
209210
UserHashInitParams

spec/ruby/core/marshal/dump_spec.rb

Lines changed: 184 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,42 @@
104104
UserMarshal.should_not_receive(:name)
105105
Marshal.dump(UserMarshal.new)
106106
end
107+
108+
it "raises TypeError if an Object is an instance of an anonymous class" do
109+
-> { Marshal.dump(Class.new(UserMarshal).new) }.should raise_error(TypeError, /can't dump anonymous class/)
110+
end
107111
end
108112

109113
describe "with an object responding to #_dump" do
110-
it "dumps the object returned by #_dump" do
114+
it "dumps the String returned by #_dump" do
111115
Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
112116
end
113117

118+
it "dumps the String in non US-ASCII and non UTF-8 encoding" do
119+
object = UserDefinedString.new("a".encode("windows-1251"))
120+
Marshal.dump(object).should == "\x04\bIu:\x16UserDefinedString\x06a\x06:\rencoding\"\x11Windows-1251"
121+
end
122+
123+
it "dumps the String in multibyte encoding" do
124+
object = UserDefinedString.new("a".encode("utf-32le"))
125+
Marshal.dump(object).should == "\x04\bIu:\x16UserDefinedString\ta\x00\x00\x00\x06:\rencoding\"\rUTF-32LE"
126+
end
127+
128+
it "ignores overridden name method" do
129+
obj = MarshalSpec::UserDefinedWithOverriddenName.new
130+
Marshal.dump(obj).should == "\x04\bu:/MarshalSpec::UserDefinedWithOverriddenName\x12\x04\b[\a:\nstuff;\x00"
131+
end
132+
114133
it "raises a TypeError if _dump returns a non-string" do
115134
m = mock("marshaled")
116135
m.should_receive(:_dump).and_return(0)
117136
-> { Marshal.dump(m) }.should raise_error(TypeError)
118137
end
119138

139+
it "raises TypeError if an Object is an instance of an anonymous class" do
140+
-> { Marshal.dump(Class.new(UserDefined).new) }.should raise_error(TypeError, /can't dump anonymous class/)
141+
end
142+
120143
it "favors marshal_dump over _dump" do
121144
m = mock("marshaled")
122145
m.should_receive(:marshal_dump).and_return(0)
@@ -166,8 +189,17 @@ def _dump(level)
166189
Marshal.dump(UserDefined::Nested).should == "\004\bc\030UserDefined::Nested"
167190
end
168191

192+
it "ignores overridden name method" do
193+
Marshal.dump(MarshalSpec::ClassWithOverriddenName).should == "\x04\bc)MarshalSpec::ClassWithOverriddenName"
194+
end
195+
196+
it "dumps a class with multibyte characters in name" do
197+
source_object = eval("MarshalSpec::MultibyteぁあぃいClass".force_encoding(Encoding::UTF_8))
198+
Marshal.dump(source_object).should == "\x04\bc,MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Class"
199+
end
200+
169201
it "raises TypeError with an anonymous Class" do
170-
-> { Marshal.dump(Class.new) }.should raise_error(TypeError)
202+
-> { Marshal.dump(Class.new) }.should raise_error(TypeError, /can't dump anonymous class/)
171203
end
172204

173205
it "raises TypeError with a singleton Class" do
@@ -180,6 +212,15 @@ def _dump(level)
180212
Marshal.dump(Marshal).should == "\004\bm\fMarshal"
181213
end
182214

215+
it "ignores overridden name method" do
216+
Marshal.dump(MarshalSpec::ModuleWithOverriddenName).should == "\x04\bc*MarshalSpec::ModuleWithOverriddenName"
217+
end
218+
219+
it "dumps a module with multibyte characters in name" do
220+
source_object = eval("MarshalSpec::MultibyteけげこごModule".force_encoding(Encoding::UTF_8))
221+
Marshal.dump(source_object).should == "\x04\bm-MarshalSpec::Multibyte\xE3\x81\x91\xE3\x81\x92\xE3\x81\x93\xE3\x81\x94Module"
222+
end
223+
183224
it "raises TypeError with an anonymous Module" do
184225
-> { Marshal.dump(Module.new) }.should raise_error(TypeError)
185226
end
@@ -255,6 +296,11 @@ def _dump(level)
255296
Marshal.dump(UserString.new.extend(Meths).force_encoding("binary")).should == "\004\be:\nMethsC:\017UserString\"\000"
256297
end
257298

299+
it "ignores overridden name method when dumps a String subclass" do
300+
obj = MarshalSpec::StringWithOverriddenName.new
301+
Marshal.dump(obj).should == "\x04\bC:*MarshalSpec::StringWithOverriddenName\"\x00"
302+
end
303+
258304
it "dumps a String with instance variables" do
259305
str = ""
260306
str.instance_variable_set("@foo", "bar")
@@ -310,14 +356,42 @@ def _dump(level)
310356
Marshal.dump(o).should == "\x04\b/\x00\x10"
311357
end
312358

359+
it "dumps an ascii-compatible Regexp" do
360+
o = Regexp.new("a".encode("us-ascii"), Regexp::FIXEDENCODING)
361+
Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\x06EF"
362+
363+
o = Regexp.new("a".encode("us-ascii"))
364+
Marshal.dump(o).should == "\x04\bI/\x06a\x00\x06:\x06EF"
365+
366+
o = Regexp.new("a".encode("windows-1251"), Regexp::FIXEDENCODING)
367+
Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\rencoding\"\x11Windows-1251"
368+
369+
o = Regexp.new("a".encode("windows-1251"))
370+
Marshal.dump(o).should == "\x04\bI/\x06a\x00\x06:\x06EF"
371+
end
372+
313373
it "dumps a UTF-8 Regexp" do
314374
o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING)
315375
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET"
376+
377+
o = Regexp.new("a".force_encoding("utf-8"), Regexp::FIXEDENCODING)
378+
Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\x06ET"
379+
380+
o = Regexp.new("\u3042".force_encoding("utf-8"), Regexp::FIXEDENCODING)
381+
Marshal.dump(o).should == "\x04\bI/\b\xE3\x81\x82\x10\x06:\x06ET"
316382
end
317383

318384
it "dumps a Regexp in another encoding" do
319385
o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING)
320386
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE"
387+
388+
o = Regexp.new("a".encode("utf-16le"), Regexp::FIXEDENCODING)
389+
Marshal.dump(o).should == "\x04\bI/\aa\x00\x10\x06:\rencoding\"\rUTF-16LE"
390+
end
391+
392+
it "ignores overridden name method when dumps a Regexp subclass" do
393+
obj = MarshalSpec::RegexpWithOverriddenName.new("")
394+
Marshal.dump(obj).should == "\x04\bIC:*MarshalSpec::RegexpWithOverriddenName/\x00\x00\x06:\x06EF"
321395
end
322396
end
323397

@@ -349,13 +423,22 @@ def _dump(level)
349423
it "dumps an extended Array" do
350424
Marshal.dump([].extend(Meths)).should == "\004\be:\nMeths[\000"
351425
end
426+
427+
it "ignores overridden name method when dumps an Array subclass" do
428+
obj = MarshalSpec::ArrayWithOverriddenName.new
429+
Marshal.dump(obj).should == "\x04\bC:)MarshalSpec::ArrayWithOverriddenName[\x00"
430+
end
352431
end
353432

354433
describe "with a Hash" do
355434
it "dumps a Hash" do
356435
Marshal.dump({}).should == "\004\b{\000"
357436
end
358437

438+
it "dumps a non-empty Hash" do
439+
Marshal.dump({a: 1}).should == "\x04\b{\x06:\x06ai\x06"
440+
end
441+
359442
it "dumps a Hash subclass" do
360443
Marshal.dump(UserHash.new).should == "\004\bC:\rUserHash{\000"
361444
end
@@ -364,8 +447,24 @@ def _dump(level)
364447
Marshal.dump(Hash.new(1)).should == "\004\b}\000i\006"
365448
end
366449

450+
ruby_version_is "3.1" do
451+
it "dumps a Hash with compare_by_identity" do
452+
h = {}
453+
h.compare_by_identity
454+
455+
Marshal.dump(h).should == "\004\bC:\tHash{\x00"
456+
end
457+
458+
it "dumps a Hash subclass with compare_by_identity" do
459+
h = UserHash.new
460+
h.compare_by_identity
461+
462+
Marshal.dump(h).should == "\x04\bC:\rUserHashC:\tHash{\x00"
463+
end
464+
end
465+
367466
it "raises a TypeError with hash having default proc" do
368-
-> { Marshal.dump(Hash.new {}) }.should raise_error(TypeError)
467+
-> { Marshal.dump(Hash.new {}) }.should raise_error(TypeError, "can't dump hash with default proc")
369468
end
370469

371470
it "dumps a Hash with instance variables" do
@@ -381,6 +480,11 @@ def _dump(level)
381480
it "dumps an Hash subclass with a parameter to initialize" do
382481
Marshal.dump(UserHashInitParams.new(1)).should == "\004\bIC:\027UserHashInitParams{\000\006:\a@ai\006"
383482
end
483+
484+
it "ignores overridden name method when dumps a Hash subclass" do
485+
obj = MarshalSpec::HashWithOverriddenName.new
486+
Marshal.dump(obj).should == "\x04\bC:(MarshalSpec::HashWithOverriddenName{\x00"
487+
end
384488
end
385489

386490
describe "with a Struct" do
@@ -409,6 +513,15 @@ def _dump(level)
409513
Marshal.dump(obj).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
410514
Struct.send(:remove_const, :Extended)
411515
end
516+
517+
it "ignores overridden name method" do
518+
obj = MarshalSpec::StructWithOverriddenName.new("member")
519+
Marshal.dump(obj).should == "\x04\bS:*MarshalSpec::StructWithOverriddenName\x06:\x06a\"\vmember"
520+
end
521+
522+
it "raises TypeError with an anonymous Struct" do
523+
-> { Marshal.dump(Struct.new(:a).new(1)) }.should raise_error(TypeError, /can't dump anonymous class/)
524+
end
412525
end
413526

414527
describe "with an Object" do
@@ -440,20 +553,51 @@ def _dump(level)
440553
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
441554
end
442555

443-
it "dumps an Object if it has a singleton class but no singleton methods" do
556+
it "dumps an Object if it has a singleton class but no singleton methods and no singleton instance variables" do
444557
obj = Object.new
445558
obj.singleton_class
446559
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
447560
end
448561

449-
it "raises if an Object has a singleton class and singleton methods" do
562+
it "ignores overridden name method" do
563+
obj = MarshalSpec::ClassWithOverriddenName.new
564+
Marshal.dump(obj).should == "\x04\bo:)MarshalSpec::ClassWithOverriddenName\x00"
565+
end
566+
567+
it "raises TypeError if an Object has a singleton class and singleton methods" do
450568
obj = Object.new
451569
def obj.foo; end
452570
-> {
453571
Marshal.dump(obj)
454572
}.should raise_error(TypeError, "singleton can't be dumped")
455573
end
456574

575+
it "raises TypeError if an Object has a singleton class and singleton instance variables" do
576+
obj = Object.new
577+
class << obj
578+
@v = 1
579+
end
580+
581+
-> {
582+
Marshal.dump(obj)
583+
}.should raise_error(TypeError, "singleton can't be dumped")
584+
end
585+
586+
it "raises TypeError if an Object is an instance of an anonymous class" do
587+
anonymous_class = Class.new
588+
obj = anonymous_class.new
589+
590+
-> { Marshal.dump(obj) }.should raise_error(TypeError, /can't dump anonymous class/)
591+
end
592+
593+
it "raises TypeError if an Object extends an anonymous module" do
594+
anonymous_module = Module.new
595+
obj = Object.new
596+
obj.extend(anonymous_module)
597+
598+
-> { Marshal.dump(obj) }.should raise_error(TypeError, /can't dump anonymous class/)
599+
end
600+
457601
it "dumps a BasicObject subclass if it defines respond_to?" do
458602
obj = MarshalSpec::BasicObjectSubWithRespondToFalse.new
459603
Marshal.dump(obj).should == "\x04\bo:2MarshalSpec::BasicObjectSubWithRespondToFalse\x00"
@@ -483,6 +627,10 @@ def obj.foo; end
483627
load.instance_variable_get(:@foo).should == 42
484628
end
485629
end
630+
631+
it "raises TypeError with an anonymous Range subclass" do
632+
-> { Marshal.dump(Class.new(Range).new(1, 2)) }.should raise_error(TypeError, /can't dump anonymous class/)
633+
end
486634
end
487635

488636
describe "with a Time" do
@@ -520,6 +668,20 @@ def obj.foo; end
520668
zone = ":\tzoneI\"\bUTC\x06:\x06EF" # Last is 'F' (US-ASCII)
521669
dump.should == "\x04\bIu:\tTime\r#{@utc_dump}\x06#{zone}"
522670
end
671+
672+
it "ignores overridden name method" do
673+
obj = MarshalSpec::TimeWithOverriddenName.new
674+
Marshal.dump(obj).should include("MarshalSpec::TimeWithOverriddenName")
675+
end
676+
677+
it "dumps a Time subclass with multibyte characters in name" do
678+
source_object = eval("MarshalSpec::MultibyteぁあぃいTime".force_encoding(Encoding::UTF_8))
679+
Marshal.dump(source_object).should == "\x04\bc+MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Time"
680+
end
681+
682+
it "raises TypeError with an anonymous Time subclass" do
683+
-> { Marshal.dump(Class.new(Time).now) }.should raise_error(TypeError)
684+
end
523685
end
524686

525687
describe "with an Exception" do
@@ -560,6 +722,23 @@ def obj.foo; end
560722
reloaded.cause.should be_an_instance_of(StandardError)
561723
reloaded.cause.message.should == "the cause"
562724
end
725+
726+
# NoMethodError uses an exception formatter on TruffleRuby and computes a message lazily
727+
it "dumps the message for the raised NoMethodError exception" do
728+
begin
729+
"".foo
730+
rescue => e
731+
end
732+
733+
Marshal.dump(e).should.include? "undefined method `foo' for \"\":String"
734+
end
735+
736+
it "raises TypeError if an Object is an instance of an anonymous class" do
737+
anonymous_class = Class.new(Exception)
738+
obj = anonymous_class.new
739+
740+
-> { Marshal.dump(obj) }.should raise_error(TypeError, /can't dump anonymous class/)
741+
end
563742
end
564743

565744
it "dumps subsequent appearances of a symbol as a link" do

0 commit comments

Comments
 (0)