wardite 0.2.0 → 0.2.2

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