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.
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