wardite 0.2.1 → 0.3.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 +4 -4
- data/examples/break.wat +13 -0
- data/examples/call_indirect.wat +15 -0
- data/examples/global.wat +13 -0
- data/examples/loop.wat +20 -0
- data/examples/start.wat +12 -0
- data/lib/wardite/instruction.rb +12 -6
- data/lib/wardite/load.rb +800 -0
- data/lib/wardite/value.rb +45 -42
- data/lib/wardite/version.rb +1 -1
- data/lib/wardite/wasi.rb +2 -2
- data/lib/wardite.rb +465 -605
- data/sig/generated/wardite/instruction.rbs +8 -5
- data/sig/generated/wardite/load.rbs +236 -0
- data/sig/generated/wardite/value.rbs +82 -80
- data/sig/generated/wardite/wasi.rbs +4 -4
- data/sig/generated/wardite.rbs +120 -181
- data/sig/wardite.rbs +1 -3
- metadata +9 -2
data/lib/wardite/load.rb
ADDED
@@ -0,0 +1,800 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
module Wardite
|
3
|
+
class Section
|
4
|
+
attr_accessor :name #: String
|
5
|
+
|
6
|
+
attr_accessor :code #: Integer
|
7
|
+
|
8
|
+
attr_accessor :size #: Integer
|
9
|
+
end
|
10
|
+
|
11
|
+
class TypeSection < Section
|
12
|
+
attr_accessor :defined_types #: Array[Array[Symbol]]
|
13
|
+
|
14
|
+
attr_accessor :defined_results #: Array[Array[Symbol]]
|
15
|
+
|
16
|
+
# @rbs return: void
|
17
|
+
def initialize
|
18
|
+
self.name = "Type"
|
19
|
+
self.code = 0x1
|
20
|
+
|
21
|
+
@defined_types = []
|
22
|
+
@defined_results = []
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class FunctionSection < Section
|
27
|
+
attr_accessor :func_indices #: Array[Integer]
|
28
|
+
|
29
|
+
# @rbs return: void
|
30
|
+
def initialize
|
31
|
+
self.name = "Function"
|
32
|
+
self.code = 0x3
|
33
|
+
|
34
|
+
@func_indices = []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class TableSection < Section
|
39
|
+
attr_accessor :table_types #: Array[Symbol]
|
40
|
+
|
41
|
+
attr_accessor :table_limits #: Array[[Integer, Integer?]]
|
42
|
+
|
43
|
+
# @rbs return: void
|
44
|
+
def initialize
|
45
|
+
self.name = "Table"
|
46
|
+
self.code = 0x4
|
47
|
+
|
48
|
+
@table_types = []
|
49
|
+
@table_limits = []
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class MemorySection < Section
|
54
|
+
attr_accessor :limits #: Array[[Integer, Integer?]]
|
55
|
+
|
56
|
+
# @rbs return: void
|
57
|
+
def initialize
|
58
|
+
self.name = "Memory"
|
59
|
+
self.code = 0x5
|
60
|
+
|
61
|
+
@limits = []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class GlobalSection < Section
|
66
|
+
class Global
|
67
|
+
attr_accessor :type #: Symbol
|
68
|
+
|
69
|
+
attr_accessor :mutable #: bool
|
70
|
+
|
71
|
+
# TODO: unused in wasm 1.0 spec?
|
72
|
+
attr_accessor :shared #: bool
|
73
|
+
|
74
|
+
attr_accessor :value #: wasmValue
|
75
|
+
|
76
|
+
# @rbs &blk: (Global) -> void
|
77
|
+
# @rbs return: void
|
78
|
+
def initialize(&blk)
|
79
|
+
blk.call(self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
attr_accessor :globals #: Array[Global]
|
84
|
+
|
85
|
+
# @rbs return: void
|
86
|
+
def initialize
|
87
|
+
self.name = "Data"
|
88
|
+
self.code = 0x6
|
89
|
+
|
90
|
+
@globals = []
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class StartSection < Section
|
95
|
+
attr_accessor :func_index #: Integer
|
96
|
+
|
97
|
+
# @rbs return: void
|
98
|
+
def initialize
|
99
|
+
self.name = "Start"
|
100
|
+
self.code = 0x8
|
101
|
+
self.func_index = -1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class ElemSection < Section
|
106
|
+
attr_accessor :table_indices #: Array[Integer]
|
107
|
+
|
108
|
+
attr_accessor :table_offsets #: Array[Integer]
|
109
|
+
|
110
|
+
attr_accessor :element_indices #: Array[Array[Integer]]
|
111
|
+
|
112
|
+
# @rbs return: void
|
113
|
+
def initialize
|
114
|
+
self.name = "Elem"
|
115
|
+
self.code = 0x9
|
116
|
+
|
117
|
+
@table_indices = []
|
118
|
+
@table_offsets = []
|
119
|
+
@element_indices = []
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class CodeSection < Section
|
124
|
+
class CodeBody
|
125
|
+
attr_accessor :locals_count #: Array[Integer]
|
126
|
+
|
127
|
+
attr_accessor :locals_type #: Array[Symbol]
|
128
|
+
|
129
|
+
attr_accessor :body #: Array[Op]
|
130
|
+
|
131
|
+
# @rbs &blk: (CodeBody) -> void
|
132
|
+
# @rbs return: void
|
133
|
+
def initialize(&blk)
|
134
|
+
blk.call(self)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
attr_accessor :func_codes #:Array[CodeBody]
|
139
|
+
|
140
|
+
# @rbs return: void
|
141
|
+
def initialize
|
142
|
+
self.name = "Code"
|
143
|
+
self.code = 0xa
|
144
|
+
|
145
|
+
@func_codes = []
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class DataSection < Section
|
150
|
+
class Segment
|
151
|
+
attr_accessor :flags #: Integer
|
152
|
+
|
153
|
+
attr_accessor :offset #: Integer
|
154
|
+
|
155
|
+
attr_accessor :data #: String
|
156
|
+
|
157
|
+
# @rbs &blk: (Segment) -> void
|
158
|
+
# @rbs return: void
|
159
|
+
def initialize(&blk)
|
160
|
+
blk.call(self)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
attr_accessor :segments #: Array[Segment]
|
165
|
+
|
166
|
+
# @rbs return: void
|
167
|
+
def initialize
|
168
|
+
self.name = "Data"
|
169
|
+
self.code = 0xb
|
170
|
+
|
171
|
+
@segments = []
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class ExportSection < Section
|
176
|
+
class ExportDesc
|
177
|
+
attr_accessor :name #: String
|
178
|
+
|
179
|
+
attr_accessor :kind #: Integer
|
180
|
+
|
181
|
+
attr_accessor :func_index #: Integer
|
182
|
+
end
|
183
|
+
|
184
|
+
attr_accessor :exports #: Hash[String, ExportDesc]
|
185
|
+
|
186
|
+
def initialize #: void
|
187
|
+
self.name = "Export"
|
188
|
+
self.code = 0x7
|
189
|
+
|
190
|
+
@exports = {}
|
191
|
+
end
|
192
|
+
|
193
|
+
# @rbs &blk: (ExportDesc) -> void
|
194
|
+
def add_desc(&blk)
|
195
|
+
desc = ExportDesc.new
|
196
|
+
blk.call(desc)
|
197
|
+
self.exports[desc.name] = desc
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class ImportSection < Section
|
202
|
+
class ImportDesc
|
203
|
+
attr_accessor :module_name #: String
|
204
|
+
|
205
|
+
attr_accessor :name #: String
|
206
|
+
|
207
|
+
attr_accessor :kind #: Integer
|
208
|
+
|
209
|
+
attr_accessor :sig_index #: Integer
|
210
|
+
end
|
211
|
+
|
212
|
+
attr_accessor :imports #: Array[ImportDesc]
|
213
|
+
|
214
|
+
def initialize #: void
|
215
|
+
self.name = "Import"
|
216
|
+
self.code = 0x2
|
217
|
+
|
218
|
+
@imports = []
|
219
|
+
end
|
220
|
+
|
221
|
+
# @rbs &blk: (ImportDesc) -> void
|
222
|
+
def add_desc(&blk)
|
223
|
+
desc = ImportDesc.new
|
224
|
+
blk.call(desc) if blk
|
225
|
+
self.imports << desc
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
module BinaryLoader
|
230
|
+
extend Wardite::Leb128Helper
|
231
|
+
extend Wardite::ValueHelper
|
232
|
+
|
233
|
+
# @rbs self.@buf: File|StringIO
|
234
|
+
|
235
|
+
# @rbs buf: File|StringIO
|
236
|
+
# @rbs import_object: Hash[Symbol, Hash[Symbol, wasmCallable]]
|
237
|
+
# @rbs enable_wasi: boolish
|
238
|
+
# @rbs return: Instance
|
239
|
+
def self.load_from_buffer(buf, import_object: {}, enable_wasi: true)
|
240
|
+
@buf = buf
|
241
|
+
|
242
|
+
version = preamble
|
243
|
+
sections_ = sections
|
244
|
+
|
245
|
+
if enable_wasi
|
246
|
+
wasi_env = Wardite::WasiSnapshotPreview1.new
|
247
|
+
import_object[:wasi_snapshot_preview1] = wasi_env.to_module
|
248
|
+
end
|
249
|
+
|
250
|
+
return Instance.new(import_object) do |i|
|
251
|
+
i.version = version
|
252
|
+
i.sections = sections_
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# @rbs return: Integer
|
257
|
+
def self.preamble
|
258
|
+
asm = @buf.read 4
|
259
|
+
if !asm
|
260
|
+
raise LoadError, "buffer too short"
|
261
|
+
end
|
262
|
+
if asm != "\u0000asm"
|
263
|
+
raise LoadError, "invalid preamble"
|
264
|
+
end
|
265
|
+
|
266
|
+
vstr = @buf.read(4)
|
267
|
+
if !vstr
|
268
|
+
raise LoadError, "buffer too short"
|
269
|
+
end
|
270
|
+
version = vstr.to_enum(:chars)
|
271
|
+
.with_index
|
272
|
+
.inject(0) {|dest, (c, i)| dest | (c.ord << i*8) }
|
273
|
+
if version != 1
|
274
|
+
raise LoadError, "unsupported version: #{version}"
|
275
|
+
end
|
276
|
+
version
|
277
|
+
end
|
278
|
+
|
279
|
+
# @rbs return: Array[Section]
|
280
|
+
def self.sections
|
281
|
+
sections = [] #: Array[Section]
|
282
|
+
|
283
|
+
loop do
|
284
|
+
byte = @buf.read(1)
|
285
|
+
if !byte
|
286
|
+
break
|
287
|
+
end
|
288
|
+
code = byte.ord
|
289
|
+
|
290
|
+
section = case code
|
291
|
+
when Wardite::SectionType
|
292
|
+
type_section
|
293
|
+
when Wardite::SectionImport
|
294
|
+
import_section
|
295
|
+
when Wardite::SectionFunction
|
296
|
+
function_section
|
297
|
+
when Wardite::SectionTable
|
298
|
+
table_section
|
299
|
+
when Wardite::SectionMemory
|
300
|
+
memory_section
|
301
|
+
when Wardite::SectionGlobal
|
302
|
+
global_section
|
303
|
+
when Wardite::SectionExport
|
304
|
+
export_section
|
305
|
+
when Wardite::SectionStart
|
306
|
+
start_section
|
307
|
+
when Wardite::SectionElement
|
308
|
+
elem_section
|
309
|
+
when Wardite::SectionCode
|
310
|
+
code_section
|
311
|
+
when Wardite::SectionData
|
312
|
+
data_section
|
313
|
+
when Wardite::SectionCustom
|
314
|
+
unimplemented_skip_section(code)
|
315
|
+
else
|
316
|
+
raise LoadError, "unknown code: #{code}(\"#{code.to_s 16}\")"
|
317
|
+
end
|
318
|
+
|
319
|
+
if section
|
320
|
+
sections << section
|
321
|
+
end
|
322
|
+
end
|
323
|
+
sections
|
324
|
+
end
|
325
|
+
|
326
|
+
# @rbs return: TypeSection
|
327
|
+
def self.type_section
|
328
|
+
dest = TypeSection.new
|
329
|
+
|
330
|
+
size = fetch_uleb128(@buf)
|
331
|
+
dest.size = size
|
332
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
333
|
+
|
334
|
+
len = fetch_uleb128(sbuf)
|
335
|
+
len.times do |i|
|
336
|
+
fncode = assert_read(sbuf, 1)
|
337
|
+
if fncode != "\x60"
|
338
|
+
raise LoadError, "not a function definition"
|
339
|
+
end
|
340
|
+
|
341
|
+
arglen = fetch_uleb128(sbuf)
|
342
|
+
arg = []
|
343
|
+
arglen.times do
|
344
|
+
case ty = assert_read(sbuf, 1)&.ord
|
345
|
+
when 0x7f
|
346
|
+
arg << :i32
|
347
|
+
when 0x7e
|
348
|
+
arg << :i64
|
349
|
+
when 0x7d
|
350
|
+
arg << :f32
|
351
|
+
when 0x7c
|
352
|
+
arg << :f64
|
353
|
+
else
|
354
|
+
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
355
|
+
end
|
356
|
+
end
|
357
|
+
dest.defined_types << arg
|
358
|
+
|
359
|
+
retlen = fetch_uleb128(sbuf)
|
360
|
+
ret = []
|
361
|
+
retlen.times do
|
362
|
+
case ty = assert_read(sbuf, 1)&.ord
|
363
|
+
when 0x7f
|
364
|
+
ret << :i32
|
365
|
+
when 0x7e
|
366
|
+
ret << :i64
|
367
|
+
when 0x7d
|
368
|
+
ret << :f32
|
369
|
+
when 0x7c
|
370
|
+
ret << :f64
|
371
|
+
else
|
372
|
+
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
373
|
+
end
|
374
|
+
end
|
375
|
+
dest.defined_results << ret
|
376
|
+
end
|
377
|
+
|
378
|
+
dest
|
379
|
+
end
|
380
|
+
|
381
|
+
# @rbs return: ImportSection
|
382
|
+
def self.import_section
|
383
|
+
dest = ImportSection.new
|
384
|
+
size = fetch_uleb128(@buf)
|
385
|
+
dest.size = size
|
386
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
387
|
+
|
388
|
+
len = fetch_uleb128(sbuf)
|
389
|
+
len.times do |i|
|
390
|
+
mlen = fetch_uleb128(sbuf)
|
391
|
+
module_name = assert_read(sbuf, mlen)
|
392
|
+
nlen = fetch_uleb128(sbuf)
|
393
|
+
name = assert_read(sbuf, nlen)
|
394
|
+
kind_ = assert_read(sbuf, 1)
|
395
|
+
kind = kind_[0]&.ord
|
396
|
+
if !kind
|
397
|
+
raise "[BUG] empty unpacked string" # guard rbs
|
398
|
+
end
|
399
|
+
|
400
|
+
index = fetch_uleb128(sbuf)
|
401
|
+
dest.add_desc do |desc|
|
402
|
+
desc.module_name = module_name
|
403
|
+
desc.name = name
|
404
|
+
desc.kind = kind
|
405
|
+
desc.sig_index = index
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
dest
|
410
|
+
end
|
411
|
+
|
412
|
+
# @rbs return: MemorySection
|
413
|
+
def self.memory_section
|
414
|
+
dest = MemorySection.new
|
415
|
+
size = fetch_uleb128(@buf)
|
416
|
+
dest.size = size
|
417
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
418
|
+
|
419
|
+
len = fetch_uleb128(sbuf)
|
420
|
+
if len != 1
|
421
|
+
raise LoadError, "memory section has invalid size: #{len}"
|
422
|
+
end
|
423
|
+
len.times do |i|
|
424
|
+
flags = fetch_uleb128(sbuf)
|
425
|
+
min = fetch_uleb128(sbuf)
|
426
|
+
|
427
|
+
max = nil
|
428
|
+
if flags != 0
|
429
|
+
max = fetch_uleb128(sbuf)
|
430
|
+
end
|
431
|
+
dest.limits << [min, max]
|
432
|
+
end
|
433
|
+
dest
|
434
|
+
end
|
435
|
+
|
436
|
+
# @rbs return: StartSection
|
437
|
+
def self.start_section
|
438
|
+
dest = StartSection.new
|
439
|
+
size = fetch_uleb128(@buf)
|
440
|
+
dest.size = size
|
441
|
+
# StartSection won't use size
|
442
|
+
func_index = fetch_uleb128(@buf)
|
443
|
+
dest.func_index = func_index
|
444
|
+
dest
|
445
|
+
end
|
446
|
+
|
447
|
+
# @rbs return: ElemSection
|
448
|
+
def self.elem_section
|
449
|
+
dest = ElemSection.new
|
450
|
+
size = fetch_uleb128(@buf)
|
451
|
+
dest.size = size
|
452
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
453
|
+
|
454
|
+
len = fetch_uleb128(sbuf)
|
455
|
+
len.times do |i|
|
456
|
+
etype = fetch_uleb128(sbuf)
|
457
|
+
case etype
|
458
|
+
when 0x0 # expr, vec(funcidx)
|
459
|
+
dest.table_indices << 0 # default and fixed to table[0]
|
460
|
+
|
461
|
+
code = fetch_insn_while_end(sbuf)
|
462
|
+
ops = code_body(StringIO.new(code))
|
463
|
+
offset = decode_expr(ops)
|
464
|
+
dest.table_offsets << offset
|
465
|
+
|
466
|
+
elms = []
|
467
|
+
elen = fetch_uleb128(sbuf)
|
468
|
+
elen.times do |i|
|
469
|
+
index = fetch_uleb128(sbuf)
|
470
|
+
elms << index
|
471
|
+
end
|
472
|
+
dest.element_indices << elms
|
473
|
+
else
|
474
|
+
raise NotImplementedError, "element section type #{etype} is a TODO!"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
dest
|
478
|
+
end
|
479
|
+
|
480
|
+
# @rbs return: GlobalSection
|
481
|
+
def self.global_section
|
482
|
+
dest = GlobalSection.new
|
483
|
+
size = fetch_uleb128(@buf)
|
484
|
+
dest.size = size
|
485
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
486
|
+
|
487
|
+
len = fetch_uleb128(sbuf)
|
488
|
+
len.times do |i|
|
489
|
+
typeb = fetch_uleb128(sbuf)
|
490
|
+
gtype = Op.i2type(typeb)
|
491
|
+
mut = sbuf.read 1
|
492
|
+
if !mut
|
493
|
+
raise LoadError, "global section too short"
|
494
|
+
end
|
495
|
+
|
496
|
+
code = fetch_insn_while_end(sbuf)
|
497
|
+
ops = code_body(StringIO.new(code))
|
498
|
+
value = decode_global_expr(ops)
|
499
|
+
|
500
|
+
global = GlobalSection::Global.new do |g|
|
501
|
+
g.type = gtype
|
502
|
+
g.mutable = (mut.ord == 0x01)
|
503
|
+
g.shared = false # always
|
504
|
+
g.value = value
|
505
|
+
end
|
506
|
+
dest.globals << global
|
507
|
+
end
|
508
|
+
dest
|
509
|
+
end
|
510
|
+
|
511
|
+
# @rbs return: FunctionSection
|
512
|
+
def self.function_section
|
513
|
+
dest = FunctionSection.new
|
514
|
+
size = fetch_uleb128(@buf)
|
515
|
+
dest.size = size
|
516
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
517
|
+
|
518
|
+
len = fetch_uleb128(sbuf)
|
519
|
+
len.times do |i|
|
520
|
+
index = fetch_uleb128(sbuf)
|
521
|
+
dest.func_indices << index
|
522
|
+
end
|
523
|
+
dest
|
524
|
+
end
|
525
|
+
|
526
|
+
# @rbs return: TableSection
|
527
|
+
def self.table_section
|
528
|
+
dest = TableSection.new
|
529
|
+
size = fetch_uleb128(@buf)
|
530
|
+
dest.size = size
|
531
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
532
|
+
|
533
|
+
len = fetch_uleb128(sbuf)
|
534
|
+
len.times do |i|
|
535
|
+
code = fetch_uleb128(sbuf)
|
536
|
+
type = Op.i2type(code)
|
537
|
+
dest.table_types << type
|
538
|
+
|
539
|
+
flags = fetch_uleb128(sbuf)
|
540
|
+
min = fetch_uleb128(sbuf)
|
541
|
+
max = nil
|
542
|
+
if flags != 0
|
543
|
+
max = fetch_uleb128(sbuf)
|
544
|
+
end
|
545
|
+
dest.table_limits << [min, max]
|
546
|
+
end
|
547
|
+
dest
|
548
|
+
end
|
549
|
+
|
550
|
+
# @rbs return: CodeSection
|
551
|
+
def self.code_section
|
552
|
+
dest = CodeSection.new
|
553
|
+
size = fetch_uleb128(@buf)
|
554
|
+
dest.size = size
|
555
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
556
|
+
|
557
|
+
len = fetch_uleb128(sbuf)
|
558
|
+
len.times do |i|
|
559
|
+
ilen = fetch_uleb128(sbuf)
|
560
|
+
code = assert_read(sbuf, ilen)
|
561
|
+
last_code = code[-1]
|
562
|
+
if ! last_code
|
563
|
+
raise "[BUG] empty code fetched" # guard for steep check
|
564
|
+
end
|
565
|
+
if last_code.ord != 0x0b
|
566
|
+
$stderr.puts "warning: instruction not ended with inst end(0x0b): 0x0#{last_code.ord}"
|
567
|
+
end
|
568
|
+
cbuf = StringIO.new(code)
|
569
|
+
locals_count = []
|
570
|
+
locals_type = []
|
571
|
+
locals_len = fetch_uleb128(cbuf)
|
572
|
+
locals_len.times do
|
573
|
+
type_count = fetch_uleb128(cbuf)
|
574
|
+
locals_count << type_count
|
575
|
+
value_type = assert_read(cbuf, 1)&.ord
|
576
|
+
locals_type << Op.i2type(value_type || -1)
|
577
|
+
end
|
578
|
+
body = code_body(cbuf)
|
579
|
+
dest.func_codes << CodeSection::CodeBody.new do |b|
|
580
|
+
b.locals_count = locals_count
|
581
|
+
b.locals_type = locals_type
|
582
|
+
b.body = body
|
583
|
+
end
|
584
|
+
end
|
585
|
+
dest
|
586
|
+
end
|
587
|
+
|
588
|
+
# @rbs buf: StringIO
|
589
|
+
# @rbs return: Array[::Wardite::Op]
|
590
|
+
def self.code_body(buf)
|
591
|
+
dest = []
|
592
|
+
while c = buf.read(1)
|
593
|
+
namespace, code = Op.to_sym(c)
|
594
|
+
operand_types = Op.operand_of(code)
|
595
|
+
operand = [] #: Array[operandItem]
|
596
|
+
operand_types.each do |typ|
|
597
|
+
case typ
|
598
|
+
when :u8
|
599
|
+
ope = buf.read 1
|
600
|
+
if ! ope
|
601
|
+
raise LoadError, "buffer too short"
|
602
|
+
end
|
603
|
+
operand << ope.ord
|
604
|
+
when :u32
|
605
|
+
operand << fetch_uleb128(buf)
|
606
|
+
when :u32_vec
|
607
|
+
len = fetch_uleb128(buf)
|
608
|
+
vec = [] #: Array[Integer]
|
609
|
+
len.times do
|
610
|
+
vec << fetch_uleb128(buf)
|
611
|
+
end
|
612
|
+
operand << vec
|
613
|
+
when :i32
|
614
|
+
operand << fetch_sleb128(buf)
|
615
|
+
when :i64
|
616
|
+
operand << fetch_sleb128(buf)
|
617
|
+
when :f32
|
618
|
+
data = buf.read 4
|
619
|
+
if !data || data.size != 4
|
620
|
+
raise LoadError, "buffer too short"
|
621
|
+
end
|
622
|
+
v = data.unpack("e")[0]
|
623
|
+
raise "String#unpack is broken" if !v.is_a?(Float)
|
624
|
+
operand << v
|
625
|
+
when :f64
|
626
|
+
data = buf.read 8
|
627
|
+
if !data || data.size != 8
|
628
|
+
raise LoadError, "buffer too short"
|
629
|
+
end
|
630
|
+
v = data.unpack("E")[0]
|
631
|
+
raise "String#unpack is broken" if !v.is_a?(Float)
|
632
|
+
operand << v
|
633
|
+
when :u8_block
|
634
|
+
block_ope = buf.read 1
|
635
|
+
if ! block_ope
|
636
|
+
raise LoadError, "buffer too short for if"
|
637
|
+
end
|
638
|
+
if block_ope.ord == 0x40
|
639
|
+
operand << Block.void
|
640
|
+
else
|
641
|
+
operand << Block.new([block_ope.ord])
|
642
|
+
end
|
643
|
+
else
|
644
|
+
$stderr.puts "warning: unknown type #{typ.inspect}. defaulting to u32"
|
645
|
+
operand << fetch_uleb128(buf)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
dest << Op.new(namespace, code, operand)
|
650
|
+
end
|
651
|
+
|
652
|
+
dest
|
653
|
+
end
|
654
|
+
|
655
|
+
# @rbs return: DataSection
|
656
|
+
def self.data_section
|
657
|
+
dest = DataSection.new
|
658
|
+
size = fetch_uleb128(@buf)
|
659
|
+
dest.size = size
|
660
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
661
|
+
|
662
|
+
len = fetch_uleb128(sbuf)
|
663
|
+
len.times do |i|
|
664
|
+
mem_index = fetch_uleb128(sbuf)
|
665
|
+
code = fetch_insn_while_end(sbuf)
|
666
|
+
ops = code_body(StringIO.new(code))
|
667
|
+
offset = decode_expr(ops)
|
668
|
+
|
669
|
+
len = fetch_uleb128(sbuf)
|
670
|
+
data = sbuf.read len
|
671
|
+
if !data
|
672
|
+
raise LoadError, "buffer too short"
|
673
|
+
end
|
674
|
+
|
675
|
+
segment = DataSection::Segment.new do |seg|
|
676
|
+
seg.flags = mem_index
|
677
|
+
seg.offset = offset
|
678
|
+
seg.data = data
|
679
|
+
end
|
680
|
+
dest.segments << segment
|
681
|
+
end
|
682
|
+
dest
|
683
|
+
end
|
684
|
+
|
685
|
+
# @rbs sbuf: StringIO
|
686
|
+
# @rbs return: String
|
687
|
+
def self.fetch_insn_while_end(sbuf)
|
688
|
+
code = String.new("")
|
689
|
+
loop {
|
690
|
+
c = sbuf.read 1
|
691
|
+
if !c
|
692
|
+
break
|
693
|
+
end
|
694
|
+
code << c
|
695
|
+
if c == "\u000b" # :end
|
696
|
+
break
|
697
|
+
end
|
698
|
+
}
|
699
|
+
code
|
700
|
+
end
|
701
|
+
|
702
|
+
# @rbs ops: Array[Op]
|
703
|
+
# @rbs return: Integer
|
704
|
+
def self.decode_expr(ops)
|
705
|
+
# sees first opcode
|
706
|
+
op = ops.first
|
707
|
+
if !op
|
708
|
+
raise LoadError, "empty opcodes"
|
709
|
+
end
|
710
|
+
case op.code
|
711
|
+
when :i32_const
|
712
|
+
arg = op.operand[0]
|
713
|
+
if !arg.is_a?(Integer)
|
714
|
+
raise "Invalid definition of operand"
|
715
|
+
end
|
716
|
+
return arg
|
717
|
+
else
|
718
|
+
raise "Unimplemented offset op: #{op.code.inspect}"
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
# @rbs ops: Array[Op]
|
723
|
+
# @rbs return: wasmValue
|
724
|
+
def self.decode_global_expr(ops)
|
725
|
+
# sees first opcode
|
726
|
+
op = ops.first
|
727
|
+
if !op
|
728
|
+
raise LoadError, "empty opcodes"
|
729
|
+
end
|
730
|
+
case op.code
|
731
|
+
when :i32_const
|
732
|
+
arg = op.operand[0]
|
733
|
+
if !arg.is_a?(Integer)
|
734
|
+
raise "Invalid definition of operand"
|
735
|
+
end
|
736
|
+
return I32(arg)
|
737
|
+
when :i64_const
|
738
|
+
arg = op.operand[0]
|
739
|
+
if !arg.is_a?(Integer)
|
740
|
+
raise "Invalid definition of operand"
|
741
|
+
end
|
742
|
+
return I64(arg)
|
743
|
+
# TODO: floats
|
744
|
+
else
|
745
|
+
raise "Unimplemented offset op: #{op.code.inspect}"
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
# @rbs return: ExportSection
|
750
|
+
def self.export_section
|
751
|
+
dest = ExportSection.new
|
752
|
+
size = fetch_uleb128(@buf)
|
753
|
+
dest.size = size
|
754
|
+
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
755
|
+
|
756
|
+
len = fetch_uleb128(sbuf)
|
757
|
+
len.times do |i|
|
758
|
+
nlen = fetch_uleb128(sbuf)
|
759
|
+
name = assert_read(sbuf, nlen)
|
760
|
+
kind_ = assert_read(sbuf, 1)
|
761
|
+
kind = kind_[0]&.ord
|
762
|
+
if !kind
|
763
|
+
raise "[BUG] empty unpacked string" # guard rbs
|
764
|
+
end
|
765
|
+
|
766
|
+
index = fetch_uleb128(sbuf)
|
767
|
+
dest.add_desc do |desc|
|
768
|
+
desc.name = name
|
769
|
+
desc.kind = kind
|
770
|
+
desc.func_index = index
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
dest
|
775
|
+
end
|
776
|
+
|
777
|
+
# @rbs code: Integer
|
778
|
+
# @rbs return: nil
|
779
|
+
def self.unimplemented_skip_section(code)
|
780
|
+
$stderr.puts "warning: unimplemented section: 0x0#{code}"
|
781
|
+
size = @buf.read(1)&.ord
|
782
|
+
@buf.read(size)
|
783
|
+
nil
|
784
|
+
end
|
785
|
+
|
786
|
+
# @rbs sbuf: StringIO
|
787
|
+
# @rbs n: Integer
|
788
|
+
# @rbs return: String
|
789
|
+
def self.assert_read(sbuf, n)
|
790
|
+
ret = sbuf.read n
|
791
|
+
if !ret
|
792
|
+
raise LoadError, "too short section size"
|
793
|
+
end
|
794
|
+
if ret.size != n
|
795
|
+
raise LoadError, "too short section size"
|
796
|
+
end
|
797
|
+
ret
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|