wardite 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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