Class: RBS::WASM::Deserializer

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initialize(bytes, buffer) ⇒ Deserializer

Returns a new instance of Deserializer.

Parameters:

  • bytes (String)
  • buffer (Buffer)


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.

Parameters:

  • bytes (String)
  • buffer (Buffer)

Returns:

  • (Object)


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).

Parameters:

  • bytes (String)
  • buffer (Buffer)

Returns:

  • (Array[untyped])


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).

Parameters:

  • bytes (String)
  • buffer (Buffer)

Returns:



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

Parameters:

  • name (String)

Returns:

  • (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_nameSymbol, ...

Returns:

  • (Symbol, false, nil)


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_countInteger

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.

Returns:

  • (Integer)


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

Parameters:

  • reader (Object)

Returns:

  • (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_hashHash[untyped, untyped]

Returns:

  • (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_i32Integer

Returns:

  • (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.

Parameters:

  • loc_children (Array[untyped], nil)

Returns:



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_valueLocation?

A standalone location range field: nil or an RBS::Location without children.

Returns:



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_listArray[Location?]

Returns:



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_nodeObject

Reads the next node and returns the reconstructed Ruby value.

Returns:

  • (Object)


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_listArray[untyped]

Returns:

  • (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.

Returns:

  • ([ Integer, Integer ], nil)


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

Parameters:

  • encoding (Encoding)

Returns:

  • (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

Parameters:

  • entry (Array[untyped])

Returns:

  • (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_tokensArray[[ 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.

Returns:



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_u32Integer

Returns:

  • (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_u8Integer

Returns:

  • (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