Change soft-fail to use the config, rather than env
[rbx.git] / kernel / core / marshal.rb
blob3bab19b71e0f7c1076263287a99821791f96da52
1 # depends on: module.rb class.rb
3 class Object
4   def to_marshal(ms, strip_ivars = false)
5     out = ms.serialize_extended_object self
6     out << Marshal::TYPE_OBJECT
7     out << ms.serialize(self.class.name.to_sym)
8     out << ms.serialize_instance_variables_suffix(self, true, strip_ivars)
9   end
10 end
12 class Range
13   def to_marshal(ms)
14     super(ms, true)
15   end
16 end
18 class NilClass
19   def to_marshal(ms)
20     Marshal::TYPE_NIL
21   end
22 end
24 class TrueClass
25   def to_marshal(ms)
26     Marshal::TYPE_TRUE
27   end
28 end
30 class FalseClass
31   def to_marshal(ms)
32     Marshal::TYPE_FALSE
33   end
34 end
36 class Class
37   def to_marshal(ms)
38     raise TypeError, "can't dump anonymous class #{self}" if self.name == ''
39     Marshal::TYPE_CLASS + ms.serialize_integer(name.length) + name
40   end
41 end
43 class Module
44   def to_marshal(ms)
45     raise TypeError, "can't dump anonymous module #{self}" if self.name == ''
46     Marshal::TYPE_MODULE + ms.serialize_integer(name.length) + name
47   end
48 end
50 class Symbol
51   def to_marshal(ms)
52     if idx = ms.find_symlink(self) then
53       Marshal::TYPE_SYMLINK + ms.serialize_integer(idx)
54     else
55       ms.add_symlink self
57       str = to_s
58       Marshal::TYPE_SYMBOL + ms.serialize_integer(str.length) + str
59     end
60   end
61 end
63 class String
64   def to_marshal(ms)
65     out = ms.serialize_instance_variables_prefix(self)
66     out << ms.serialize_extended_object(self)
67     out << ms.serialize_user_class(self, String)
68     out << Marshal::TYPE_STRING
69     out << ms.serialize_integer(self.length) << self
70     out << ms.serialize_instance_variables_suffix(self)
71   end
72 end
74 class Fixnum
75   def to_marshal(ms)
76     Marshal::TYPE_FIXNUM + ms.serialize_integer(self)
77   end
78 end
80 class Bignum
81   def to_marshal(ms)
82     str = Marshal::TYPE_BIGNUM + (self < 0 ? '-' : '+')
83     cnt = 0
84     num = self.abs
86     while num != 0
87       str << ms.to_byte(num)
88       num >>= 8
89       cnt += 1
90     end
92     if cnt % 2 == 1
93       str << "\0"
94       cnt += 1
95     end
97     str[0..1] + ms.serialize_integer(cnt / 2) + str[2..-1]
98   end
99 end
101 class Regexp
102   def to_marshal(ms)
103     str = self.source
104     out = ms.serialize_instance_variables_prefix(self)
105     out << ms.serialize_extended_object(self)
106     out << ms.serialize_user_class(self, Regexp)
107     out << Marshal::TYPE_REGEXP
108     out << ms.serialize_integer(str.length) + str
109     out << ms.to_byte(options & 0x7)
110     out << ms.serialize_instance_variables_suffix(self)
111   end
114 class Struct
115   def to_marshal(ms)
116     out =  ms.serialize_instance_variables_prefix(self)
117     out << ms.serialize_extended_object(self)
119     out << Marshal::TYPE_STRUCT
121     out << ms.serialize(self.class.name.to_sym)
122     out << ms.serialize_integer(self.length)
124     self.each_pair do |name, value|
125       out << ms.serialize(name)
126       out << ms.serialize(value)
127     end
129     out << ms.serialize_instance_variables_suffix(self)
130     out
131   end
134 class Array
135   def to_marshal(ms)
136     out = ms.serialize_instance_variables_prefix(self)
137     out << ms.serialize_extended_object(self)
138     out << ms.serialize_user_class(self, Array)
139     out << Marshal::TYPE_ARRAY
140     out << ms.serialize_integer(self.length)
141     unless empty? then
142       each do |element|
143         out << ms.serialize(element)
144       end
145     end
146     out << ms.serialize_instance_variables_suffix(self)
147   end
150 class Hash
151   def to_marshal(ms)
152     raise TypeError, "can't dump hash with default proc" if default_proc
153     out = ms.serialize_instance_variables_prefix(self)
154     out << ms.serialize_extended_object(self)
155     out << ms.serialize_user_class(self, Hash)
156     out << (self.default ? Marshal::TYPE_HASH_DEF : Marshal::TYPE_HASH)
157     out << ms.serialize_integer(length)
158     unless empty? then
159       each_pair do |(key, val)|
160         out << ms.serialize(key)
161         out << ms.serialize(val)
162       end
163     end
164     out << (default ? ms.serialize(default) : '')
165     out << ms.serialize_instance_variables_suffix(self)
166   end
169 class Float
170   def to_marshal(ms)
171     str = if nan? then
172             "nan"
173           elsif zero? then
174             (1.0 / self) < 0 ? '-0' : '0'
175           elsif infinite? then
176             self < 0 ? "-inf" : "inf"
177           else
178             "%.*g" % [17, self] + ms.serialize_float_thing(self)
179           end
180     Marshal::TYPE_FLOAT + ms.serialize_integer(str.length) + str
181   end
184 module Marshal
186   MAJOR_VERSION = 4
187   MINOR_VERSION = 8
189   VERSION_STRING = "\x04\x08"
191   TYPE_NIL = '0'
192   TYPE_TRUE = 'T'
193   TYPE_FALSE = 'F'
194   TYPE_FIXNUM = 'i'
196   TYPE_EXTENDED = 'e'
197   TYPE_UCLASS = 'C'
198   TYPE_OBJECT = 'o'
199   TYPE_DATA = 'd'  # no specs
200   TYPE_USERDEF = 'u'
201   TYPE_USRMARSHAL = 'U'
202   TYPE_FLOAT = 'f'
203   TYPE_BIGNUM = 'l'
204   TYPE_STRING = '"'
205   TYPE_REGEXP = '/'
206   TYPE_ARRAY = '['
207   TYPE_HASH = '{'
208   TYPE_HASH_DEF = '}'
209   TYPE_STRUCT = 'S'
210   TYPE_MODULE_OLD = 'M'  # no specs
211   TYPE_CLASS = 'c'
212   TYPE_MODULE = 'm'
214   TYPE_SYMBOL = ':'
215   TYPE_SYMLINK = ';'
217   TYPE_IVAR = 'I'
218   TYPE_LINK = '@'
220   class State
222     def initialize(stream, depth, proc)
223       # shared
224       @links = {}
225       @symlinks = {}
226       @symbols = []
227       @objects = []
229       # dumping
230       @depth = depth
232       # loading
233       @stream = stream
234       @consumed = 0
236       consume 2 if @stream
238       @modules = nil
239       @has_ivar = []
240       @proc = proc
241       @call = true
242     end
244     def add_object(obj)
245       return if obj.kind_of?(ImmediateValue)
246       sz = @links.size
247       @objects[sz] = obj
248       @links[obj.object_id] = sz
249     end
251     def add_symlink(obj)
252       sz = @symlinks.size
253       @symbols[sz] = obj
254       @symlinks[obj.object_id] = sz
255     end
257     def call(obj)
258       @proc.call obj if @proc and @call
259     end
261     def construct(ivar_index = nil, call_proc = true)
262       type = consume
263       obj = case type
264             when TYPE_NIL
265               nil
266             when TYPE_TRUE
267               true
268             when TYPE_FALSE
269               false
270             when TYPE_CLASS, TYPE_MODULE
271               name = construct_symbol
272               obj = Object.const_lookup name
274               store_unique_object obj
276               obj
277             when TYPE_FIXNUM
278               construct_integer
279             when TYPE_BIGNUM
280               construct_bignum
281             when TYPE_FLOAT
282               construct_float
283             when TYPE_SYMBOL
284               construct_symbol
285             when TYPE_STRING
286               construct_string
287             when TYPE_REGEXP
288               construct_regexp
289             when TYPE_ARRAY
290               construct_array
291             when TYPE_HASH, TYPE_HASH_DEF
292               construct_hash type
293             when TYPE_STRUCT
294               construct_struct
295             when TYPE_OBJECT
296               construct_object
297             when TYPE_USERDEF
298               construct_user_defined ivar_index
299             when TYPE_USRMARSHAL
300               construct_user_marshal
301             when TYPE_LINK
302               num = construct_integer
303               obj = @objects[num]
305               raise ArgumentError, "dump format error (unlinked)" if obj.nil?
307               return obj
308             when TYPE_SYMLINK
309               num = construct_integer
310               sym = @symbols[num]
312               raise ArgumentError, "bad symbol" if sym.nil?
314               return sym
315             when TYPE_EXTENDED
316               @modules ||= []
318               name = get_symbol
319               @modules << Object.const_lookup(name)
321               obj = construct nil, false
323               extend_object obj
325               obj
326             when TYPE_UCLASS
327               name = get_symbol
328               @user_class = name
330               construct nil, false
332             when TYPE_IVAR
333               ivar_index = @has_ivar.length
334               @has_ivar.push true
336               obj = construct ivar_index, false
338               set_instance_variables obj if @has_ivar.pop
340               obj
341             else
342               raise ArgumentError, "load error, unknown type #{type}"
343             end
345       call obj if call_proc
347       obj
348     end
350     def construct_array
351       obj = @user_class ? get_user_class.new : []
352       store_unique_object obj
354       construct_integer.times do
355         obj << construct
356       end
358       obj
359     end
361     def construct_bignum
362       sign = consume == '-' ? -1 : 1
363       size = construct_integer * 2
365       result = 0
367       data = consume size
368       (0...size).each do |exp|
369         result += (data[exp] * 2**(exp*8))
370       end
372       obj = result * sign
374       store_unique_object obj
375     end
377     def construct_float
378       s = get_byte_sequence
380       if s == "nan"
381         obj = 0.0 / 0.0
382       elsif s == "inf"
383         obj = 1.0 / 0.0
384       elsif s == "-inf"
385         obj = 1.0 / -0.0
386       else
387         obj = s.to_f
388       end
390       store_unique_object obj
392       obj
393     end
395     def construct_hash(type)
396       obj = @user_class ? get_user_class.allocate : {}
397       store_unique_object obj
399       construct_integer.times do
400         key = construct
401         val = construct
402         obj[key] = val
403       end
405       obj.default = construct if type == TYPE_HASH_DEF
407       obj
408     end
410     def construct_integer
411       n = consume[0]
413       if (n > 0 and n < 5) or n > 251
414         size, signed = n > 251 ? [256 - n, 2**((256 - n)*8)] : [n, 0]
416         result = 0
417         data = consume size
419         (0...size).each do |exp|
420           result += (data[exp] * 2**(exp*8))
421         end
423         result - signed
424       elsif n > 127
425         (n - 256) + 5
426       elsif n > 4
427         n - 5
428       else
429         n
430       end
431     end
433     def construct_object
434       name = get_symbol
435       klass = Object.const_lookup name
436       obj = klass.allocate
438       raise TypeError, 'dump format error' unless Object === obj
440       store_unique_object obj
441       set_instance_variables obj
443       obj
444     end
446     def construct_regexp
447       s = get_byte_sequence
448       if @user_class
449         obj = get_user_class.new s, consume[0]
450       else
451         obj = Regexp.new s, consume[0]
452       end
454       store_unique_object obj
455     end
457     def construct_string
458       obj = get_byte_sequence
459       obj = get_user_class.new obj if @user_class
461       store_unique_object obj
462     end
464     def construct_struct
465       symbols = []
466       values = []
468       name = get_symbol
469       store_unique_object name
471       klass = Object.const_lookup name
472       members = klass.members
474       obj = klass.allocate
475       store_unique_object obj
477       construct_integer.times do |i|
478         slot = get_symbol
479         unless members[i].intern == slot then
480           raise TypeError, "struct %s is not compatible (%p for %p)" %
481             [klass, slot, members[i]]
482         end
484         obj.instance_variable_set "@#{slot}", construct
485       end
487       obj
488     end
490     def construct_symbol
491       obj = get_byte_sequence.to_sym
492       store_unique_object obj
494       obj
495     end
497     def construct_user_defined(ivar_index)
498       name = get_symbol
499       klass = Module.const_lookup name
501       data = get_byte_sequence
503       if ivar_index and @has_ivar[ivar_index] then
504         set_instance_variables data
505         @has_ivar[ivar_index] = false
506       end
508       obj = klass._load data
510       store_unique_object obj
512       obj
513     end
515     def construct_user_marshal
516       name = get_symbol
517       store_unique_object name
519       klass = Module.const_lookup name
520       obj = klass.allocate
522       extend_object obj if @modules
524       unless obj.respond_to? :marshal_load then
525         raise TypeError, "instance of #{klass} needs to have method `marshal_load'"
526       end
528       store_unique_object obj
530       data = construct
531       obj.marshal_load data
533       obj
534     end
536     def consume(bytes = 1)
537       data = @stream[@consumed, bytes]
538       @consumed += bytes
539       data
540     end
542     def extend_object(obj)
543       obj.extend(@modules.pop) until @modules.empty?
544     end
546     def find_link(obj)
547       @links[obj.object_id]
548     end
550     def find_symlink(obj)
551       @symlinks[obj.object_id]
552     end
554     def frexp(flt)
555       ptr = MemoryPointer.new :int
556       return Platform::Float.frexp(flt, ptr)
557     ensure
558       ptr.free if ptr
559     end
561     def get_byte_sequence
562       size = construct_integer
563       consume size
564     end
566     def get_module_names(obj)
567       names = []
568       sup = obj.metaclass.superclass
570       while sup and [Module, IncludedModule].include? sup.class do
571         names << sup.name
572         sup = sup.superclass
573       end
575       names
576     end
578     def get_user_class
579       cls = Module.const_lookup @user_class
580       @user_class = nil
581       cls
582     end
584     def get_symbol
585       type = consume
587       case type
588       when TYPE_SYMBOL then
589         @call = false
590         obj = construct_symbol
591         @call = true
592         obj
593       when TYPE_SYMLINK then
594         num = construct_integer
595         @symbols[num]
596       else
597         raise ArgumentError, "expected TYPE_SYMBOL or TYPE_SYMLINK, got #{type.inspect}"
598       end
599     end
601     def ldexp(flt, exp)
602       Platform::Float.ldexp flt, exp
603     end
605     def modf(flt)
606       ptr = MemoryPointer.new :double
608       flt = Platform::Float.modf flt, ptr
609       num = ptr.read_float
611       return flt, num
612     ensure
613       ptr.free if ptr
614     end
616     def prepare_ivar(ivar)
617       ivar.to_s =~ /\A@/ ? ivar : "@#{ivar}".to_sym
618     end
620     def serialize(obj)
621       raise ArgumentError, "exceed depth limit" if @depth == 0
623       # How much depth we have left.
624       @depth -= 1;
626       if link = find_link(obj)
627         str = TYPE_LINK + serialize_integer(link)
628       else
629         add_object obj
631         if obj.respond_to? :_dump then
632           str = serialize_user_defined obj
633         elsif obj.respond_to? :marshal_dump then
634           str = serialize_user_marshal obj
635         else
636           str = obj.to_marshal self
637         end
638       end
640       @depth += 1
642       return str
643     end
645     def serialize_extended_object(obj)
646       str = ''
647       get_module_names(obj).each do |mod_name|
648         str << TYPE_EXTENDED + serialize(mod_name.to_sym)
649       end
650       str
651     end
653     def serialize_float_thing(flt)
654       str = ''
655       (flt, ) = modf(ldexp(frexp(flt.abs), 37));
656       str << "\0" if flt > 0
657       while flt > 0
658         (flt, n) = modf(ldexp(flt, 32))
659         n = n.to_i
660         str << to_byte(n >> 24)
661         str << to_byte(n >> 16)
662         str << to_byte(n >> 8)
663         str << to_byte(n)
664       end
665       str.chomp!("\0") while str[-1] == 0
666       str
667     end
669     def serialize_instance_variables_prefix(obj)
670       if obj.instance_variables.length > 0
671         TYPE_IVAR + ''
672       else
673       ''
674       end
675     end
677     def serialize_instance_variables_suffix(obj, force = false, strip_ivars = false)
678       if force or obj.instance_variables.length > 0
679         str = serialize_integer(obj.instance_variables.length)
680         obj.instance_variables.each do |ivar|
681           sym = ivar.to_sym
682           val = obj.instance_variable_get(sym)
683           unless strip_ivars then
684             str << serialize(sym)
685           else
686             str << serialize(ivar[1..-1].to_sym)
687           end
688           str << serialize(val)
689         end
690         str
691       else
692       ''
693       end
694     end
696     def serialize_integer(n)
697       if n == 0
698         s = to_byte(n)
699       elsif n > 0 and n < 123
700         s = to_byte(n + 5)
701       elsif n < 0 and n > -124
702         s = to_byte(256 + (n - 5))
703       else
704         s = "\0"
705         cnt = 0
706         4.times do
707           s << to_byte(n)
708           n >>= 8
709           cnt += 1
710           break if n == 0 or n == -1
711         end
712         s[0] = to_byte(n < 0 ? 256 - cnt : cnt)
713       end
714       s
715     end
717     def serialize_user_class(obj, cls)
718       if obj.class != cls
719         TYPE_UCLASS + serialize(obj.class.name.to_sym)
720       else
721       ''
722       end
723     end
725     def serialize_user_defined(obj)
726       str = obj._dump @depth
727       raise TypeError, "_dump() must return string" if str.class != String
728       out = serialize_instance_variables_prefix(str)
729       out << TYPE_USERDEF + serialize(obj.class.name.to_sym)
730       out << serialize_integer(str.length) + str
731       out << serialize_instance_variables_suffix(str)
732     end
734     def serialize_user_marshal(obj)
735       val = obj.marshal_dump
737       add_object val
739       out = TYPE_USRMARSHAL + serialize(obj.class.name.to_sym)
740       out << val.to_marshal(self)
741     end
743     def set_instance_variables(obj)
744       construct_integer.times do
745         ivar = get_symbol
746         value = construct
747         obj.instance_variable_set prepare_ivar(ivar), value
748       end
749     end
751     def store_unique_object(obj)
752       if obj.kind_of? Symbol
753         add_symlink obj
754       else
755         add_object obj
756       end
757       obj
758     end
760     def to_byte(n)
761       [n].pack('C')
762     end
764   end
766   def self.dump(obj, an_io=nil, limit=nil)
767     if limit.nil?
768       if an_io.kind_of? Fixnum
769         limit = an_io
770         an_io = nil
771       else
772         limit = -1
773       end
774     end
776     depth = Type.coerce_to limit, Fixnum, :to_int
777     ms = State.new nil, depth, nil
779     if an_io and !an_io.respond_to? :write
780       raise TypeError, "output must respond to write"
781     end
783     str = VERSION_STRING + ms.serialize(obj)
785     if an_io
786       an_io.write(str)
787       return an_io
788     end
790     return str
791   end
793   def self.load(obj, proc = nil)
794     if obj.respond_to? :to_str
795       data = obj.to_s
796     elsif obj.respond_to? :read
797       data = obj.read
798       if data.empty?
799         raise EOFError, "end of file reached"
800       end
801     elsif obj.respond_to? :getc  # FIXME - don't read all of it upfront
802       data = ''
803       data << c while (c = obj.getc.chr)
804     else
805       raise TypeError, "instance of IO needed"
806     end
808     major = data[0]
809     minor = data[1]
811     if major != MAJOR_VERSION or minor > MINOR_VERSION then
812       raise TypeError, "incompatible marshal file format (can't be read)\n\tformat version #{MAJOR_VERSION}.#{MINOR_VERSION} required; #{major}.#{minor} given"
813     end
815     ms = State.new data, nil, proc
816     ms.construct
817   end