wardite 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wardite/wasi.rb CHANGED
@@ -14,7 +14,7 @@ module Wardite
14
14
  end
15
15
 
16
16
  # @rbs store: Store
17
- # @rbs args: Array[I32|I64|F32|F64]
17
+ # @rbs args: Array[wasmValue]
18
18
  # @rbs return: Object
19
19
  def fd_write(store, args)
20
20
  iargs = args.map do |elm|
@@ -45,7 +45,7 @@ module Wardite
45
45
  0
46
46
  end
47
47
 
48
- # @rbs return: Hash[Symbol, Proc]
48
+ # @rbs return: Hash[Symbol, wasmCallable]
49
49
  def to_module
50
50
  {
51
51
  fd_write: lambda{|store, args| self.fd_write(store, args) },
data/lib/wardite.rb CHANGED
@@ -31,13 +31,15 @@ module Wardite
31
31
 
32
32
  attr_accessor :runtime #: Runtime
33
33
 
34
+ attr_accessor :types #: Array[Type]
35
+
34
36
  attr_accessor :store #: Store
35
37
 
36
38
  attr_accessor :exports #: Exports
37
39
 
38
- attr_reader :import_object #: Hash[Symbol, Hash[Symbol, Proc]]
40
+ attr_reader :import_object #: Hash[Symbol, Hash[Symbol, wasmCallable]]
39
41
 
40
- # @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
42
+ # @rbs import_object: Hash[Symbol, Hash[Symbol, wasmCallable]]
41
43
  # @rbs &blk: (Instance) -> void
42
44
  def initialize(import_object, &blk)
43
45
  blk.call(self)
@@ -46,6 +48,15 @@ module Wardite
46
48
  @store = Store.new(self)
47
49
  @exports = Exports.new(self.export_section, store)
48
50
  @runtime = Runtime.new(self)
51
+
52
+ @types = []
53
+ type_section = self.type_section
54
+ if type_section
55
+ type_section.defined_types.each_with_index do |calltype, idx|
56
+ rettype = type_section.defined_results[idx]
57
+ @types << Type.new(calltype, rettype)
58
+ end
59
+ end
49
60
  end
50
61
 
51
62
  # @rbs return: ImportSection
@@ -82,6 +93,18 @@ module Wardite
82
93
  sec
83
94
  end
84
95
 
96
+ # @rbs return: StartSection|nil
97
+ def start_section
98
+ sec = @sections.find{|s| s.code == Const::SectionStart }
99
+ if !sec
100
+ return nil
101
+ end
102
+ if !sec.is_a?(StartSection)
103
+ raise(GenericError, "[BUG] found invalid start section")
104
+ end
105
+ sec
106
+ end
107
+
85
108
  # @rbs return: GlobalSection|nil
86
109
  def global_section
87
110
  sec = @sections.find{|s| s.code == Const::SectionGlobal }
@@ -118,6 +141,30 @@ module Wardite
118
141
  sec
119
142
  end
120
143
 
144
+ # @rbs return: TableSection?
145
+ def table_section
146
+ sec = @sections.find{|s| s.code == Const::SectionTable }
147
+ if !sec
148
+ return nil
149
+ end
150
+ if !sec.is_a?(TableSection)
151
+ raise(GenericError, "instance doesn't have required section")
152
+ end
153
+ sec
154
+ end
155
+
156
+ # @rbs return: ElemSection?
157
+ def elem_section
158
+ sec = @sections.find{|s| s.code == Const::SectionElement }
159
+ if !sec
160
+ return nil
161
+ end
162
+ if !sec.is_a?(ElemSection)
163
+ raise(GenericError, "instance doesn't have required section")
164
+ end
165
+ sec
166
+ end
167
+
121
168
  # @rbs return: CodeSection|nil
122
169
  def code_section
123
170
  sec = @sections.find{|s| s.code == Const::SectionCode }
@@ -147,7 +194,7 @@ module Wardite
147
194
  include ValueHelper
148
195
 
149
196
  # TODO: add types of class that the stack accomodates
150
- attr_accessor :stack #: Array[I32|I64|F32|F64]
197
+ attr_accessor :stack #: Array[wasmValue]
151
198
 
152
199
  attr_accessor :call_stack #: Array[Frame]
153
200
 
@@ -158,8 +205,18 @@ module Wardite
158
205
  @stack = []
159
206
  @call_stack = []
160
207
  @instance = inst
208
+
209
+ invoke_start_section
161
210
  end
162
211
 
212
+ # @rbs return: void
213
+ def invoke_start_section
214
+ start_section = instance.start_section
215
+ if start_section
216
+ call_by_index(start_section.func_index)
217
+ end
218
+ end
219
+
163
220
  # @rbs name: String|Symbol
164
221
  # @rbs return: bool
165
222
  def callable?(name)
@@ -200,6 +257,21 @@ module Wardite
200
257
  end
201
258
  end
202
259
 
260
+ # @rbs idx: Integer
261
+ # @rbs return: void
262
+ def call_by_index(idx)
263
+ fn = @instance.store.funcs[idx]
264
+
265
+ case fn
266
+ when WasmFunction
267
+ invoke_internal(fn)
268
+ when ExternalFunction
269
+ invoke_external(fn)
270
+ else
271
+ raise GenericError, "registered pointer is not to a function"
272
+ end
273
+ end
274
+
203
275
  # @rbs wasm_function: WasmFunction
204
276
  # @rbs return: void
205
277
  def push_frame(wasm_function)
@@ -249,7 +321,7 @@ module Wardite
249
321
  end
250
322
 
251
323
  # @rbs external_function: ExternalFunction
252
- # @rbs return: I32|I64|F32|F64|nil
324
+ # @rbs return: wasmValue|nil
253
325
  def invoke_external(external_function)
254
326
  local_start = stack.size - external_function.callsig.size
255
327
  args = stack[local_start..]
@@ -326,19 +398,80 @@ module Wardite
326
398
  when :nop
327
399
  return
328
400
 
401
+ when :br
402
+ level = insn.operand[0]
403
+ raise EvalError, "br op without level" if !level.is_a?(Integer)
404
+ pc = do_branch(frame.labels, stack, level)
405
+ frame.pc = pc
406
+
407
+ when :br_if
408
+ level = insn.operand[0]
409
+ raise EvalError, "br op without level" if !level.is_a?(Integer)
410
+ cond = stack.pop
411
+ raise EvalError, "cond not found" if !cond.is_a?(I32)
412
+ if cond.value.zero?
413
+ return
414
+ end
415
+ pc = do_branch(frame.labels, stack, level)
416
+ frame.pc = pc
417
+
418
+ when :br_table
419
+ level_vec = insn.operand[0]
420
+ raise EvalError, "no level vector" if !level_vec.is_a?(Array)
421
+ default = insn.operand[1]
422
+ raise EvalError, "no default specified" if !default.is_a?(Integer)
423
+ idx = stack.pop
424
+ raise EvalError, "idx not found" if !idx.is_a?(I32)
425
+ level = if idx.value_s < 0 || idx.value_s >= level_vec.size
426
+ default
427
+ else
428
+ level_vec[idx.value_s]
429
+ end
430
+ pc = do_branch(frame.labels, stack, level)
431
+ frame.pc = pc
432
+
433
+ when :block
434
+ block = insn.operand[0]
435
+ raise EvalError, "block op without block" if !block.is_a?(Block)
436
+ next_pc = fetch_ops_while_end(frame.body, frame.pc)
437
+ label = Label.new(:block, next_pc, stack.size, block.result_size)
438
+ frame.labels.push(label)
439
+
440
+ when :loop
441
+ block = insn.operand[0]
442
+ raise EvalError, "loop op without block" if !block.is_a?(Block)
443
+ start = frame.pc
444
+ end_pc = fetch_ops_while_end(frame.body, frame.pc)
445
+ label = Label.new(:loop, end_pc, stack.size, block.result_size, start)
446
+ frame.labels.push(label)
447
+
329
448
  when :if
330
449
  block = insn.operand[0]
331
450
  raise EvalError, "if op without block" if !block.is_a?(Block)
332
451
  cond = stack.pop
333
452
  raise EvalError, "cond not found" if !cond.is_a?(I32)
334
453
  next_pc = fetch_ops_while_end(frame.body, frame.pc)
454
+
335
455
  if cond.value.zero?
336
- frame.pc = next_pc
456
+ frame.pc = fetch_ops_while_else_or_end(frame.body, frame.pc)
457
+ end
458
+
459
+ if frame.pc == next_pc
460
+ # This means if block has no else instr.
461
+ return
337
462
  end
338
463
 
339
464
  label = Label.new(:if, next_pc, stack.size, block.result_size)
340
465
  frame.labels.push(label)
341
466
 
467
+ when :else
468
+ if old_label = frame.labels.pop
469
+ frame.pc = old_label.pc
470
+ stack_unwind(old_label.sp, old_label.arity)
471
+ else
472
+ raise EvalError, "else should be in if block"
473
+ end
474
+
342
475
  when :call
343
476
  idx = insn.operand[0]
344
477
  raise EvalError, "[BUG] local operand not found" if !idx.is_a?(Integer)
@@ -353,6 +486,45 @@ module Wardite
353
486
  raise GenericError, "got a non-function pointer"
354
487
  end
355
488
 
489
+ when :call_indirect
490
+ table = self.instance.store.tables[0]
491
+ raise EvalError, "table required but not found" if !table
492
+ type_idx = insn.operand[0]
493
+ raise EvalError, "[BUG] index operand invalid" if !type_idx.is_a?(Integer)
494
+ nullbyte = insn.operand[1]
495
+ raise EvalError, "[BUG] invalid bytearray of call_indirect" if nullbyte != 0x0
496
+ table_idx = stack.pop
497
+ raise EvalError, "[BUG] index stack invalid" if !table_idx.is_a?(I32)
498
+ fntype = self.instance.types[type_idx]
499
+ if !fntype
500
+ raise EvalError, "undefined type index: idx=#{type_idx}"
501
+ end
502
+ refs = self.instance.store.tables[0]&.refs
503
+ if !refs
504
+ raise EvalError, "uninitialized element idx:#{table_idx}"
505
+ end
506
+
507
+ fn = refs[table_idx.value]
508
+ case fn
509
+ when WasmFunction
510
+ if table.type != :funcref
511
+ raise EvalError, "invalid type of elem; expected: #{table.type}"
512
+ end
513
+ fn = fn.clone(override_type: fntype)
514
+ push_frame(fn)
515
+ when ExternalFunction
516
+ if table.type != :externref
517
+ raise EvalError, "invalid type of elem; expected: #{table.type}"
518
+ end
519
+ fn = fn.clone(override_type: fntype)
520
+ ret = invoke_external(fn)
521
+ self.stack.push ret if ret
522
+ when nil
523
+ raise EvalError, "uninitialized element idx:#{table_idx.value}"
524
+ else
525
+ raise EvalError, "[BUG] unknwon function type #{fn}"
526
+ end
527
+
356
528
  when :return
357
529
  old_frame = call_stack.pop
358
530
  if !old_frame
@@ -408,6 +580,18 @@ module Wardite
408
580
  end
409
581
  frame.locals[idx] = value
410
582
 
583
+ when :local_tee
584
+ idx = insn.operand[0]
585
+ if !idx.is_a?(Integer)
586
+ raise EvalError, "[BUG] invalid type of operand"
587
+ end
588
+ value = stack.pop
589
+ if !value
590
+ raise EvalError, "value should be pushed"
591
+ end
592
+ frame.locals[idx] = value
593
+ stack.push value
594
+
411
595
  when :global_get
412
596
  idx = insn.operand[0]
413
597
  if !idx.is_a?(Integer)
@@ -456,11 +640,69 @@ module Wardite
456
640
 
457
641
  rescue => e
458
642
  require "pp"
643
+ $stderr.puts "instance:::\n#{self.instance.pretty_inspect}"
459
644
  $stderr.puts "frame:::\n#{frame.pretty_inspect}"
460
645
  $stderr.puts "stack:::\n#{stack.pretty_inspect}"
461
646
  raise e
462
647
  end
463
648
 
649
+ # @rbs ops: Array[Op]
650
+ # @rbs pc_start: Integer
651
+ # @rbs return: Integer
652
+ def fetch_ops_while_else_or_end(ops, pc_start)
653
+ cursor = pc_start
654
+ depth = 0
655
+ loop {
656
+ cursor += 1
657
+ inst = ops[cursor]
658
+ case inst&.code
659
+ when nil
660
+ raise EvalError, "end op not found"
661
+ when :if
662
+ depth += 1
663
+ when :else
664
+ if depth == 0
665
+ return cursor
666
+ end
667
+ # do not touch depth
668
+ when :end
669
+ if depth == 0
670
+ return cursor
671
+ else
672
+ depth -= 1
673
+ end
674
+ else
675
+ # nop
676
+ end
677
+ }
678
+ raise "[BUG] unreachable"
679
+ end
680
+
681
+ # @rbs labels: Array[Label]
682
+ # @rbs stack: Array[wasmValue]
683
+ # @rbs level: Integer
684
+ # @rbs return: Integer
685
+ def do_branch(labels, stack, level)
686
+ idx = labels.size - 1 - level
687
+ label = labels[idx]
688
+ pc = if label.kind == :loop
689
+ # keep the top of labels for loop again...
690
+ while labels.size > idx + 1
691
+ labels.pop
692
+ end
693
+ stack_unwind(label.sp, 0)
694
+ label.start || raise(EvalError, "loop withour start")
695
+ else
696
+ while labels.size > idx
697
+ labels.pop
698
+ end
699
+ stack_unwind(label.sp, label.arity)
700
+ label.pc
701
+ end
702
+
703
+ pc
704
+ end
705
+
464
706
  # @rbs ops: Array[Op]
465
707
  # @rbs pc_start: Integer
466
708
  # @rbs return: Integer
@@ -473,7 +715,7 @@ module Wardite
473
715
  case inst&.code
474
716
  when nil
475
717
  raise EvalError, "end op not found"
476
- when :i
718
+ when :if, :block, :loop
477
719
  depth += 1
478
720
  when :end
479
721
  if depth == 0
@@ -509,7 +751,7 @@ module Wardite
509
751
  end
510
752
 
511
753
  # @rbs finish: Integer
512
- # @rbs return: Array[I32|I64|F32|F64]
754
+ # @rbs return: Array[wasmValue]
513
755
  def drained_stack(finish)
514
756
  drained = stack[0...finish]
515
757
  if ! drained
@@ -537,6 +779,20 @@ module Wardite
537
779
  end
538
780
  end
539
781
 
782
+ class Type
783
+ attr_accessor :callsig #: Array[Symbol]
784
+
785
+ attr_accessor :retsig #: Array[Symbol]
786
+
787
+ # @rbs callsig: Array[Symbol]
788
+ # @rbs retsig: Array[Symbol]
789
+ # @rbs returb: void
790
+ def initialize(callsig, retsig)
791
+ @callsig = callsig
792
+ @retsig = retsig
793
+ end
794
+ end
795
+
540
796
  class Frame
541
797
  attr_accessor :pc #: Integer
542
798
  attr_accessor :sp #: Integer
@@ -547,13 +803,13 @@ module Wardite
547
803
 
548
804
  attr_accessor :labels #: Array[Label]
549
805
 
550
- attr_accessor :locals #: Array[I32|I64|F32|F64]
806
+ attr_accessor :locals #: Array[wasmValue]
551
807
 
552
808
  # @rbs pc: Integer
553
809
  # @rbs sp: Integer
554
810
  # @rbs body: Array[Op]
555
811
  # @rbs arity: Integer
556
- # @rbs locals: Array[Object]
812
+ # @rbs locals: Array[wasmValue]
557
813
  # @rbs returb: void
558
814
  def initialize(pc, sp, body, arity, locals)
559
815
  @pc = pc
@@ -573,16 +829,20 @@ module Wardite
573
829
 
574
830
  attr_accessor :arity #: Integer
575
831
 
832
+ attr_accessor :start #: Integer|nil
833
+
576
834
  # @rbs kind: (:if|:loop|:block)
577
835
  # @rbs pc: Integer
578
836
  # @rbs sp: Integer
579
837
  # @rbs arity: Integer
580
- # @rbs returb: void
581
- def initialize(kind, pc, sp, arity)
838
+ # @rbs start: Integer|nil
839
+ # @rbs return: void
840
+ def initialize(kind, pc, sp, arity, start=nil)
582
841
  @kind = kind
583
842
  @pc = pc
584
843
  @sp = sp
585
844
  @arity = arity
845
+ @start = start
586
846
  end
587
847
  end
588
848
 
@@ -595,6 +855,10 @@ module Wardite
595
855
 
596
856
  attr_accessor :globals #: Array[Global]
597
857
 
858
+ attr_accessor :tables #: Array[Table]
859
+
860
+ attr_accessor :elements #: Array[[Symbol, Integer, Array[Integer]]]
861
+
598
862
  # @rbs inst: Instance
599
863
  # @rbs return: void
600
864
  def initialize(inst)
@@ -669,6 +933,56 @@ module Wardite
669
933
  end
670
934
  end
671
935
  end
936
+
937
+ @tables = []
938
+ @elements = []
939
+ table_section = inst.table_section
940
+ if table_section
941
+ table_section.table_types.each_with_index do |typ, idx|
942
+ init, max = *table_section.table_limits[idx]
943
+ if !init
944
+ raise LoadError, "empty limits"
945
+ end
946
+ table = Table.new(typ, init, max)
947
+ @tables << table
948
+ end
949
+ end
950
+
951
+ elem_section = inst.elem_section
952
+ if elem_section
953
+ elem_section.table_indices.each_with_index do |tidx, idx|
954
+ table = @tables[tidx]
955
+ if !table
956
+ raise LoadError, "invalid table index #{tidx}"
957
+ end
958
+ typ = table.type
959
+ offset = elem_section.table_offsets[idx]
960
+ if !offset
961
+ raise LoadError, "invalid element index #{idx}"
962
+ end
963
+ indices = elem_section.element_indices[idx]
964
+ if !indices
965
+ raise LoadError, "invalid element index #{idx}"
966
+ end
967
+ elms = [typ, offset, indices] #: [Symbol, Integer, Array[Integer]]
968
+ @elements << elms
969
+ end
970
+ end
971
+
972
+ @elements.each_with_index do |(typ, offset, indices), idx|
973
+ table = @tables[idx]
974
+ if !table
975
+ raise LoadError, "invalid table index #{idx}"
976
+ end
977
+ indices.each_with_index do |eidx, tidx|
978
+ case typ
979
+ when :funcref
980
+ table.set(offset + tidx, @funcs[eidx])
981
+ when :externref
982
+ raise NotImplementedError, "no support :externref"
983
+ end
984
+ end
985
+ end
672
986
  end
673
987
 
674
988
  # @rbs idx: Integer
@@ -711,6 +1025,38 @@ module Wardite
711
1025
  end
712
1026
  end
713
1027
 
1028
+ class Table
1029
+ attr_accessor :type #: Symbol
1030
+
1031
+ attr_accessor :current #: Integer
1032
+
1033
+ attr_accessor :max #: Integer|nil
1034
+
1035
+ attr_accessor :refs #: Array[WasmFunction|ExternalFunction|nil]
1036
+
1037
+ # @rbs type: Symbol
1038
+ # @rbs init: Integer
1039
+ # @rbs max: Integer|nil
1040
+ # @rbs return: void
1041
+ def initialize(type, init, max)
1042
+ @type = type
1043
+ @current = init
1044
+ @max = max
1045
+
1046
+ @refs = Array.new(3, nil)
1047
+ end
1048
+
1049
+ # @rbs idx: Integer
1050
+ # @rbs elem: WasmFunction|ExternalFunction|nil
1051
+ # @rbs return: void
1052
+ def set(idx, elem)
1053
+ if idx >= @current
1054
+ raise GenericError, "idx too large for table"
1055
+ end
1056
+ @refs[idx] = elem
1057
+ end
1058
+ end
1059
+
714
1060
  class Global
715
1061
  attr_accessor :type #: Symbol
716
1062
 
@@ -719,7 +1065,7 @@ module Wardite
719
1065
  # TODO: unused in wasm 1.0 spec?
720
1066
  attr_accessor :shared #: bool
721
1067
 
722
- attr_accessor :value #: I32|I64|F32|F64
1068
+ attr_accessor :value #: wasmValue
723
1069
 
724
1070
  # @rbs &blk: (Global) -> void
725
1071
  # @rbs return: void
@@ -829,25 +1175,51 @@ module Wardite
829
1175
  # @rbs return: Array[Integer]
830
1176
  def locals_count
831
1177
  code_body.locals_count
832
- end
1178
+ end
1179
+
1180
+ # @rbs override_type: Type?
1181
+ # @rbs return: WasmFunction
1182
+ def clone(override_type: nil)
1183
+ if override_type
1184
+ # code_body is assumed to be frozen, so we can copy its ref
1185
+ WasmFunction.new(override_type.callsig, override_type.retsig, code_body)
1186
+ else
1187
+ WasmFunction.new(callsig, retsig, code_body)
1188
+ end
1189
+ end
833
1190
  end
834
1191
 
1192
+ # @rbs!
1193
+ # type wasmFuncReturn = Object|nil
1194
+ # type wasmCallable = ^(Store, Array[wasmValue]) -> wasmFuncReturn
1195
+
835
1196
  class ExternalFunction
836
1197
  attr_accessor :callsig #: Array[Symbol]
837
1198
 
838
1199
  attr_accessor :retsig #: Array[Symbol]
839
1200
 
840
- attr_accessor :callable #: Proc
1201
+ attr_accessor :callable #: wasmCallable
841
1202
 
842
1203
  # @rbs callsig: Array[Symbol]
843
1204
  # @rbs retsig: Array[Symbol]
844
- # @rbs callable: Proc
1205
+ # @rbs callable: wasmCallable
845
1206
  # @rbs return: void
846
1207
  def initialize(callsig, retsig, callable)
847
1208
  @callsig = callsig
848
1209
  @retsig = retsig
849
1210
  @callable = callable
850
1211
  end
1212
+
1213
+ # @rbs override_type: Type?
1214
+ # @rbs return: ExternalFunction
1215
+ def clone(override_type: nil)
1216
+ if override_type
1217
+ # callable is assumed to be frozen, so we can copy its ref
1218
+ ExternalFunction.new(override_type.callsig, override_type.retsig, callable)
1219
+ else
1220
+ ExternalFunction.new(callsig, retsig, callable)
1221
+ end
1222
+ end
851
1223
  end
852
1224
 
853
1225
  class GenericError < StandardError; end
@@ -1,6 +1,8 @@
1
1
  # Generated from lib/wardite/instruction.rb with RBS::Inline
2
2
 
3
3
  module Wardite
4
+ type operandItem = Integer | Array[Integer] | Float | Block
5
+
4
6
  class Op
5
7
  # @see https://pengowray.github.io/wasm-ops/
6
8
  SYMS: Array[Symbol]
@@ -13,12 +15,12 @@ module Wardite
13
15
  attr_accessor code: Symbol
14
16
 
15
17
  # TODO: add types of potential operands
16
- attr_accessor operand: Array[Integer | Float | Block]
18
+ attr_accessor operand: Array[operandItem]
17
19
 
18
20
  # @rbs namespace: Symbol
19
21
  # @rbs code: Symbol
20
- # @rbs operand: Array[Integer|Float|Block]
21
- def initialize: (Symbol namespace, Symbol code, Array[Integer | Float | Block] operand) -> untyped
22
+ # @rbs operand: Array[operandItem]
23
+ def initialize: (Symbol namespace, Symbol code, Array[operandItem] operand) -> untyped
22
24
 
23
25
  # @rbs chr: String
24
26
  # @rbs return: [Symbol, Symbol]
@@ -29,6 +31,7 @@ module Wardite
29
31
  def self.operand_of: (Symbol code) -> Array[Symbol]
30
32
 
31
33
  # @see https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2
34
+ # We use this for reftype conversion. https://webassembly.github.io/spec/core/binary/types.html#binary-reftype
32
35
  # @rbs code: Integer
33
36
  # @rbs return: Symbol
34
37
  def self.i2type: (Integer code) -> Symbol