mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
1227 lines
28 KiB
Ruby
1227 lines
28 KiB
Ruby
class BasicObject
|
|
def __marshal__(ms, strip_ivars = false)
|
|
out = ms.serialize_extended_object self
|
|
out << "o"
|
|
cls = Rubinius::Type.object_class self
|
|
name = Rubinius::Type.module_inspect cls
|
|
out << ms.serialize(name.to_sym)
|
|
out << ms.serialize_instance_variables_suffix(self, true, strip_ivars)
|
|
end
|
|
end
|
|
|
|
class Class
|
|
def __marshal__(ms)
|
|
if Rubinius::Type.singleton_class_object(self)
|
|
raise TypeError, "singleton class can't be dumped"
|
|
elsif name.nil? || name.empty?
|
|
raise TypeError, "can't dump anonymous module #{self}"
|
|
end
|
|
|
|
"c#{ms.serialize_integer(name.length)}#{name}"
|
|
end
|
|
end
|
|
|
|
class Module
|
|
def __marshal__(ms)
|
|
raise TypeError, "can't dump anonymous module #{self}" if name.nil? || name.empty?
|
|
"m#{ms.serialize_integer(name.length)}#{name}"
|
|
end
|
|
end
|
|
|
|
class Float
|
|
def __marshal__(ms)
|
|
if nan?
|
|
str = "nan"
|
|
elsif zero?
|
|
str = (1.0 / self) < 0 ? '-0' : '0'
|
|
elsif infinite?
|
|
str = self < 0 ? "-inf" : "inf"
|
|
else
|
|
s, decimal, sign, digits = dtoa
|
|
|
|
if decimal < -3 or decimal > digits
|
|
str = s.insert(1, ".") << "e#{decimal - 1}"
|
|
elsif decimal > 0
|
|
str = s[0, decimal]
|
|
digits -= decimal
|
|
str << ".#{s[decimal, digits]}" if digits > 0
|
|
else
|
|
str = "0."
|
|
str << "0" * -decimal if decimal != 0
|
|
str << s[0, digits]
|
|
end
|
|
end
|
|
|
|
sl = str.length
|
|
if sign == 1
|
|
ss = "-"
|
|
sl += 1
|
|
end
|
|
|
|
Rubinius::Type.binary_string("f#{ms.serialize_integer(sl)}#{ss}#{str}")
|
|
end
|
|
end
|
|
|
|
class Exception
|
|
def __marshal__(ms)
|
|
out = ms.serialize_extended_object self
|
|
out << "o"
|
|
cls = Rubinius::Type.object_class self
|
|
name = Rubinius::Type.module_inspect cls
|
|
out << ms.serialize(name.to_sym)
|
|
out << ms.serialize_fixnum(2)
|
|
|
|
out << ms.serialize(:mesg)
|
|
out << ms.serialize(@reason_message)
|
|
out << ms.serialize(:bt)
|
|
out << ms.serialize(backtrace)
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
class Time
|
|
def __custom_marshal__(ms)
|
|
out = Rubinius::Type.binary_string("")
|
|
|
|
# Order matters.
|
|
extra_values = {}
|
|
extra_values[:offset] = gmt_offset unless gmt?
|
|
extra_values[:zone] = zone
|
|
|
|
if nsec > 0
|
|
# MRI serializes nanoseconds as a Rational using an
|
|
# obscure and implementation-dependent method.
|
|
# To keep compatibility we can just put nanoseconds
|
|
# in the numerator and set the denominator to 1.
|
|
extra_values[:nano_num] = nsec
|
|
extra_values[:nano_den] = 1
|
|
end
|
|
|
|
ivars = ms.serializable_instance_variables(self, false)
|
|
out << Rubinius::Type.binary_string("I")
|
|
out << Rubinius::Type.binary_string("u#{ms.serialize(self.class.name.to_sym)}")
|
|
|
|
str = _dump
|
|
out << ms.serialize_integer(str.length) + str
|
|
|
|
count = ivars.size + extra_values.size
|
|
out << ms.serialize_integer(count)
|
|
|
|
ivars.each do |ivar|
|
|
sym = ivar.to_sym
|
|
val = __instance_variable_get__(sym)
|
|
out << ms.serialize(sym)
|
|
out << ms.serialize(val)
|
|
end
|
|
|
|
extra_values.each_pair do |key, value|
|
|
out << ms.serialize(key)
|
|
out << ms.serialize(value)
|
|
end
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
module Marshal
|
|
class State
|
|
def serialize_encoding?(obj)
|
|
enc = Rubinius::Type.object_encoding(obj)
|
|
enc && enc != Encoding::BINARY
|
|
end
|
|
|
|
def serialize_encoding(obj)
|
|
case enc = Rubinius::Type.object_encoding(obj)
|
|
when Encoding::US_ASCII
|
|
:E.__marshal__(self) + false.__marshal__(self)
|
|
when Encoding::UTF_8
|
|
:E.__marshal__(self) + true.__marshal__(self)
|
|
else
|
|
:encoding.__marshal__(self) + serialize_string(enc.name)
|
|
end
|
|
end
|
|
|
|
def set_object_encoding(obj, enc)
|
|
case obj
|
|
when String
|
|
obj.force_encoding enc
|
|
when Regexp
|
|
obj.source.force_encoding enc
|
|
when Symbol
|
|
# TODO
|
|
end
|
|
end
|
|
|
|
def set_instance_variables(obj)
|
|
construct_integer.times do
|
|
ivar = get_symbol
|
|
value = construct
|
|
|
|
case ivar
|
|
when :E
|
|
if value
|
|
set_object_encoding obj, Encoding::UTF_8
|
|
else
|
|
set_object_encoding obj, Encoding::US_ASCII
|
|
end
|
|
next
|
|
when :encoding
|
|
if enc = Encoding.find(value)
|
|
set_object_encoding obj, enc
|
|
next
|
|
end
|
|
end
|
|
|
|
obj.__instance_variable_set__ prepare_ivar(ivar), value
|
|
end
|
|
end
|
|
|
|
def construct_string
|
|
obj = get_byte_sequence
|
|
Rubinius::Unsafe.set_class(obj, get_user_class) if @user_class
|
|
|
|
set_object_encoding(obj, Encoding::ASCII_8BIT)
|
|
|
|
store_unique_object obj
|
|
end
|
|
end
|
|
end
|
|
|
|
class Range
|
|
def __marshal__(ms)
|
|
super(ms, true)
|
|
end
|
|
end
|
|
|
|
class NilClass
|
|
def __marshal__(ms)
|
|
Rubinius::Type.binary_string("0")
|
|
end
|
|
end
|
|
|
|
class TrueClass
|
|
def __marshal__(ms)
|
|
Rubinius::Type.binary_string("T")
|
|
end
|
|
end
|
|
|
|
class FalseClass
|
|
def __marshal__(ms)
|
|
Rubinius::Type.binary_string("F")
|
|
end
|
|
end
|
|
|
|
class Symbol
|
|
def __marshal__(ms)
|
|
if idx = ms.find_symlink(self)
|
|
Rubinius::Type.binary_string(";#{ms.serialize_integer(idx)}")
|
|
else
|
|
ms.add_symlink self
|
|
ms.serialize_symbol(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
class String
|
|
def __marshal__(ms)
|
|
out = ms.serialize_instance_variables_prefix(self)
|
|
out << ms.serialize_extended_object(self)
|
|
out << ms.serialize_user_class(self, String)
|
|
out << ms.serialize_string(self)
|
|
out << ms.serialize_instance_variables_suffix(self)
|
|
out
|
|
end
|
|
end
|
|
|
|
class Fixnum
|
|
def __marshal__(ms)
|
|
ms.serialize_integer(self, "i")
|
|
end
|
|
end
|
|
|
|
class Bignum
|
|
def __marshal__(ms)
|
|
ms.serialize_bignum(self)
|
|
end
|
|
end
|
|
|
|
class Regexp
|
|
def __marshal__(ms)
|
|
str = self.source
|
|
out = ms.serialize_instance_variables_prefix(self)
|
|
out << ms.serialize_extended_object(self)
|
|
out << ms.serialize_user_class(self, Regexp)
|
|
out << "/"
|
|
out << ms.serialize_integer(str.length) + str
|
|
out << (options & Regexp::OPTION_MASK).chr
|
|
out << ms.serialize_instance_variables_suffix(self)
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
class Struct
|
|
def __marshal__(ms)
|
|
exclude = _attrs.map { |a| "@#{a}".to_sym }
|
|
|
|
out = ms.serialize_instance_variables_prefix(self, exclude)
|
|
out << ms.serialize_extended_object(self)
|
|
|
|
out << "S"
|
|
|
|
out << ms.serialize(self.class.name.to_sym)
|
|
out << ms.serialize_integer(self.length)
|
|
|
|
self.each_pair do |name, value|
|
|
out << ms.serialize(name)
|
|
out << ms.serialize(value)
|
|
end
|
|
|
|
out << ms.serialize_instance_variables_suffix(self, false, false, exclude)
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
class Array
|
|
def __marshal__(ms)
|
|
out = ms.serialize_instance_variables_prefix(self)
|
|
out << ms.serialize_extended_object(self)
|
|
out << ms.serialize_user_class(self, Array)
|
|
out << "["
|
|
out << ms.serialize_integer(self.length)
|
|
unless empty?
|
|
each do |element|
|
|
out << ms.serialize(element)
|
|
end
|
|
end
|
|
out << ms.serialize_instance_variables_suffix(self)
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
class Hash
|
|
def __marshal__(ms)
|
|
raise TypeError, "can't dump hash with default proc" if default_proc
|
|
|
|
excluded_ivars = %w[
|
|
@head @tail @size @previous @next @key @key_hash @value
|
|
@key_hash @entries @bmp @entries @level @state @trie
|
|
@compare_by_identity @default @default_proc
|
|
].map { |a| a.to_sym }
|
|
|
|
out = ms.serialize_instance_variables_prefix(self, excluded_ivars)
|
|
out << ms.serialize_extended_object(self)
|
|
out << ms.serialize_user_class(self, Hash)
|
|
out << (self.default ? "}" : "{")
|
|
out << ms.serialize_integer(length)
|
|
unless empty?
|
|
each_pair do |key, val|
|
|
out << ms.serialize(key)
|
|
out << ms.serialize(val)
|
|
end
|
|
end
|
|
out << (self.default ? ms.serialize(self.default) : '')
|
|
out << ms.serialize_instance_variables_suffix(self, false, false,
|
|
excluded_ivars)
|
|
|
|
out
|
|
end
|
|
end
|
|
|
|
class Time
|
|
def self.__construct__(ms, data, ivar_index, has_ivar)
|
|
obj = _load(data)
|
|
ms.store_unique_object obj
|
|
|
|
if ivar_index and has_ivar[ivar_index]
|
|
ms.set_instance_variables obj
|
|
has_ivar[ivar_index] = false
|
|
end
|
|
|
|
nano_num = obj.instance_variable_get(:@nano_num)
|
|
nano_den = obj.instance_variable_get(:@nano_den)
|
|
if nano_num && nano_den
|
|
obj.send(:nsec=, Rational(nano_num, nano_den).to_i)
|
|
end
|
|
|
|
obj
|
|
end
|
|
end
|
|
|
|
module Unmarshalable
|
|
def __marshal__(ms)
|
|
raise TypeError, "marshaling is undefined for class #{self.class}"
|
|
end
|
|
end
|
|
|
|
class Method
|
|
include Unmarshalable
|
|
end
|
|
|
|
class Proc
|
|
include Unmarshalable
|
|
end
|
|
|
|
class IO
|
|
include Unmarshalable
|
|
end
|
|
|
|
class MatchData
|
|
include Unmarshalable
|
|
end
|
|
|
|
module Marshal
|
|
|
|
MAJOR_VERSION = 4
|
|
MINOR_VERSION = 8
|
|
|
|
VERSION_STRING = "\x04\x08"
|
|
|
|
# Here only for reference
|
|
TYPE_NIL = ?0
|
|
TYPE_TRUE = ?T
|
|
TYPE_FALSE = ?F
|
|
TYPE_FIXNUM = ?i
|
|
|
|
TYPE_EXTENDED = ?e
|
|
TYPE_UCLASS = ?C
|
|
TYPE_OBJECT = ?o
|
|
TYPE_DATA = ?d # no specs
|
|
TYPE_USERDEF = ?u
|
|
TYPE_USRMARSHAL = ?U
|
|
TYPE_FLOAT = ?f
|
|
TYPE_BIGNUM = ?l
|
|
TYPE_STRING = ?"
|
|
TYPE_REGEXP = ?/
|
|
TYPE_ARRAY = ?[
|
|
TYPE_HASH = ?{
|
|
TYPE_HASH_DEF = ?}
|
|
TYPE_STRUCT = ?S
|
|
TYPE_MODULE_OLD = ?M # no specs
|
|
TYPE_CLASS = ?c
|
|
TYPE_MODULE = ?m
|
|
|
|
TYPE_SYMBOL = ?:
|
|
TYPE_SYMLINK = ?;
|
|
|
|
TYPE_IVAR = ?I
|
|
TYPE_LINK = ?@
|
|
|
|
class State
|
|
|
|
def initialize(stream, depth, proc)
|
|
# shared
|
|
@links = Rubinius::LookupTable.new
|
|
@symlinks = Rubinius::LookupTable.new
|
|
@symbols = []
|
|
@objects = []
|
|
|
|
# dumping
|
|
@depth = depth
|
|
|
|
# loading
|
|
if stream
|
|
@stream = stream
|
|
else
|
|
@stream = nil
|
|
end
|
|
|
|
if stream
|
|
@consumed = 2
|
|
else
|
|
@consumed = 0
|
|
end
|
|
|
|
@modules = nil
|
|
@has_ivar = []
|
|
@proc = proc
|
|
@call = true
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def const_lookup(name, type = nil)
|
|
mod = Object
|
|
|
|
parts = String(name).split '::'
|
|
parts.each do |part|
|
|
unless Rubinius::Type.const_exists?(mod, part)
|
|
raise ArgumentError, "undefined class/module #{name}"
|
|
end
|
|
|
|
mod = Rubinius::Type.const_get(mod, part, false)
|
|
end
|
|
|
|
if type and not mod.instance_of? type
|
|
raise ArgumentError, "#{name} does not refer to a #{type}"
|
|
end
|
|
|
|
mod
|
|
end
|
|
|
|
def add_non_immediate_object(obj)
|
|
return if Rubinius::Type.object_kind_of? obj, ImmediateValue
|
|
add_object(obj)
|
|
end
|
|
|
|
def add_object(obj)
|
|
sz = @objects.size
|
|
@objects[sz] = obj
|
|
@links[obj.__id__] = sz
|
|
end
|
|
|
|
def add_symlink(obj)
|
|
sz = @symlinks.size
|
|
@symbols[sz] = obj
|
|
@symlinks[obj.__id__] = sz
|
|
end
|
|
|
|
def call(obj)
|
|
@proc.call obj if @proc and @call
|
|
end
|
|
|
|
def construct(ivar_index = nil, call_proc = true)
|
|
type = consume_byte()
|
|
obj = case type
|
|
when 48 # ?0
|
|
nil
|
|
when 84 # ?T
|
|
true
|
|
when 70 # ?F
|
|
false
|
|
when 99 # ?c
|
|
construct_class
|
|
when 109 # ?m
|
|
construct_module
|
|
when 77 # ?M
|
|
construct_old_module
|
|
when 105 # ?i
|
|
construct_integer
|
|
when 108 # ?l
|
|
construct_bignum
|
|
when 102 # ?f
|
|
construct_float
|
|
when 58 # ?:
|
|
construct_symbol
|
|
when 34 # ?"
|
|
construct_string
|
|
when 47 # ?/
|
|
construct_regexp
|
|
when 91 # ?[
|
|
construct_array
|
|
when 123 # ?{
|
|
construct_hash
|
|
when 125 # ?}
|
|
construct_hash_def
|
|
when 83 # ?S
|
|
construct_struct
|
|
when 111 # ?o
|
|
construct_object
|
|
when 117 # ?u
|
|
construct_user_defined ivar_index
|
|
when 85 # ?U
|
|
construct_user_marshal
|
|
when 100 # ?d
|
|
construct_data
|
|
when 64 # ?@
|
|
num = construct_integer
|
|
|
|
begin
|
|
obj = @objects.fetch(num)
|
|
return obj
|
|
rescue IndexError
|
|
raise ArgumentError, "dump format error (unlinked)"
|
|
end
|
|
|
|
when 59 # ?;
|
|
num = construct_integer
|
|
sym = @symbols[num]
|
|
|
|
raise ArgumentError, "bad symbol" unless sym
|
|
|
|
return sym
|
|
when 101 # ?e
|
|
@modules ||= []
|
|
|
|
name = get_symbol
|
|
@modules << const_lookup(name, Module)
|
|
|
|
obj = construct nil, false
|
|
|
|
extend_object obj
|
|
|
|
obj
|
|
when 67 # ?C
|
|
name = get_symbol
|
|
@user_class = name
|
|
|
|
construct nil, false
|
|
|
|
when 73 # ?I
|
|
ivar_index = @has_ivar.length
|
|
@has_ivar.push true
|
|
|
|
obj = construct ivar_index, false
|
|
|
|
set_instance_variables obj if @has_ivar.pop
|
|
|
|
obj
|
|
else
|
|
raise ArgumentError, "load error, unknown type #{type}"
|
|
end
|
|
|
|
call obj if @proc and call_proc
|
|
|
|
Rubinius::Type.infect obj, @stream unless obj.frozen?
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_class
|
|
obj = const_lookup(get_byte_sequence.to_sym, Class)
|
|
store_unique_object obj
|
|
obj
|
|
end
|
|
|
|
def construct_module
|
|
obj = const_lookup(get_byte_sequence.to_sym, Module)
|
|
store_unique_object obj
|
|
obj
|
|
end
|
|
|
|
def construct_old_module
|
|
obj = const_lookup(get_byte_sequence.to_sym)
|
|
store_unique_object obj
|
|
obj
|
|
end
|
|
|
|
def construct_array
|
|
obj = []
|
|
store_unique_object obj
|
|
|
|
if @user_class
|
|
cls = get_user_class()
|
|
if cls < Array
|
|
Rubinius::Unsafe.set_class obj, cls
|
|
else
|
|
# This is what MRI does, it's weird.
|
|
return cls.allocate
|
|
end
|
|
end
|
|
|
|
construct_integer.times do |i|
|
|
obj.__append__ construct
|
|
end
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_bignum
|
|
sign = consume_byte() == 45 ? -1 : 1 # ?-
|
|
size = construct_integer * 2
|
|
|
|
result = 0
|
|
|
|
data = consume size
|
|
(0...size).each do |exp|
|
|
result += (data.getbyte(exp) * 2**(exp*8))
|
|
end
|
|
|
|
obj = result * sign
|
|
|
|
add_object obj
|
|
obj
|
|
end
|
|
|
|
def construct_data
|
|
name = get_symbol
|
|
klass = const_lookup name, Class
|
|
store_unique_object klass
|
|
|
|
obj = klass.allocate
|
|
|
|
# TODO ensure obj is a wrapped C pointer (T_DATA in MRI-land)
|
|
|
|
store_unique_object obj
|
|
|
|
unless Rubinius::Type.object_respond_to? obj, :_load_data
|
|
raise TypeError,
|
|
"class #{name} needs to have instance method `_load_data'"
|
|
end
|
|
|
|
obj._load_data construct
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_float
|
|
s = get_byte_sequence
|
|
|
|
if s == "nan"
|
|
obj = 0.0 / 0.0
|
|
elsif s == "inf"
|
|
obj = 1.0 / 0.0
|
|
elsif s == "-inf"
|
|
obj = 1.0 / -0.0
|
|
else
|
|
obj = s.to_f
|
|
end
|
|
|
|
store_unique_object obj
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_hash
|
|
obj = @user_class ? get_user_class.allocate : {}
|
|
store_unique_object obj
|
|
|
|
construct_integer.times do
|
|
original_modules = @modules
|
|
@modules = nil
|
|
key = construct
|
|
val = construct
|
|
@modules = original_modules
|
|
|
|
# Use __store__ (an alias for []=) to get around subclass overrides
|
|
obj.__store__ key, val
|
|
end
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_hash_def
|
|
obj = @user_class ? get_user_class.allocate : {}
|
|
store_unique_object obj
|
|
|
|
construct_integer.times do
|
|
key = construct
|
|
val = construct
|
|
obj[key] = val
|
|
end
|
|
|
|
obj.default = construct
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_integer
|
|
c = consume_byte()
|
|
|
|
# The format appears to be a simple integer compression format
|
|
#
|
|
# The 0-123 cases are easy, and use one byte
|
|
# We've read c as unsigned char in a way, but we need to honor
|
|
# the sign bit. We do that by simply comparing with the +128 values
|
|
return 0 if c == 0
|
|
return c - 5 if 4 < c and c < 128
|
|
|
|
# negative, but checked known it's instead in 2's complement
|
|
return c - 251 if 252 > c and c > 127
|
|
|
|
# otherwise c (now in the 1 to 4 range) indicates how many
|
|
# bytes to read to construct the value.
|
|
#
|
|
# Because we're operating on a small number of possible values,
|
|
# it's cleaner to just unroll the calculate of each
|
|
|
|
case c
|
|
when 1
|
|
consume_byte
|
|
when 2
|
|
consume_byte | (consume_byte << 8)
|
|
when 3
|
|
consume_byte | (consume_byte << 8) | (consume_byte << 16)
|
|
when 4
|
|
consume_byte | (consume_byte << 8) | (consume_byte << 16) |
|
|
(consume_byte << 24)
|
|
|
|
when 255 # -1
|
|
consume_byte - 256
|
|
when 254 # -2
|
|
(consume_byte | (consume_byte << 8)) - 65536
|
|
when 253 # -3
|
|
(consume_byte |
|
|
(consume_byte << 8) |
|
|
(consume_byte << 16)) - 16777216 # 2 ** 24
|
|
when 252 # -4
|
|
(consume_byte |
|
|
(consume_byte << 8) |
|
|
(consume_byte << 16) |
|
|
(consume_byte << 24)) - 4294967296
|
|
else
|
|
raise "Invalid integer size: #{c}"
|
|
end
|
|
end
|
|
|
|
def construct_object
|
|
name = get_symbol
|
|
klass = const_lookup name, Class
|
|
obj = klass.allocate
|
|
|
|
raise TypeError, 'dump format error' unless Object === obj
|
|
|
|
store_unique_object obj
|
|
if Rubinius::Type.object_kind_of? obj, Exception
|
|
set_exception_variables obj
|
|
else
|
|
set_instance_variables obj
|
|
end
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_regexp
|
|
s = get_byte_sequence
|
|
if @user_class
|
|
obj = get_user_class.new s, consume_byte
|
|
else
|
|
obj = Regexp.new s, consume_byte
|
|
end
|
|
|
|
store_unique_object obj
|
|
end
|
|
|
|
def construct_struct
|
|
symbols = []
|
|
values = []
|
|
|
|
name = get_symbol
|
|
store_unique_object name
|
|
|
|
klass = const_lookup name, Class
|
|
members = klass.members
|
|
|
|
obj = klass.allocate
|
|
store_unique_object obj
|
|
|
|
construct_integer.times do |i|
|
|
slot = get_symbol
|
|
unless members[i].intern == slot
|
|
raise TypeError, "struct %s is not compatible (%p for %p)" %
|
|
[klass, slot, members[i]]
|
|
end
|
|
|
|
obj.instance_variable_set "@#{slot}", construct
|
|
end
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_symbol
|
|
obj = get_byte_sequence.to_sym
|
|
store_unique_object obj
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_user_defined(ivar_index)
|
|
name = get_symbol
|
|
klass = const_lookup name, Class
|
|
|
|
data = get_byte_sequence
|
|
|
|
if Rubinius::Type.object_respond_to? klass, :__construct__
|
|
return klass.__construct__(self, data, ivar_index, @has_ivar)
|
|
end
|
|
|
|
if ivar_index and @has_ivar[ivar_index]
|
|
set_instance_variables data
|
|
@has_ivar[ivar_index] = false
|
|
end
|
|
|
|
obj = nil
|
|
Rubinius.privately do
|
|
obj = klass._load data
|
|
end
|
|
|
|
add_object obj
|
|
|
|
obj
|
|
end
|
|
|
|
def construct_user_marshal
|
|
name = get_symbol
|
|
store_unique_object name
|
|
|
|
klass = const_lookup name, Class
|
|
obj = klass.allocate
|
|
|
|
extend_object obj if @modules
|
|
|
|
unless Rubinius::Type.object_respond_to_marshal_load? obj
|
|
raise TypeError, "instance of #{klass} needs to have method `marshal_load'"
|
|
end
|
|
|
|
store_unique_object obj
|
|
|
|
data = construct
|
|
Rubinius.privately do
|
|
obj.marshal_load data
|
|
end
|
|
|
|
obj
|
|
end
|
|
|
|
def extend_object(obj)
|
|
obj.__extend__(@modules.pop) until @modules.empty?
|
|
end
|
|
|
|
def find_link(obj)
|
|
@links[obj.__id__]
|
|
end
|
|
|
|
def find_symlink(obj)
|
|
@symlinks[obj.__id__]
|
|
end
|
|
|
|
def get_byte_sequence
|
|
size = construct_integer
|
|
consume size
|
|
end
|
|
|
|
def get_user_class
|
|
cls = const_lookup @user_class, Class
|
|
@user_class = nil
|
|
cls
|
|
end
|
|
|
|
def get_symbol
|
|
type = consume_byte()
|
|
|
|
case type
|
|
when 58 # TYPE_SYMBOL
|
|
@call = false
|
|
obj = construct_symbol
|
|
@call = true
|
|
obj
|
|
when 59 # TYPE_SYMLINK
|
|
num = construct_integer
|
|
@symbols[num]
|
|
else
|
|
raise ArgumentError, "expected TYPE_SYMBOL or TYPE_SYMLINK, got #{type.inspect}"
|
|
end
|
|
end
|
|
|
|
def prepare_ivar(ivar)
|
|
ivar.to_s =~ /\A@/ ? ivar : "@#{ivar}".to_sym
|
|
end
|
|
|
|
def serialize(obj)
|
|
raise ArgumentError, "exceed depth limit" if @depth == 0
|
|
|
|
# How much depth we have left.
|
|
@depth -= 1;
|
|
|
|
if link = find_link(obj)
|
|
str = Rubinius::Type.binary_string("@#{serialize_integer(link)}")
|
|
else
|
|
add_non_immediate_object obj
|
|
|
|
# ORDER MATTERS.
|
|
if Rubinius::Type.object_respond_to_marshal_dump? obj
|
|
str = serialize_user_marshal obj
|
|
elsif Rubinius::Type.object_respond_to__dump? obj
|
|
str = serialize_user_defined obj
|
|
else
|
|
str = obj.__marshal__ self
|
|
end
|
|
end
|
|
|
|
@depth += 1
|
|
|
|
Rubinius::Type.infect(str, obj)
|
|
end
|
|
|
|
def serialize_extended_object(obj)
|
|
str = ''
|
|
if mods = Rubinius.extended_modules(obj)
|
|
mods.each do |mod|
|
|
str << "e#{serialize(mod.name.to_sym)}"
|
|
end
|
|
end
|
|
Rubinius::Type.binary_string(str)
|
|
end
|
|
|
|
def serializable_instance_variables(obj, exclude_ivars)
|
|
ivars = Rubinius.invoke_primitive :object_instance_variables, obj
|
|
ivars -= exclude_ivars if exclude_ivars
|
|
ivars
|
|
end
|
|
|
|
def serialize_instance_variables_prefix(obj, exclude_ivars = false)
|
|
ivars = serializable_instance_variables(obj, exclude_ivars)
|
|
Rubinius::Type.binary_string(!ivars.empty? || serialize_encoding?(obj) ? "I" : "")
|
|
end
|
|
|
|
def serialize_instance_variables_suffix(obj, force=false,
|
|
strip_ivars=false,
|
|
exclude_ivars=false)
|
|
ivars = serializable_instance_variables(obj, exclude_ivars)
|
|
|
|
unless force or !ivars.empty? or serialize_encoding?(obj)
|
|
return Rubinius::Type.binary_string("")
|
|
end
|
|
|
|
count = ivars.size
|
|
|
|
if serialize_encoding?(obj)
|
|
str = serialize_integer(count + 1)
|
|
str << serialize_encoding(obj)
|
|
else
|
|
str = serialize_integer(count)
|
|
end
|
|
|
|
ivars.each do |ivar|
|
|
sym = ivar.to_sym
|
|
val = obj.__instance_variable_get__(sym)
|
|
if strip_ivars
|
|
str << serialize(ivar.to_s[1..-1].to_sym)
|
|
else
|
|
str << serialize(sym)
|
|
end
|
|
str << serialize(val)
|
|
end
|
|
|
|
Rubinius::Type.binary_string(str)
|
|
end
|
|
|
|
def serialize_integer(n, prefix = nil)
|
|
if (!Rubinius::L64 && n.is_a?(Fixnum)) || ((n >> 31) == 0 or (n >> 31) == -1)
|
|
Rubinius::Type.binary_string(prefix.to_s + serialize_fixnum(n))
|
|
else
|
|
serialize_bignum(n)
|
|
end
|
|
end
|
|
|
|
def serialize_fixnum(n)
|
|
if n == 0
|
|
s = n.chr
|
|
elsif n > 0 and n < 123
|
|
s = (n + 5).chr
|
|
elsif n < 0 and n > -124
|
|
s = (256 + (n - 5)).chr
|
|
else
|
|
s = "\0"
|
|
cnt = 0
|
|
4.times do
|
|
s << (n & 0xff).chr
|
|
n >>= 8
|
|
cnt += 1
|
|
break if n == 0 or n == -1
|
|
end
|
|
s[0] = (n < 0 ? 256 - cnt : cnt).chr
|
|
end
|
|
Rubinius::Type.binary_string(s)
|
|
end
|
|
|
|
def serialize_bignum(n)
|
|
str = (n < 0 ? 'l-' : 'l+')
|
|
cnt = 0
|
|
num = n.abs
|
|
|
|
while num != 0
|
|
str << (num & 0xff).chr
|
|
num >>= 8
|
|
cnt += 1
|
|
end
|
|
|
|
if cnt % 2 == 1
|
|
str << "\0"
|
|
cnt += 1
|
|
end
|
|
|
|
Rubinius::Type.binary_string(str[0..1] + serialize_fixnum(cnt / 2) + str[2..-1])
|
|
end
|
|
|
|
def serialize_symbol(obj)
|
|
str = obj.to_s
|
|
mf = "I" unless str.ascii_only?
|
|
if mf
|
|
if Rubinius::Type.object_encoding(obj).equal? Encoding::BINARY
|
|
me = serialize_integer(0)
|
|
elsif serialize_encoding?(obj)
|
|
me = serialize_integer(1) + serialize_encoding(obj.encoding)
|
|
end
|
|
end
|
|
mi = serialize_integer(str.bytesize)
|
|
s = Rubinius::Type.binary_string str
|
|
Rubinius::Type.binary_string("#{mf}:#{mi}#{s}#{me}")
|
|
end
|
|
|
|
def serialize_string(str)
|
|
output = Rubinius::Type.binary_string("\"#{serialize_integer(str.bytesize)}")
|
|
output + Rubinius::Type.binary_string(str.dup)
|
|
end
|
|
|
|
def serialize_user_class(obj, cls)
|
|
if obj.class != cls
|
|
Rubinius::Type.binary_string("C#{serialize(obj.class.name.to_sym)}")
|
|
else
|
|
Rubinius::Type.binary_string('')
|
|
end
|
|
end
|
|
|
|
def serialize_user_defined(obj)
|
|
if Rubinius::Type.object_respond_to? obj, :__custom_marshal__
|
|
return obj.__custom_marshal__(self)
|
|
end
|
|
|
|
str = nil
|
|
Rubinius.privately do
|
|
str = obj._dump @depth
|
|
end
|
|
|
|
unless Rubinius::Type.object_kind_of? str, String
|
|
raise TypeError, "_dump() must return string"
|
|
end
|
|
|
|
out = serialize_instance_variables_prefix(str)
|
|
out << Rubinius::Type.binary_string("u#{serialize(obj.class.name.to_sym)}")
|
|
out << serialize_integer(str.length) + str
|
|
out << serialize_instance_variables_suffix(str)
|
|
|
|
out
|
|
end
|
|
|
|
def serialize_user_marshal(obj)
|
|
val = nil
|
|
Rubinius.privately do
|
|
val = obj.marshal_dump
|
|
end
|
|
|
|
add_non_immediate_object val
|
|
|
|
cls = Rubinius::Type.object_class obj
|
|
name = Rubinius::Type.module_inspect cls
|
|
Rubinius::Type.binary_string("U#{serialize(name.to_sym)}#{val.__marshal__(self)}")
|
|
end
|
|
|
|
def store_unique_object(obj)
|
|
if Symbol === obj
|
|
add_symlink obj
|
|
else
|
|
add_non_immediate_object obj
|
|
end
|
|
obj
|
|
end
|
|
|
|
def set_exception_variables(obj)
|
|
construct_integer.times do
|
|
ivar = get_symbol
|
|
value = construct
|
|
case ivar
|
|
when :bt
|
|
obj.__instance_variable_set__ :@custom_backtrace, value
|
|
when :mesg
|
|
obj.__instance_variable_set__ :@reason_message, value
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
class IOState < State
|
|
def consume(bytes)
|
|
@stream.read(bytes)
|
|
end
|
|
|
|
def consume_byte
|
|
b = @stream.getbyte
|
|
raise EOFError unless b
|
|
b
|
|
end
|
|
end
|
|
|
|
class StringState < State
|
|
def initialize(stream, depth, prc)
|
|
super stream, depth, prc
|
|
|
|
if @stream
|
|
@byte_array = stream.data
|
|
end
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def consume(bytes)
|
|
raise ArgumentError, "marshal data too short" if @consumed > @stream.bytesize
|
|
data = @stream.byteslice @consumed, bytes
|
|
@consumed += bytes
|
|
data
|
|
end
|
|
|
|
def consume_byte
|
|
raise ArgumentError, "marshal data too short" if @consumed >= @stream.bytesize
|
|
data = @byte_array.get_byte @consumed
|
|
@consumed += 1
|
|
return data
|
|
end
|
|
end
|
|
|
|
def self.dump(obj, an_io=nil, limit=nil)
|
|
unless limit
|
|
if Rubinius::Type.object_kind_of? an_io, Fixnum
|
|
limit = an_io
|
|
an_io = nil
|
|
else
|
|
limit = -1
|
|
end
|
|
end
|
|
|
|
depth = Rubinius::Type.coerce_to limit, Fixnum, :to_int
|
|
ms = State.new nil, depth, nil
|
|
|
|
if an_io
|
|
if !Rubinius::Type.object_respond_to? an_io, :write
|
|
raise TypeError, "output must respond to write"
|
|
end
|
|
if Rubinius::Type.object_respond_to? an_io, :binmode
|
|
an_io.binmode
|
|
end
|
|
end
|
|
|
|
str = Rubinius::Type.binary_string(VERSION_STRING) + ms.serialize(obj)
|
|
|
|
if an_io
|
|
an_io.write(str)
|
|
return an_io
|
|
end
|
|
|
|
return str
|
|
end
|
|
|
|
def self.load(obj, prc = nil)
|
|
if Rubinius::Type.object_respond_to? obj, :to_str
|
|
data = obj.to_s
|
|
|
|
major = data.getbyte 0
|
|
minor = data.getbyte 1
|
|
|
|
ms = StringState.new data, nil, prc
|
|
|
|
elsif Rubinius::Type.object_respond_to? obj, :read and
|
|
Rubinius::Type.object_respond_to? obj, :getc
|
|
ms = IOState.new obj, nil, prc
|
|
|
|
major = ms.consume_byte
|
|
minor = ms.consume_byte
|
|
else
|
|
raise TypeError, "instance of IO needed"
|
|
end
|
|
|
|
if major != MAJOR_VERSION or minor > MINOR_VERSION
|
|
raise TypeError, "incompatible marshal file format (can't be read)\n\tformat version #{MAJOR_VERSION}.#{MINOR_VERSION} required; #{major.inspect}.#{minor.inspect} given"
|
|
end
|
|
|
|
ms.construct
|
|
end
|
|
|
|
class << self
|
|
alias_method :restore, :load
|
|
end
|
|
|
|
end
|