wardite 0.2.0 → 0.2.2

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