Class: RBS::WASM::Deserializer
- Inherits:
-
Object
- Object
- RBS::WASM::Deserializer
- Defined in:
- lib/rbs/wasm/deserializer.rb,
sig/wasm/deserializer.rbs
Overview
Rebuilds RBS::AST objects from the binary buffer produced by
rbs_serialize_node (src/serialize.c), driven by SerializationSchema.
Class Method Summary collapse
-
.deserialize(bytes, buffer) ⇒ Object
Deserialize a buffer produced for a whole signature, returning
[directives, declarations]to match RBS::Parser._parse_signature. -
.deserialize_node_list(bytes, buffer) ⇒ Array[untyped]
Deserialize a bare node list (RBS::Parser._parse_type_params).
-
.deserialize_tokens(bytes, buffer) ⇒ Array[[ Symbol, Location ]]
Deserialize the token stream from rbs_wasm_lex (RBS::Parser._lex).
Instance Method Summary collapse
- #class_for(name) ⇒ Object
-
#initialize(bytes, buffer) ⇒ Deserializer
constructor
A new instance of Deserializer.
- #read_attr_ivar_name ⇒ Symbol, ...
-
#read_count ⇒ Integer
A count of nested items.
- #read_field(reader) ⇒ Object
- #read_hash ⇒ Hash[untyped, untyped]
- #read_i32 ⇒ Integer
-
#read_location(loc_children) ⇒ Location?
The base location of a node, followed by its named child ranges.
-
#read_location_value ⇒ Location?
A standalone location range field: nil or an RBS::Location without children.
- #read_location_value_list ⇒ Array[Location?]
-
#read_node ⇒ Object
Reads the next node and returns the reconstructed Ruby value.
- #read_node_list ⇒ Array[untyped]
-
#read_range ⇒ [ Integer, Integer ]?
Reads a presence byte and, when present, the start/end character positions.
- #read_string(encoding) ⇒ String
- #read_struct(entry) ⇒ Object
-
#read_tokens ⇒ Array[[ Symbol, Location ]]
The lex stream has no leading count: read records until the buffer is exhausted.
- #read_u32 ⇒ Integer
- #read_u8 ⇒ Integer
Constructor Details
#initialize(bytes, buffer) ⇒ Deserializer
Returns a new instance of Deserializer.
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rbs/wasm/deserializer.rb', line 34 def initialize(bytes, buffer) @bytes = bytes @buffer = buffer # Symbols and rbs_string fields (comments, annotations) inherit the # source encoding, matching ast_translation.c. String/Integer literal # nodes are always UTF-8 (see read_node). @encoding = buffer.content.encoding @pos = 0 @class_cache = {} #: Hash[String, untyped] end |
Class Method Details
.deserialize(bytes, buffer) ⇒ Object
Deserialize a buffer produced for a whole signature, returning
[directives, declarations] to match RBS::Parser._parse_signature.
18 19 20 |
# File 'lib/rbs/wasm/deserializer.rb', line 18 def self.deserialize(bytes, buffer) new(bytes, buffer).read_node end |
.deserialize_node_list(bytes, buffer) ⇒ Array[untyped]
Deserialize a bare node list (RBS::Parser._parse_type_params).
24 25 26 |
# File 'lib/rbs/wasm/deserializer.rb', line 24 def self.deserialize_node_list(bytes, buffer) new(bytes, buffer).read_node_list end |
.deserialize_tokens(bytes, buffer) ⇒ Array[[ Symbol, Location ]]
Deserialize the token stream from rbs_wasm_lex (RBS::Parser._lex).
30 31 32 |
# File 'lib/rbs/wasm/deserializer.rb', line 30 def self.deserialize_tokens(bytes, buffer) new(bytes, buffer).read_tokens end |
Instance Method Details
#class_for(name) ⇒ Object
208 209 210 |
# File 'lib/rbs/wasm/deserializer.rb', line 208 def class_for(name) @class_cache[name] ||= Object.const_get(name) end |
#read_attr_ivar_name ⇒ Symbol, ...
166 167 168 169 170 171 172 |
# File 'lib/rbs/wasm/deserializer.rb', line 166 def read_attr_ivar_name case read_u8 when 0 then nil # inferred instance variable when 1 then false # no instance variable else read_string(@encoding).to_sym end end |
#read_count ⇒ Integer
A count of nested items. Each item is at least one byte, so a count that exceeds the bytes remaining signals the cursor has drifted out of sync.
130 131 132 133 134 135 136 |
# File 'lib/rbs/wasm/deserializer.rb', line 130 def read_count count = read_u32 if count > @bytes.bytesize - @pos raise "Corrupt buffer: count #{count} exceeds #{@bytes.bytesize - @pos} remaining bytes at offset #{@pos}" end count end |
#read_field(reader) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rbs/wasm/deserializer.rb', line 104 def read_field(reader) case reader when :node then read_node when :node_list then read_node_list when :hash then read_hash when :string then read_string(@encoding) when :bool then read_u8 != 0 when :location_range then read_location_value when :location_range_list then read_location_value_list when :attr_ivar_name then read_attr_ivar_name else # [:enum, [value_or_nil, ...]] reader[1][read_u8] end end |
#read_hash ⇒ Hash[untyped, untyped]
119 120 121 122 123 124 125 126 |
# File 'lib/rbs/wasm/deserializer.rb', line 119 def read_hash hash = {} #: Hash[untyped, untyped] read_count.times do key = read_node hash[key] = read_node end hash end |
#read_i32 ⇒ Integer
195 196 197 198 199 |
# File 'lib/rbs/wasm/deserializer.rb', line 195 def read_i32 value = @bytes.unpack1("l<", offset: @pos) #: Integer @pos += 4 value end |
#read_location(loc_children) ⇒ Location?
The base location of a node, followed by its named child ranges.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/rbs/wasm/deserializer.rb', line 139 def read_location(loc_children) base = read_range children = (loc_children || []).map { |name, required| [name, required, read_range] } return nil unless base location = RBS::Location.new(@buffer, base[0], base[1]) children.each do |name, required, range| if required location.add_required_child(name, range[0]...range[1]) if range else location.add_optional_child(name, range ? (range[0]...range[1]) : nil) end end location end |
#read_location_value ⇒ Location?
A standalone location range field: nil or an RBS::Location without children.
157 158 159 160 |
# File 'lib/rbs/wasm/deserializer.rb', line 157 def read_location_value range = read_range range && RBS::Location.new(@buffer, range[0], range[1]) end |
#read_location_value_list ⇒ Array[Location?]
162 163 164 |
# File 'lib/rbs/wasm/deserializer.rb', line 162 def read_location_value_list Array.new(read_count) { read_location_value } end |
#read_node ⇒ Object
Reads the next node and returns the reconstructed Ruby value.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'sig/wasm/deserializer.rbs', line 29 def read_node tag = read_u8 return nil if tag == 0 return read_string(@encoding).to_sym if tag == SerializationSchema::SYMBOL_TAG entry = SerializationSchema::SCHEMA[tag] or raise "Unknown node tag: #{tag}" case entry[0] when :node then read_struct(entry) when :bool then read_u8 != 0 when :integer then read_string(Encoding::UTF_8).to_i when :string_value then read_string(Encoding::UTF_8) when :record_field then [read_node, read_u8 != 0] when :signature then [read_node_list, read_node_list] when :namespace then RBS::Namespace[read_node_list, read_u8 != 0] when :type_name then RBS::TypeName[read_node, read_node] else raise "Unknown schema entry kind: #{entry[0].inspect}" end end |
#read_node_list ⇒ Array[untyped]
65 66 67 |
# File 'lib/rbs/wasm/deserializer.rb', line 65 def read_node_list Array.new(read_count) { read_node } end |
#read_range ⇒ [ Integer, Integer ]?
Reads a presence byte and, when present, the start/end character positions.
175 176 177 178 179 180 181 |
# File 'lib/rbs/wasm/deserializer.rb', line 175 def read_range return nil if read_u8 == 0 start_char = read_i32 end_char = read_i32 [start_char, end_char] end |
#read_string(encoding) ⇒ String
201 202 203 204 205 206 |
# File 'lib/rbs/wasm/deserializer.rb', line 201 def read_string(encoding) length = read_u32 string = @bytes.byteslice(@pos, length) or raise "Unexpected end of buffer" @pos += length string.force_encoding(encoding) end |
#read_struct(entry) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/rbs/wasm/deserializer.rb', line 84 def read_struct(entry) _, class_name, expose_location, loc_children, fields, resolve_type_params = entry location = read_location(loc_children) if expose_location kwargs = {} #: Hash[Symbol, untyped] (fields || []).each do |name, reader| kwargs[name] = read_field(reader) end RBS::AST::TypeParam.resolve_variables(kwargs[:type_params]) if resolve_type_params klass = class_for(class_name) if expose_location klass.new(location: location, **kwargs) else klass.new(**kwargs) end end |
#read_tokens ⇒ Array[[ Symbol, Location ]]
The lex stream has no leading count: read records until the buffer is exhausted. Each is a token type name followed by its character range.
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rbs/wasm/deserializer.rb', line 71 def read_tokens tokens = [] #: Array[[ Symbol, Location ]] until @pos >= @bytes.bytesize type = read_string(Encoding::UTF_8).to_sym start_char = read_i32 end_char = read_i32 tokens << [type, RBS::Location.new(@buffer, start_char, end_char)] end tokens end |
#read_u32 ⇒ Integer
189 190 191 192 193 |
# File 'lib/rbs/wasm/deserializer.rb', line 189 def read_u32 value = @bytes.unpack1("L<", offset: @pos) #: Integer @pos += 4 value end |
#read_u8 ⇒ Integer
183 184 185 186 187 |
# File 'lib/rbs/wasm/deserializer.rb', line 183 def read_u8 byte = @bytes.getbyte(@pos) or raise "Unexpected end of buffer" @pos += 1 byte end |