wardite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +35 -0
- data/Rakefile +19 -0
- data/Steepfile +37 -0
- data/examples/.gitignore +1 -0
- data/examples/add.wasm +0 -0
- data/examples/add.wat +7 -0
- data/examples/call.wat +11 -0
- data/examples/helloworld.wat +24 -0
- data/examples/i32_const.wat +6 -0
- data/examples/i32_store.wat +9 -0
- data/examples/local_set.wat +8 -0
- data/examples/memory.wat +5 -0
- data/exe/wardite +15 -0
- data/lib/wardite/const.rb +20 -0
- data/lib/wardite/instruction.rb +71 -0
- data/lib/wardite/leb128.rb +58 -0
- data/lib/wardite/version.rb +6 -0
- data/lib/wardite/wasi.rb +67 -0
- data/lib/wardite.rb +1149 -0
- data/sig/generated/wardite/const.rbs +34 -0
- data/sig/generated/wardite/instruction.rbs +27 -0
- data/sig/generated/wardite/leb128.rbs +14 -0
- data/sig/generated/wardite/version.rbs +5 -0
- data/sig/generated/wardite/wasi.rbs +24 -0
- data/sig/generated/wardite.rbs +384 -0
- data/sig/wardite.rbs +6 -0
- metadata +71 -0
data/lib/wardite.rb
ADDED
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative "wardite/version"
|
|
5
|
+
require_relative "wardite/leb128"
|
|
6
|
+
require_relative "wardite/const"
|
|
7
|
+
require_relative "wardite/instruction"
|
|
8
|
+
require_relative "wardite/wasi"
|
|
9
|
+
|
|
10
|
+
require "stringio"
|
|
11
|
+
|
|
12
|
+
module Wardite
|
|
13
|
+
class Section
|
|
14
|
+
attr_accessor :name #: String
|
|
15
|
+
|
|
16
|
+
attr_accessor :code #: Integer
|
|
17
|
+
|
|
18
|
+
attr_accessor :size #: Integer
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class TypeSection < Section
|
|
22
|
+
attr_accessor :defined_types #: Array[Array[Symbol]]
|
|
23
|
+
|
|
24
|
+
attr_accessor :defined_results #: Array[Array[Symbol]]
|
|
25
|
+
|
|
26
|
+
# @rbs return: void
|
|
27
|
+
def initialize
|
|
28
|
+
self.name = "Type"
|
|
29
|
+
self.code = 0x1
|
|
30
|
+
|
|
31
|
+
@defined_types = []
|
|
32
|
+
@defined_results = []
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class FunctionSection < Section
|
|
37
|
+
attr_accessor :func_indices #: Array[Integer]
|
|
38
|
+
|
|
39
|
+
# @rbs return: void
|
|
40
|
+
def initialize
|
|
41
|
+
self.name = "Function"
|
|
42
|
+
self.code = 0x3
|
|
43
|
+
|
|
44
|
+
@func_indices = []
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class MemorySection < Section
|
|
49
|
+
attr_accessor :limits #: Array[[Integer, Integer|nil]]
|
|
50
|
+
|
|
51
|
+
# @rbs return: void
|
|
52
|
+
def initialize
|
|
53
|
+
self.name = "Memory"
|
|
54
|
+
self.code = 0x5
|
|
55
|
+
|
|
56
|
+
@limits = []
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class CodeSection < Section
|
|
61
|
+
class CodeBody
|
|
62
|
+
attr_accessor :locals_count #: Array[Integer]
|
|
63
|
+
|
|
64
|
+
attr_accessor :locals_type #: Array[Symbol]
|
|
65
|
+
|
|
66
|
+
attr_accessor :body #: Array[Op]
|
|
67
|
+
|
|
68
|
+
# @rbs &blk: (CodeBody) -> void
|
|
69
|
+
# @rbs return: void
|
|
70
|
+
def initialize(&blk)
|
|
71
|
+
blk.call(self)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
attr_accessor :func_codes #:Array[CodeBody]
|
|
76
|
+
|
|
77
|
+
# @rbs return: void
|
|
78
|
+
def initialize
|
|
79
|
+
self.name = "Code"
|
|
80
|
+
self.code = 0xa
|
|
81
|
+
|
|
82
|
+
@func_codes = []
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class DataSection < Section
|
|
87
|
+
class Segment
|
|
88
|
+
attr_accessor :flags #: Integer
|
|
89
|
+
|
|
90
|
+
attr_accessor :offset #: Integer
|
|
91
|
+
|
|
92
|
+
attr_accessor :data #: String
|
|
93
|
+
|
|
94
|
+
# @rbs &blk: (Segment) -> void
|
|
95
|
+
# @rbs return: void
|
|
96
|
+
def initialize(&blk)
|
|
97
|
+
blk.call(self)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
attr_accessor :segments #: Array[Segment]
|
|
102
|
+
|
|
103
|
+
# @rbs return: void
|
|
104
|
+
def initialize
|
|
105
|
+
self.name = "Data"
|
|
106
|
+
self.code = 0xb
|
|
107
|
+
|
|
108
|
+
@segments = []
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class ExportSection < Section
|
|
113
|
+
class ExportDesc
|
|
114
|
+
attr_accessor :name #: String
|
|
115
|
+
|
|
116
|
+
attr_accessor :kind #: Integer
|
|
117
|
+
|
|
118
|
+
attr_accessor :func_index #: Integer
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
attr_accessor :exports #: Hash[String, ExportDesc]
|
|
122
|
+
|
|
123
|
+
def initialize #: void
|
|
124
|
+
self.name = "Export"
|
|
125
|
+
self.code = 0x7
|
|
126
|
+
|
|
127
|
+
@exports = {}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @rbs &blk: (ExportDesc) -> void
|
|
131
|
+
def add_desc(&blk)
|
|
132
|
+
desc = ExportDesc.new
|
|
133
|
+
blk.call(desc)
|
|
134
|
+
self.exports[desc.name] = desc
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class ImportSection < Section
|
|
139
|
+
class ImportDesc
|
|
140
|
+
attr_accessor :module_name #: String
|
|
141
|
+
|
|
142
|
+
attr_accessor :name #: String
|
|
143
|
+
|
|
144
|
+
attr_accessor :kind #: Integer
|
|
145
|
+
|
|
146
|
+
attr_accessor :sig_index #: Integer
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
attr_accessor :imports #: Array[ImportDesc]
|
|
150
|
+
|
|
151
|
+
def initialize #: void
|
|
152
|
+
self.name = "Import"
|
|
153
|
+
self.code = 0x2
|
|
154
|
+
|
|
155
|
+
@imports = []
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @rbs &blk: (ImportDesc) -> void
|
|
159
|
+
def add_desc(&blk)
|
|
160
|
+
desc = ImportDesc.new
|
|
161
|
+
blk.call(desc) if blk
|
|
162
|
+
self.imports << desc
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
module BinaryLoader
|
|
167
|
+
extend Wardite::Leb128Helpers
|
|
168
|
+
|
|
169
|
+
# @rbs buf: File|StringIO
|
|
170
|
+
# @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
|
|
171
|
+
# @rbs enable_wasi: boolish
|
|
172
|
+
# @rbs return: Instance
|
|
173
|
+
def self.load_from_buffer(buf, import_object: {}, enable_wasi: true)
|
|
174
|
+
@buf = buf
|
|
175
|
+
|
|
176
|
+
version = preamble
|
|
177
|
+
sections_ = sections
|
|
178
|
+
|
|
179
|
+
if enable_wasi
|
|
180
|
+
wasi_env = Wardite::WasiSnapshotPreview1.new
|
|
181
|
+
import_object[:wasi_snapshot_preview1] = wasi_env.to_module
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
return Instance.new(import_object) do |i|
|
|
185
|
+
i.version = version
|
|
186
|
+
i.sections = sections_
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @rbs return: Integer
|
|
191
|
+
def self.preamble
|
|
192
|
+
asm = @buf.read 4
|
|
193
|
+
if !asm
|
|
194
|
+
raise LoadError, "buffer too short"
|
|
195
|
+
end
|
|
196
|
+
if asm != "\u0000asm"
|
|
197
|
+
raise LoadError, "invalid preamble"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
vstr = @buf.read(4)
|
|
201
|
+
if !vstr
|
|
202
|
+
raise LoadError, "buffer too short"
|
|
203
|
+
end
|
|
204
|
+
version = vstr.to_enum(:chars)
|
|
205
|
+
.with_index
|
|
206
|
+
.inject(0) {|dest, (c, i)| dest | (c.ord << i*8) }
|
|
207
|
+
if version != 1
|
|
208
|
+
raise LoadError, "unsupported version: #{version}"
|
|
209
|
+
end
|
|
210
|
+
version
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# @rbs return: Array[Section]
|
|
214
|
+
def self.sections
|
|
215
|
+
sections = [] #: Array[Section]
|
|
216
|
+
|
|
217
|
+
loop do
|
|
218
|
+
byte = @buf.read(1)
|
|
219
|
+
if !byte
|
|
220
|
+
break
|
|
221
|
+
end
|
|
222
|
+
code = byte.ord
|
|
223
|
+
|
|
224
|
+
section = case code
|
|
225
|
+
when Wardite::SectionType
|
|
226
|
+
type_section
|
|
227
|
+
when Wardite::SectionImport
|
|
228
|
+
import_section
|
|
229
|
+
when Wardite::SectionFunction
|
|
230
|
+
function_section
|
|
231
|
+
when Wardite::SectionTable
|
|
232
|
+
unimplemented_skip_section(code)
|
|
233
|
+
when Wardite::SectionMemory
|
|
234
|
+
memory_section
|
|
235
|
+
when Wardite::SectionGlobal
|
|
236
|
+
unimplemented_skip_section(code)
|
|
237
|
+
when Wardite::SectionExport
|
|
238
|
+
export_section
|
|
239
|
+
when Wardite::SectionStart
|
|
240
|
+
unimplemented_skip_section(code)
|
|
241
|
+
when Wardite::SectionElement
|
|
242
|
+
unimplemented_skip_section(code)
|
|
243
|
+
when Wardite::SectionCode
|
|
244
|
+
code_section
|
|
245
|
+
when Wardite::SectionData
|
|
246
|
+
data_section
|
|
247
|
+
when Wardite::SectionCustom
|
|
248
|
+
unimplemented_skip_section(code)
|
|
249
|
+
else
|
|
250
|
+
raise LoadError, "unknown code: #{code}(\"#{code.to_s 16}\")"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
if section
|
|
254
|
+
sections << section
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
sections
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @rbs return: TypeSection
|
|
261
|
+
def self.type_section
|
|
262
|
+
dest = TypeSection.new
|
|
263
|
+
|
|
264
|
+
size = fetch_uleb128(@buf)
|
|
265
|
+
dest.size = size
|
|
266
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
267
|
+
|
|
268
|
+
len = fetch_uleb128(sbuf)
|
|
269
|
+
len.times do |i|
|
|
270
|
+
fncode = assert_read(sbuf, 1)
|
|
271
|
+
if fncode != "\x60"
|
|
272
|
+
raise LoadError, "not a function definition"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
arglen = fetch_uleb128(sbuf)
|
|
276
|
+
arg = []
|
|
277
|
+
arglen.times do
|
|
278
|
+
case ty = assert_read(sbuf, 1)&.ord
|
|
279
|
+
when 0x7f
|
|
280
|
+
arg << :i32
|
|
281
|
+
when 0x7e
|
|
282
|
+
arg << :i64
|
|
283
|
+
else
|
|
284
|
+
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
dest.defined_types << arg
|
|
288
|
+
|
|
289
|
+
retlen = fetch_uleb128(sbuf)
|
|
290
|
+
ret = []
|
|
291
|
+
retlen.times do
|
|
292
|
+
case ty = assert_read(sbuf, 1)&.ord
|
|
293
|
+
when 0x7f
|
|
294
|
+
ret << :i32
|
|
295
|
+
when 0x7e
|
|
296
|
+
ret << :i64
|
|
297
|
+
else
|
|
298
|
+
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
dest.defined_results << ret
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
dest
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# @rbs return: ImportSection
|
|
308
|
+
def self.import_section
|
|
309
|
+
dest = ImportSection.new
|
|
310
|
+
size = fetch_uleb128(@buf)
|
|
311
|
+
dest.size = size
|
|
312
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
313
|
+
|
|
314
|
+
len = fetch_uleb128(sbuf)
|
|
315
|
+
len.times do |i|
|
|
316
|
+
mlen = fetch_uleb128(sbuf)
|
|
317
|
+
module_name = assert_read(sbuf, mlen)
|
|
318
|
+
nlen = fetch_uleb128(sbuf)
|
|
319
|
+
name = assert_read(sbuf, nlen)
|
|
320
|
+
kind_ = assert_read(sbuf, 1)
|
|
321
|
+
kind = kind_[0]&.ord
|
|
322
|
+
if !kind
|
|
323
|
+
raise "[BUG] empty unpacked string" # guard rbs
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
index = fetch_uleb128(sbuf)
|
|
327
|
+
dest.add_desc do |desc|
|
|
328
|
+
desc.module_name = module_name
|
|
329
|
+
desc.name = name
|
|
330
|
+
desc.kind = kind
|
|
331
|
+
desc.sig_index = index
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
dest
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# @rbs return: MemorySection
|
|
339
|
+
def self.memory_section
|
|
340
|
+
dest = MemorySection.new
|
|
341
|
+
size = fetch_uleb128(@buf)
|
|
342
|
+
dest.size = size
|
|
343
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
344
|
+
|
|
345
|
+
len = fetch_uleb128(sbuf)
|
|
346
|
+
if len != 1
|
|
347
|
+
raise LoadError, "memory section has invalid size: #{len}"
|
|
348
|
+
end
|
|
349
|
+
len.times do |i|
|
|
350
|
+
flags = fetch_uleb128(sbuf)
|
|
351
|
+
min = fetch_uleb128(sbuf)
|
|
352
|
+
|
|
353
|
+
max = nil
|
|
354
|
+
if flags != 0
|
|
355
|
+
max = fetch_uleb128(sbuf)
|
|
356
|
+
end
|
|
357
|
+
dest.limits << [min, max]
|
|
358
|
+
end
|
|
359
|
+
dest
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# @rbs return: FunctionSection
|
|
363
|
+
def self.function_section
|
|
364
|
+
dest = FunctionSection.new
|
|
365
|
+
size = fetch_uleb128(@buf)
|
|
366
|
+
dest.size = size
|
|
367
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
368
|
+
|
|
369
|
+
len = fetch_uleb128(sbuf)
|
|
370
|
+
len.times do |i|
|
|
371
|
+
index = fetch_uleb128(sbuf)
|
|
372
|
+
dest.func_indices << index
|
|
373
|
+
end
|
|
374
|
+
dest
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# @rbs return: CodeSection
|
|
378
|
+
def self.code_section
|
|
379
|
+
dest = CodeSection.new
|
|
380
|
+
size = fetch_uleb128(@buf)
|
|
381
|
+
dest.size = size
|
|
382
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
383
|
+
|
|
384
|
+
len = fetch_uleb128(sbuf)
|
|
385
|
+
len.times do |i|
|
|
386
|
+
ilen = fetch_uleb128(sbuf)
|
|
387
|
+
code = assert_read(sbuf, ilen)
|
|
388
|
+
last_code = code[-1]
|
|
389
|
+
if ! last_code
|
|
390
|
+
raise "[BUG] empty code fetched" # guard for steep check
|
|
391
|
+
end
|
|
392
|
+
if last_code.ord != 0x0b
|
|
393
|
+
$stderr.puts "warning: instruction not ended with inst end(0x0b): 0x0#{last_code.ord}"
|
|
394
|
+
end
|
|
395
|
+
cbuf = StringIO.new(code)
|
|
396
|
+
locals_count = []
|
|
397
|
+
locals_type = []
|
|
398
|
+
locals_len = fetch_uleb128(cbuf)
|
|
399
|
+
locals_len.times do
|
|
400
|
+
type_count = fetch_uleb128(cbuf)
|
|
401
|
+
locals_count << type_count
|
|
402
|
+
value_type = assert_read(cbuf, 1)&.ord
|
|
403
|
+
locals_type << Op.i2type(value_type || -1)
|
|
404
|
+
end
|
|
405
|
+
body = code_body(cbuf)
|
|
406
|
+
dest.func_codes << CodeSection::CodeBody.new do |b|
|
|
407
|
+
b.locals_count = locals_count
|
|
408
|
+
b.locals_type = locals_type
|
|
409
|
+
b.body = body
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
dest
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# @rbs buf: StringIO
|
|
416
|
+
# @rbs return: Array[::Wardite::Op]
|
|
417
|
+
def self.code_body(buf)
|
|
418
|
+
dest = []
|
|
419
|
+
while c = buf.read(1)
|
|
420
|
+
code = Op.to_sym(c)
|
|
421
|
+
operand_types = Op.operand_of(code)
|
|
422
|
+
operand = []
|
|
423
|
+
operand_types.each do |typ|
|
|
424
|
+
case typ
|
|
425
|
+
when :u32
|
|
426
|
+
operand << fetch_uleb128(buf)
|
|
427
|
+
when :i32
|
|
428
|
+
operand << fetch_sleb128(buf)
|
|
429
|
+
else
|
|
430
|
+
$stderr.puts "warning: unknown type #{typ.inspect}. defaulting to u32"
|
|
431
|
+
operand << fetch_uleb128(buf)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
dest << Op.new(code, operand)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
dest
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# @rbs return: DataSection
|
|
442
|
+
def self.data_section
|
|
443
|
+
dest = DataSection.new
|
|
444
|
+
size = fetch_uleb128(@buf)
|
|
445
|
+
dest.size = size
|
|
446
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
447
|
+
|
|
448
|
+
len = fetch_uleb128(sbuf)
|
|
449
|
+
len.times do |i|
|
|
450
|
+
mem_index = fetch_uleb128(sbuf)
|
|
451
|
+
code = fetch_insn_while_end(sbuf)
|
|
452
|
+
ops = code_body(StringIO.new(code))
|
|
453
|
+
offset = decode_expr(ops)
|
|
454
|
+
|
|
455
|
+
len = fetch_uleb128(sbuf)
|
|
456
|
+
data = sbuf.read len
|
|
457
|
+
if !data
|
|
458
|
+
raise LoadError, "buffer too short"
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
segment = DataSection::Segment.new do |seg|
|
|
462
|
+
seg.flags = mem_index
|
|
463
|
+
seg.offset = offset
|
|
464
|
+
seg.data = data
|
|
465
|
+
end
|
|
466
|
+
dest.segments << segment
|
|
467
|
+
end
|
|
468
|
+
dest
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# @rbs sbuf: StringIO
|
|
472
|
+
# @rbs return: String
|
|
473
|
+
def self.fetch_insn_while_end(sbuf)
|
|
474
|
+
code = String.new("")
|
|
475
|
+
loop {
|
|
476
|
+
c = sbuf.read 1
|
|
477
|
+
if !c
|
|
478
|
+
break
|
|
479
|
+
end
|
|
480
|
+
code << c
|
|
481
|
+
if c == "\u000b" # :end
|
|
482
|
+
break
|
|
483
|
+
end
|
|
484
|
+
}
|
|
485
|
+
code
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# @rbs ops: Array[Op]
|
|
489
|
+
# @rbs return: Integer
|
|
490
|
+
def self.decode_expr(ops)
|
|
491
|
+
# sees first opcode
|
|
492
|
+
op = ops.first
|
|
493
|
+
if !op
|
|
494
|
+
raise LoadError, "empty opcodes"
|
|
495
|
+
end
|
|
496
|
+
case op.code
|
|
497
|
+
when :i32_const
|
|
498
|
+
arg = op.operand[0]
|
|
499
|
+
if !arg.is_a?(Integer)
|
|
500
|
+
raise "Invalid definition of operand"
|
|
501
|
+
end
|
|
502
|
+
return arg
|
|
503
|
+
else
|
|
504
|
+
raise "Unimplemented offset op: #{op.code.inspect}"
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# @rbs return: ExportSection
|
|
509
|
+
def self.export_section
|
|
510
|
+
dest = ExportSection.new
|
|
511
|
+
size = fetch_uleb128(@buf)
|
|
512
|
+
dest.size = size
|
|
513
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
|
514
|
+
|
|
515
|
+
len = fetch_uleb128(sbuf)
|
|
516
|
+
len.times do |i|
|
|
517
|
+
nlen = fetch_uleb128(sbuf)
|
|
518
|
+
name = assert_read(sbuf, nlen)
|
|
519
|
+
kind_ = assert_read(sbuf, 1)
|
|
520
|
+
kind = kind_[0]&.ord
|
|
521
|
+
if !kind
|
|
522
|
+
raise "[BUG] empty unpacked string" # guard rbs
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
index = fetch_uleb128(sbuf)
|
|
526
|
+
dest.add_desc do |desc|
|
|
527
|
+
desc.name = name
|
|
528
|
+
desc.kind = kind
|
|
529
|
+
desc.func_index = index
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
dest
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# @rbs code: Integer
|
|
537
|
+
# @rbs return: nil
|
|
538
|
+
def self.unimplemented_skip_section(code)
|
|
539
|
+
$stderr.puts "warning: unimplemented section: 0x0#{code}"
|
|
540
|
+
size = @buf.read(1)&.ord
|
|
541
|
+
@buf.read(size)
|
|
542
|
+
nil
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# @rbs sbuf: StringIO
|
|
546
|
+
# @rbs n: Integer
|
|
547
|
+
# @rbs return: String
|
|
548
|
+
def self.assert_read(sbuf, n)
|
|
549
|
+
ret = sbuf.read n
|
|
550
|
+
if !ret
|
|
551
|
+
raise LoadError, "too short section size"
|
|
552
|
+
end
|
|
553
|
+
if ret.size != n
|
|
554
|
+
raise LoadError, "too short section size"
|
|
555
|
+
end
|
|
556
|
+
ret
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
class Instance
|
|
561
|
+
attr_accessor :version #: Integer
|
|
562
|
+
|
|
563
|
+
attr_accessor :sections #: Array[Section]
|
|
564
|
+
|
|
565
|
+
attr_accessor :runtime #: Runtime
|
|
566
|
+
|
|
567
|
+
attr_accessor :store #: Store
|
|
568
|
+
|
|
569
|
+
attr_accessor :exports #: Exports
|
|
570
|
+
|
|
571
|
+
attr_reader :import_object #: Hash[Symbol, Hash[Symbol, Proc]]
|
|
572
|
+
|
|
573
|
+
# @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
|
|
574
|
+
# @rbs &blk: (Instance) -> void
|
|
575
|
+
def initialize(import_object, &blk)
|
|
576
|
+
blk.call(self)
|
|
577
|
+
@import_object = import_object
|
|
578
|
+
|
|
579
|
+
@store = Store.new(self)
|
|
580
|
+
@exports = Exports.new(self.export_section, store)
|
|
581
|
+
@runtime = Runtime.new(self)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# @rbs return: ImportSection
|
|
585
|
+
def import_section
|
|
586
|
+
sec = @sections.find{|s| s.code == Const::SectionImport }
|
|
587
|
+
if !sec.is_a?(ImportSection)
|
|
588
|
+
# returns dummy empty section
|
|
589
|
+
return ImportSection.new
|
|
590
|
+
end
|
|
591
|
+
sec
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# @rbs return: TypeSection|nil
|
|
595
|
+
def type_section
|
|
596
|
+
sec = @sections.find{|s| s.code == Wardite::Const::SectionType }
|
|
597
|
+
if !sec
|
|
598
|
+
return nil
|
|
599
|
+
end
|
|
600
|
+
if !sec.is_a?(TypeSection)
|
|
601
|
+
raise(GenericError, "instance doesn't have required section")
|
|
602
|
+
end
|
|
603
|
+
sec
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# @rbs return: MemorySection|nil
|
|
607
|
+
def memory_section
|
|
608
|
+
sec = @sections.find{|s| s.code == Const::SectionMemory }
|
|
609
|
+
if !sec
|
|
610
|
+
return nil
|
|
611
|
+
end
|
|
612
|
+
if !sec.is_a?(MemorySection)
|
|
613
|
+
raise(GenericError, "[BUG] found invalid memory section")
|
|
614
|
+
end
|
|
615
|
+
sec
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# @rbs return: DataSection|nil
|
|
619
|
+
def data_section
|
|
620
|
+
sec = @sections.find{|s| s.code == Const::SectionData }
|
|
621
|
+
if !sec
|
|
622
|
+
return nil
|
|
623
|
+
end
|
|
624
|
+
if !sec.is_a?(DataSection)
|
|
625
|
+
raise(GenericError, "[BUG] found invalid data section")
|
|
626
|
+
end
|
|
627
|
+
sec
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# @rbs return: FunctionSection|nil
|
|
631
|
+
def function_section
|
|
632
|
+
sec = @sections.find{|s| s.code == Const::SectionFunction }
|
|
633
|
+
if !sec
|
|
634
|
+
return nil
|
|
635
|
+
end
|
|
636
|
+
if !sec.is_a?(FunctionSection)
|
|
637
|
+
raise(GenericError, "instance doesn't have required section")
|
|
638
|
+
end
|
|
639
|
+
sec
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
# @rbs return: CodeSection|nil
|
|
643
|
+
def code_section
|
|
644
|
+
sec = @sections.find{|s| s.code == Const::SectionCode }
|
|
645
|
+
if !sec
|
|
646
|
+
return nil
|
|
647
|
+
end
|
|
648
|
+
if !sec.is_a?(CodeSection)
|
|
649
|
+
raise(GenericError, "instance doesn't have required section")
|
|
650
|
+
end
|
|
651
|
+
sec
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# @rbs return: ExportSection
|
|
655
|
+
def export_section
|
|
656
|
+
sec = @sections.find{|s| s.code == Const::SectionExport }
|
|
657
|
+
if !sec
|
|
658
|
+
return ExportSection.new
|
|
659
|
+
end
|
|
660
|
+
if !sec.is_a?(ExportSection)
|
|
661
|
+
raise(GenericError, "instance doesn't have required section")
|
|
662
|
+
end
|
|
663
|
+
sec
|
|
664
|
+
end
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
class Runtime
|
|
668
|
+
attr_accessor :stack #: Array[Object]
|
|
669
|
+
|
|
670
|
+
attr_accessor :call_stack #: Array[Frame]
|
|
671
|
+
|
|
672
|
+
attr_reader :instance #: Instance
|
|
673
|
+
|
|
674
|
+
# @rbs inst: Instance
|
|
675
|
+
def initialize(inst)
|
|
676
|
+
@stack = []
|
|
677
|
+
@call_stack = []
|
|
678
|
+
@instance = inst
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# @rbs name: String|Symbol
|
|
682
|
+
# @rbs return: bool
|
|
683
|
+
def callable?(name)
|
|
684
|
+
!! @instance.exports[name.to_s]
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# @rbs name: String|Symbol
|
|
688
|
+
# @rbs args: Array[Object]
|
|
689
|
+
# @rbs return: Object|nil
|
|
690
|
+
def call(name, args)
|
|
691
|
+
if !callable?(name)
|
|
692
|
+
raise ::NoMethodError, "function #{name} not found"
|
|
693
|
+
end
|
|
694
|
+
kind, fn = @instance.exports[name.to_s]
|
|
695
|
+
if kind != 0
|
|
696
|
+
raise ::NoMethodError, "#{name} is not a function"
|
|
697
|
+
end
|
|
698
|
+
if fn.callsig.size != args.size
|
|
699
|
+
raise ArgumentError, "unmatch arg size"
|
|
700
|
+
end
|
|
701
|
+
args.each do |arg|
|
|
702
|
+
stack.push arg
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
case fn
|
|
706
|
+
when WasmFunction
|
|
707
|
+
invoke_internal(fn)
|
|
708
|
+
when ExternalFunction
|
|
709
|
+
invoke_external(fn)
|
|
710
|
+
else
|
|
711
|
+
raise GenericError, "registered pointer is not to a function"
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# @rbs idx: Integer
|
|
716
|
+
# @rbs args: Array[Object]
|
|
717
|
+
# @rbs return: Object|nil
|
|
718
|
+
def call_index(idx, args)
|
|
719
|
+
fn = self.instance.store[idx]
|
|
720
|
+
if !fn
|
|
721
|
+
# TODO: own error NoFunctionError
|
|
722
|
+
raise ::NoMethodError, "func #{idx} not found"
|
|
723
|
+
end
|
|
724
|
+
if fn.callsig.size != args.size
|
|
725
|
+
raise ArgumentError, "unmatch arg size"
|
|
726
|
+
end
|
|
727
|
+
args.each do |arg|
|
|
728
|
+
stack.push arg
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
case fn
|
|
732
|
+
when WasmFunction
|
|
733
|
+
invoke_internal(fn)
|
|
734
|
+
when ExternalFunction
|
|
735
|
+
invoke_external(fn)
|
|
736
|
+
else
|
|
737
|
+
raise GenericError, "registered pointer is not to a function"
|
|
738
|
+
end
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# @rbs wasm_function: WasmFunction
|
|
742
|
+
# @rbs return: void
|
|
743
|
+
def push_frame(wasm_function)
|
|
744
|
+
local_start = stack.size - wasm_function.callsig.size
|
|
745
|
+
locals = stack[local_start..]
|
|
746
|
+
if !locals
|
|
747
|
+
raise LoadError, "stack too short"
|
|
748
|
+
end
|
|
749
|
+
self.stack = drained_stack(local_start)
|
|
750
|
+
|
|
751
|
+
wasm_function.locals_type.each_with_index do |typ, i|
|
|
752
|
+
case typ
|
|
753
|
+
when :i32, :u32
|
|
754
|
+
# locals.push Local::I32(typ, 0)...
|
|
755
|
+
locals.push 0
|
|
756
|
+
else
|
|
757
|
+
$stderr.puts "warning: unknown type #{typ.inspect}. default to Object"
|
|
758
|
+
locals.push Object.new
|
|
759
|
+
end
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
arity = wasm_function.retsig.size
|
|
763
|
+
frame = Frame.new(-1, stack.size, wasm_function.body, arity, locals)
|
|
764
|
+
self.call_stack.push(frame)
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# @rbs wasm_function: WasmFunction
|
|
768
|
+
# @rbs return: Object|nil
|
|
769
|
+
def invoke_internal(wasm_function)
|
|
770
|
+
arity = wasm_function.retsig.size
|
|
771
|
+
push_frame(wasm_function)
|
|
772
|
+
execute!
|
|
773
|
+
|
|
774
|
+
if arity > 0
|
|
775
|
+
if arity > 1
|
|
776
|
+
raise ::NotImplementedError, "return artiy >= 2 not yet supported ;;"
|
|
777
|
+
end
|
|
778
|
+
if self.stack.empty?
|
|
779
|
+
raise "[BUG] stack empry"
|
|
780
|
+
end
|
|
781
|
+
v = self.stack.pop
|
|
782
|
+
return v
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
return nil
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
# @rbs external_function: ExternalFunction
|
|
789
|
+
# @rbs return: Object|nil
|
|
790
|
+
def invoke_external(external_function)
|
|
791
|
+
local_start = stack.size - external_function.callsig.size
|
|
792
|
+
args = stack[local_start..]
|
|
793
|
+
if !args
|
|
794
|
+
raise LoadError, "stack too short"
|
|
795
|
+
end
|
|
796
|
+
self.stack = drained_stack(local_start)
|
|
797
|
+
|
|
798
|
+
proc = external_function.callable
|
|
799
|
+
proc[self.instance.store, args]
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
# @rbs return: void
|
|
803
|
+
def execute!
|
|
804
|
+
loop do
|
|
805
|
+
cur_frame = self.call_stack.last #: Frame
|
|
806
|
+
if !cur_frame
|
|
807
|
+
break
|
|
808
|
+
end
|
|
809
|
+
cur_frame.pc += 1
|
|
810
|
+
insn = cur_frame.body[cur_frame.pc]
|
|
811
|
+
if !insn
|
|
812
|
+
break
|
|
813
|
+
end
|
|
814
|
+
eval_insn(cur_frame, insn)
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
# @rbs frame: Frame
|
|
819
|
+
# @rbs insn: Op
|
|
820
|
+
# @rbs return: void
|
|
821
|
+
def eval_insn(frame, insn)
|
|
822
|
+
case insn.code
|
|
823
|
+
when :local_get
|
|
824
|
+
idx = insn.operand[0]
|
|
825
|
+
if !idx.is_a?(Integer)
|
|
826
|
+
raise EvalError, "[BUG] invalid type of operand"
|
|
827
|
+
end
|
|
828
|
+
local = frame.locals[idx]
|
|
829
|
+
if !local
|
|
830
|
+
raise EvalError, "local not found"
|
|
831
|
+
end
|
|
832
|
+
stack.push(local)
|
|
833
|
+
when :local_set
|
|
834
|
+
idx = insn.operand[0]
|
|
835
|
+
if !idx.is_a?(Integer)
|
|
836
|
+
raise EvalError, "[BUG] invalid type of operand"
|
|
837
|
+
end
|
|
838
|
+
value = stack.pop
|
|
839
|
+
if !value
|
|
840
|
+
raise EvalError, "value should be pushed"
|
|
841
|
+
end
|
|
842
|
+
frame.locals[idx] = value
|
|
843
|
+
|
|
844
|
+
when :i32_add
|
|
845
|
+
right, left = stack.pop, stack.pop
|
|
846
|
+
if !right.is_a?(Integer) || !left.is_a?(Integer)
|
|
847
|
+
raise EvalError, "maybe empty stack"
|
|
848
|
+
end
|
|
849
|
+
stack.push(left + right)
|
|
850
|
+
when :i32_const
|
|
851
|
+
const = insn.operand[0]
|
|
852
|
+
if !const.is_a?(Integer)
|
|
853
|
+
raise EvalError, "[BUG] invalid type of operand"
|
|
854
|
+
end
|
|
855
|
+
stack.push(const)
|
|
856
|
+
when :i32_store
|
|
857
|
+
_align = insn.operand[0] # TODO: alignment support?
|
|
858
|
+
offset = insn.operand[1]
|
|
859
|
+
raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
|
860
|
+
|
|
861
|
+
value = stack.pop
|
|
862
|
+
addr = stack.pop
|
|
863
|
+
if !value.is_a?(Integer) || !addr.is_a?(Integer)
|
|
864
|
+
raise EvalError, "maybe stack too short"
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
at = addr + offset
|
|
868
|
+
data_end = at + 4 # sizeof(i32)
|
|
869
|
+
memory = self.instance.store.memories[0] || raise("[BUG] no memory")
|
|
870
|
+
memory.data[at...data_end] = [value].pack("I")
|
|
871
|
+
|
|
872
|
+
when :call
|
|
873
|
+
idx = insn.operand[0]
|
|
874
|
+
raise EvalError, "[BUG] local operand not found" if !idx.is_a?(Integer)
|
|
875
|
+
fn = self.instance.store.funcs[idx]
|
|
876
|
+
case fn
|
|
877
|
+
when WasmFunction
|
|
878
|
+
push_frame(fn)
|
|
879
|
+
when ExternalFunction
|
|
880
|
+
ret = invoke_external(fn)
|
|
881
|
+
self.stack.push ret if ret
|
|
882
|
+
else
|
|
883
|
+
raise GenericError, "got a non-function pointer"
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
when :end
|
|
887
|
+
old_frame = call_stack.pop
|
|
888
|
+
if !old_frame
|
|
889
|
+
raise EvalError, "maybe empty call stack"
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
# unwind the stacks
|
|
893
|
+
if old_frame.arity > 0
|
|
894
|
+
if old_frame.arity > 1
|
|
895
|
+
raise ::NotImplementedError, "return artiy >= 2 not yet supported ;;"
|
|
896
|
+
end
|
|
897
|
+
value = stack.pop
|
|
898
|
+
if !value
|
|
899
|
+
raise EvalError, "cannot obtain return value"
|
|
900
|
+
end
|
|
901
|
+
self.stack = drained_stack(old_frame.sp)
|
|
902
|
+
stack.push value
|
|
903
|
+
else
|
|
904
|
+
self.stack = drained_stack(old_frame.sp)
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
# @rbs finish: Integer
|
|
910
|
+
# @rbs return: Array[Object]
|
|
911
|
+
def drained_stack(finish)
|
|
912
|
+
drained = stack[0...finish]
|
|
913
|
+
if ! drained
|
|
914
|
+
$stderr.puts "warning: state of stack: #{stack.inspect}"
|
|
915
|
+
raise EvalError, "stack too short"
|
|
916
|
+
end
|
|
917
|
+
return drained
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
# @rbs name: Symbol
|
|
921
|
+
# @rbs args: Array[Object]
|
|
922
|
+
# @rbs return: untyped
|
|
923
|
+
def method_missing(name, *args)
|
|
924
|
+
if callable? name
|
|
925
|
+
call(name, args)
|
|
926
|
+
else
|
|
927
|
+
super
|
|
928
|
+
end
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
# @rbs name: String|Symbol
|
|
932
|
+
# @rbs return: bool
|
|
933
|
+
def respond_to? name
|
|
934
|
+
callable?(name) || super
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
class Frame
|
|
939
|
+
attr_accessor :pc #: Integer
|
|
940
|
+
attr_accessor :sp #: Integer
|
|
941
|
+
|
|
942
|
+
attr_accessor :body #: Array[Op]
|
|
943
|
+
|
|
944
|
+
attr_accessor :arity #: Integer
|
|
945
|
+
|
|
946
|
+
attr_accessor :locals #: Array[Object]
|
|
947
|
+
|
|
948
|
+
# @rbs pc: Integer
|
|
949
|
+
# @rbs sp: Integer
|
|
950
|
+
# @rbs body: Array[Op]
|
|
951
|
+
# @rbs arity: Integer
|
|
952
|
+
# @rbs locals: Array[Object]
|
|
953
|
+
# @rbs returb: void
|
|
954
|
+
def initialize(pc, sp, body, arity, locals)
|
|
955
|
+
@pc = pc
|
|
956
|
+
@sp = sp
|
|
957
|
+
@body = body
|
|
958
|
+
@arity = arity
|
|
959
|
+
@locals = locals
|
|
960
|
+
end
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
class Store
|
|
964
|
+
attr_accessor :funcs #: Array[WasmFunction|ExternalFunction]
|
|
965
|
+
|
|
966
|
+
# FIXME: attr_accessor :modules
|
|
967
|
+
|
|
968
|
+
attr_accessor :memories #: Array[Memory]
|
|
969
|
+
|
|
970
|
+
# @rbs inst: Instance
|
|
971
|
+
# @rbs return: void
|
|
972
|
+
def initialize(inst)
|
|
973
|
+
type_section = inst.type_section
|
|
974
|
+
func_section = inst.function_section
|
|
975
|
+
code_section = inst.code_section
|
|
976
|
+
|
|
977
|
+
import_section = inst.import_section
|
|
978
|
+
@funcs = []
|
|
979
|
+
|
|
980
|
+
if type_section && func_section && code_section
|
|
981
|
+
import_section.imports.each do |desc|
|
|
982
|
+
callsig = type_section.defined_types[desc.sig_index]
|
|
983
|
+
retsig = type_section.defined_results[desc.sig_index]
|
|
984
|
+
imported_module = inst.import_object[desc.module_name.to_sym]
|
|
985
|
+
if !imported_module
|
|
986
|
+
raise ::NameError, "module #{desc.module_name} not found"
|
|
987
|
+
end
|
|
988
|
+
imported_proc = imported_module[desc.name.to_sym]
|
|
989
|
+
if !imported_proc
|
|
990
|
+
raise ::NameError, "function #{desc.module_name}.#{desc.name} not found"
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
ext_function = ExternalFunction.new(callsig, retsig, imported_proc)
|
|
994
|
+
self.funcs << ext_function
|
|
995
|
+
end
|
|
996
|
+
|
|
997
|
+
func_section.func_indices.each_with_index do |sigindex, findex|
|
|
998
|
+
callsig = type_section.defined_types[sigindex]
|
|
999
|
+
retsig = type_section.defined_results[sigindex]
|
|
1000
|
+
codes = code_section.func_codes[findex]
|
|
1001
|
+
wasm_function = WasmFunction.new(callsig, retsig, codes)
|
|
1002
|
+
self.funcs << wasm_function
|
|
1003
|
+
end
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
@memories = []
|
|
1007
|
+
memory_section = inst.memory_section
|
|
1008
|
+
if memory_section
|
|
1009
|
+
memory_section.limits.each do |(min, max)|
|
|
1010
|
+
self.memories << Memory.new(min, max)
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
data_section = inst.data_section
|
|
1014
|
+
if data_section
|
|
1015
|
+
data_section.segments.each do |segment|
|
|
1016
|
+
memory = self.memories[segment.flags]
|
|
1017
|
+
if !memory
|
|
1018
|
+
raise GenericError, "invalid memory index: #{segment.flags}"
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
data_start = segment.offset
|
|
1022
|
+
data_end = segment.offset + segment.data.size
|
|
1023
|
+
if data_end > memory.data.size
|
|
1024
|
+
raise GenericError, "data too large for memory"
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
memory.data[data_start...data_end] = segment.data
|
|
1028
|
+
end
|
|
1029
|
+
end
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
# @rbs idx: Integer
|
|
1034
|
+
def [](idx)
|
|
1035
|
+
@funcs[idx]
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
class Memory
|
|
1040
|
+
attr_accessor :data #: String
|
|
1041
|
+
|
|
1042
|
+
attr_accessor :max #: Integer|nil
|
|
1043
|
+
|
|
1044
|
+
# @rbs min: Integer
|
|
1045
|
+
# @rbs max: Integer|nil
|
|
1046
|
+
# @rbs return: void
|
|
1047
|
+
def initialize(min, max)
|
|
1048
|
+
@data = String.new("\0" * (min * 64 * 1024), capacity: min * 64 * 1024)
|
|
1049
|
+
@max = max
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
def inspect
|
|
1053
|
+
"#<Wardite::Memory initial=#{@data.size.inspect} max=#{@max.inspect} @data=#{@data[0...64].inspect}...>"
|
|
1054
|
+
end
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
class WasmData
|
|
1058
|
+
attr_accessor :memory_index #: Integer
|
|
1059
|
+
|
|
1060
|
+
attr_accessor :offset #: Integer
|
|
1061
|
+
|
|
1062
|
+
attr_accessor :init #: String
|
|
1063
|
+
|
|
1064
|
+
# @rbs &blk: (WasmData) -> void
|
|
1065
|
+
# @rbs return: void
|
|
1066
|
+
def initialize(&blk)
|
|
1067
|
+
blk.call(self)
|
|
1068
|
+
end
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
class Exports
|
|
1072
|
+
attr_accessor :mappings #: Hash[String, [Integer, WasmFunction|ExternalFunction]]
|
|
1073
|
+
|
|
1074
|
+
# @rbs export_section: ExportSection
|
|
1075
|
+
# @rbs store: Store
|
|
1076
|
+
# @rbs return: void
|
|
1077
|
+
def initialize(export_section, store)
|
|
1078
|
+
@mappings = {}
|
|
1079
|
+
export_section.exports.each_pair do |name, desc|
|
|
1080
|
+
# TODO: introduce map by kind
|
|
1081
|
+
@mappings[name] = [desc.kind, store.funcs[desc.func_index]]
|
|
1082
|
+
end
|
|
1083
|
+
end
|
|
1084
|
+
|
|
1085
|
+
# @rbs name: String
|
|
1086
|
+
# @rbs return: [Integer, WasmFunction|ExternalFunction]
|
|
1087
|
+
def [](name)
|
|
1088
|
+
@mappings[name]
|
|
1089
|
+
end
|
|
1090
|
+
end
|
|
1091
|
+
|
|
1092
|
+
# TODO: common interface btw. WasmFunction and ExternalFunction?
|
|
1093
|
+
class WasmFunction
|
|
1094
|
+
attr_accessor :callsig #: Array[Symbol]
|
|
1095
|
+
|
|
1096
|
+
attr_accessor :retsig #: Array[Symbol]
|
|
1097
|
+
|
|
1098
|
+
attr_accessor :code_body #: CodeSection::CodeBody
|
|
1099
|
+
|
|
1100
|
+
# @rbs callsig: Array[Symbol]
|
|
1101
|
+
# @rbs retsig: Array[Symbol]
|
|
1102
|
+
# @rbs code_body: CodeSection::CodeBody
|
|
1103
|
+
# @rbs return: void
|
|
1104
|
+
def initialize(callsig, retsig, code_body)
|
|
1105
|
+
@callsig = callsig
|
|
1106
|
+
@retsig = retsig
|
|
1107
|
+
|
|
1108
|
+
@code_body = code_body
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
# @rbs return: Array[Op]
|
|
1112
|
+
def body
|
|
1113
|
+
code_body.body
|
|
1114
|
+
end
|
|
1115
|
+
|
|
1116
|
+
# @rbs return: Array[Symbol]
|
|
1117
|
+
def locals_type
|
|
1118
|
+
code_body.locals_type
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
# @rbs return: Array[Integer]
|
|
1122
|
+
def locals_count
|
|
1123
|
+
code_body.locals_count
|
|
1124
|
+
end
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
class ExternalFunction
|
|
1128
|
+
attr_accessor :callsig #: Array[Symbol]
|
|
1129
|
+
|
|
1130
|
+
attr_accessor :retsig #: Array[Symbol]
|
|
1131
|
+
|
|
1132
|
+
attr_accessor :callable #: Proc
|
|
1133
|
+
|
|
1134
|
+
# @rbs callsig: Array[Symbol]
|
|
1135
|
+
# @rbs retsig: Array[Symbol]
|
|
1136
|
+
# @rbs callable: Proc
|
|
1137
|
+
# @rbs return: void
|
|
1138
|
+
def initialize(callsig, retsig, callable)
|
|
1139
|
+
@callsig = callsig
|
|
1140
|
+
@retsig = retsig
|
|
1141
|
+
@callable = callable
|
|
1142
|
+
end
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
class GenericError < StandardError; end
|
|
1146
|
+
class LoadError < StandardError; end
|
|
1147
|
+
class ArgumentError < StandardError; end
|
|
1148
|
+
class EvalError < StandardError; end
|
|
1149
|
+
end
|