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