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.
- checksums.yaml +4 -4
- data/Rakefile +163 -0
- data/Steepfile +1 -0
- data/examples/consts.wat +12 -0
- data/examples/global.wat +13 -0
- data/lib/wardite/alu_f32.generated.rb +250 -0
- data/lib/wardite/alu_f64.generated.rb +250 -0
- data/lib/wardite/alu_i32.generated.rb +398 -18
- data/lib/wardite/alu_i64.generated.rb +514 -0
- data/lib/wardite/convert.generated.rb +234 -0
- data/lib/wardite/instruction.rb +63 -32
- data/lib/wardite/load.rb +679 -0
- data/lib/wardite/value.rb +576 -31
- data/lib/wardite/version.rb +1 -1
- data/lib/wardite/wasi.rb +1 -1
- data/lib/wardite.rb +172 -634
- data/scripts/gen_alu.rb +653 -27
- data/scripts/gen_conv.rb +76 -0
- data/scripts/templates/conv_module.rb.tmpl +18 -0
- data/sig/generated/wardite/alu_f32.generated.rbs +11 -0
- data/sig/generated/wardite/alu_f64.generated.rbs +11 -0
- data/sig/generated/wardite/alu_i64.generated.rbs +11 -0
- data/sig/generated/wardite/convert.generated.rbs +11 -0
- data/sig/generated/wardite/instruction.rbs +8 -2
- data/sig/generated/wardite/load.rbs +198 -0
- data/sig/generated/wardite/value.rbs +263 -19
- data/sig/generated/wardite.rbs +36 -168
- metadata +16 -2
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
|
@@ -13,570 +14,16 @@ module Wardite
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
require_relative "wardite/alu_i32.generated"
|
17
|
+
require_relative "wardite/alu_i64.generated"
|
18
|
+
require_relative "wardite/alu_f32.generated"
|
19
|
+
require_relative "wardite/alu_f64.generated"
|
20
|
+
require_relative "wardite/convert.generated"
|
16
21
|
|
17
22
|
require_relative "wardite/wasi"
|
18
23
|
|
19
24
|
require "stringio"
|
20
25
|
|
21
26
|
module Wardite
|
22
|
-
class Section
|
23
|
-
attr_accessor :name #: String
|
24
|
-
|
25
|
-
attr_accessor :code #: Integer
|
26
|
-
|
27
|
-
attr_accessor :size #: Integer
|
28
|
-
end
|
29
|
-
|
30
|
-
class TypeSection < Section
|
31
|
-
attr_accessor :defined_types #: Array[Array[Symbol]]
|
32
|
-
|
33
|
-
attr_accessor :defined_results #: Array[Array[Symbol]]
|
34
|
-
|
35
|
-
# @rbs return: void
|
36
|
-
def initialize
|
37
|
-
self.name = "Type"
|
38
|
-
self.code = 0x1
|
39
|
-
|
40
|
-
@defined_types = []
|
41
|
-
@defined_results = []
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class FunctionSection < Section
|
46
|
-
attr_accessor :func_indices #: Array[Integer]
|
47
|
-
|
48
|
-
# @rbs return: void
|
49
|
-
def initialize
|
50
|
-
self.name = "Function"
|
51
|
-
self.code = 0x3
|
52
|
-
|
53
|
-
@func_indices = []
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class MemorySection < Section
|
58
|
-
attr_accessor :limits #: Array[[Integer, Integer|nil]]
|
59
|
-
|
60
|
-
# @rbs return: void
|
61
|
-
def initialize
|
62
|
-
self.name = "Memory"
|
63
|
-
self.code = 0x5
|
64
|
-
|
65
|
-
@limits = []
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class CodeSection < Section
|
70
|
-
class CodeBody
|
71
|
-
attr_accessor :locals_count #: Array[Integer]
|
72
|
-
|
73
|
-
attr_accessor :locals_type #: Array[Symbol]
|
74
|
-
|
75
|
-
attr_accessor :body #: Array[Op]
|
76
|
-
|
77
|
-
# @rbs &blk: (CodeBody) -> void
|
78
|
-
# @rbs return: void
|
79
|
-
def initialize(&blk)
|
80
|
-
blk.call(self)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
attr_accessor :func_codes #:Array[CodeBody]
|
85
|
-
|
86
|
-
# @rbs return: void
|
87
|
-
def initialize
|
88
|
-
self.name = "Code"
|
89
|
-
self.code = 0xa
|
90
|
-
|
91
|
-
@func_codes = []
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
class DataSection < Section
|
96
|
-
class Segment
|
97
|
-
attr_accessor :flags #: Integer
|
98
|
-
|
99
|
-
attr_accessor :offset #: Integer
|
100
|
-
|
101
|
-
attr_accessor :data #: String
|
102
|
-
|
103
|
-
# @rbs &blk: (Segment) -> void
|
104
|
-
# @rbs return: void
|
105
|
-
def initialize(&blk)
|
106
|
-
blk.call(self)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
attr_accessor :segments #: Array[Segment]
|
111
|
-
|
112
|
-
# @rbs return: void
|
113
|
-
def initialize
|
114
|
-
self.name = "Data"
|
115
|
-
self.code = 0xb
|
116
|
-
|
117
|
-
@segments = []
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class ExportSection < Section
|
122
|
-
class ExportDesc
|
123
|
-
attr_accessor :name #: String
|
124
|
-
|
125
|
-
attr_accessor :kind #: Integer
|
126
|
-
|
127
|
-
attr_accessor :func_index #: Integer
|
128
|
-
end
|
129
|
-
|
130
|
-
attr_accessor :exports #: Hash[String, ExportDesc]
|
131
|
-
|
132
|
-
def initialize #: void
|
133
|
-
self.name = "Export"
|
134
|
-
self.code = 0x7
|
135
|
-
|
136
|
-
@exports = {}
|
137
|
-
end
|
138
|
-
|
139
|
-
# @rbs &blk: (ExportDesc) -> void
|
140
|
-
def add_desc(&blk)
|
141
|
-
desc = ExportDesc.new
|
142
|
-
blk.call(desc)
|
143
|
-
self.exports[desc.name] = desc
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
class ImportSection < Section
|
148
|
-
class ImportDesc
|
149
|
-
attr_accessor :module_name #: String
|
150
|
-
|
151
|
-
attr_accessor :name #: String
|
152
|
-
|
153
|
-
attr_accessor :kind #: Integer
|
154
|
-
|
155
|
-
attr_accessor :sig_index #: Integer
|
156
|
-
end
|
157
|
-
|
158
|
-
attr_accessor :imports #: Array[ImportDesc]
|
159
|
-
|
160
|
-
def initialize #: void
|
161
|
-
self.name = "Import"
|
162
|
-
self.code = 0x2
|
163
|
-
|
164
|
-
@imports = []
|
165
|
-
end
|
166
|
-
|
167
|
-
# @rbs &blk: (ImportDesc) -> void
|
168
|
-
def add_desc(&blk)
|
169
|
-
desc = ImportDesc.new
|
170
|
-
blk.call(desc) if blk
|
171
|
-
self.imports << desc
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
module BinaryLoader
|
176
|
-
extend Wardite::Leb128Helper
|
177
|
-
extend Wardite::ValueHelper
|
178
|
-
|
179
|
-
# @rbs buf: File|StringIO
|
180
|
-
# @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
|
181
|
-
# @rbs enable_wasi: boolish
|
182
|
-
# @rbs return: Instance
|
183
|
-
def self.load_from_buffer(buf, import_object: {}, enable_wasi: true)
|
184
|
-
@buf = buf
|
185
|
-
|
186
|
-
version = preamble
|
187
|
-
sections_ = sections
|
188
|
-
|
189
|
-
if enable_wasi
|
190
|
-
wasi_env = Wardite::WasiSnapshotPreview1.new
|
191
|
-
import_object[:wasi_snapshot_preview1] = wasi_env.to_module
|
192
|
-
end
|
193
|
-
|
194
|
-
return Instance.new(import_object) do |i|
|
195
|
-
i.version = version
|
196
|
-
i.sections = sections_
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# @rbs return: Integer
|
201
|
-
def self.preamble
|
202
|
-
asm = @buf.read 4
|
203
|
-
if !asm
|
204
|
-
raise LoadError, "buffer too short"
|
205
|
-
end
|
206
|
-
if asm != "\u0000asm"
|
207
|
-
raise LoadError, "invalid preamble"
|
208
|
-
end
|
209
|
-
|
210
|
-
vstr = @buf.read(4)
|
211
|
-
if !vstr
|
212
|
-
raise LoadError, "buffer too short"
|
213
|
-
end
|
214
|
-
version = vstr.to_enum(:chars)
|
215
|
-
.with_index
|
216
|
-
.inject(0) {|dest, (c, i)| dest | (c.ord << i*8) }
|
217
|
-
if version != 1
|
218
|
-
raise LoadError, "unsupported version: #{version}"
|
219
|
-
end
|
220
|
-
version
|
221
|
-
end
|
222
|
-
|
223
|
-
# @rbs return: Array[Section]
|
224
|
-
def self.sections
|
225
|
-
sections = [] #: Array[Section]
|
226
|
-
|
227
|
-
loop do
|
228
|
-
byte = @buf.read(1)
|
229
|
-
if !byte
|
230
|
-
break
|
231
|
-
end
|
232
|
-
code = byte.ord
|
233
|
-
|
234
|
-
section = case code
|
235
|
-
when Wardite::SectionType
|
236
|
-
type_section
|
237
|
-
when Wardite::SectionImport
|
238
|
-
import_section
|
239
|
-
when Wardite::SectionFunction
|
240
|
-
function_section
|
241
|
-
when Wardite::SectionTable
|
242
|
-
unimplemented_skip_section(code)
|
243
|
-
when Wardite::SectionMemory
|
244
|
-
memory_section
|
245
|
-
when Wardite::SectionGlobal
|
246
|
-
unimplemented_skip_section(code)
|
247
|
-
when Wardite::SectionExport
|
248
|
-
export_section
|
249
|
-
when Wardite::SectionStart
|
250
|
-
unimplemented_skip_section(code)
|
251
|
-
when Wardite::SectionElement
|
252
|
-
unimplemented_skip_section(code)
|
253
|
-
when Wardite::SectionCode
|
254
|
-
code_section
|
255
|
-
when Wardite::SectionData
|
256
|
-
data_section
|
257
|
-
when Wardite::SectionCustom
|
258
|
-
unimplemented_skip_section(code)
|
259
|
-
else
|
260
|
-
raise LoadError, "unknown code: #{code}(\"#{code.to_s 16}\")"
|
261
|
-
end
|
262
|
-
|
263
|
-
if section
|
264
|
-
sections << section
|
265
|
-
end
|
266
|
-
end
|
267
|
-
sections
|
268
|
-
end
|
269
|
-
|
270
|
-
# @rbs return: TypeSection
|
271
|
-
def self.type_section
|
272
|
-
dest = TypeSection.new
|
273
|
-
|
274
|
-
size = fetch_uleb128(@buf)
|
275
|
-
dest.size = size
|
276
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
277
|
-
|
278
|
-
len = fetch_uleb128(sbuf)
|
279
|
-
len.times do |i|
|
280
|
-
fncode = assert_read(sbuf, 1)
|
281
|
-
if fncode != "\x60"
|
282
|
-
raise LoadError, "not a function definition"
|
283
|
-
end
|
284
|
-
|
285
|
-
arglen = fetch_uleb128(sbuf)
|
286
|
-
arg = []
|
287
|
-
arglen.times do
|
288
|
-
case ty = assert_read(sbuf, 1)&.ord
|
289
|
-
when 0x7f
|
290
|
-
arg << :i32
|
291
|
-
when 0x7e
|
292
|
-
arg << :i64
|
293
|
-
else
|
294
|
-
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
295
|
-
end
|
296
|
-
end
|
297
|
-
dest.defined_types << arg
|
298
|
-
|
299
|
-
retlen = fetch_uleb128(sbuf)
|
300
|
-
ret = []
|
301
|
-
retlen.times do
|
302
|
-
case ty = assert_read(sbuf, 1)&.ord
|
303
|
-
when 0x7f
|
304
|
-
ret << :i32
|
305
|
-
when 0x7e
|
306
|
-
ret << :i64
|
307
|
-
else
|
308
|
-
raise NotImplementedError, "unsupported for now: #{ty.inspect}"
|
309
|
-
end
|
310
|
-
end
|
311
|
-
dest.defined_results << ret
|
312
|
-
end
|
313
|
-
|
314
|
-
dest
|
315
|
-
end
|
316
|
-
|
317
|
-
# @rbs return: ImportSection
|
318
|
-
def self.import_section
|
319
|
-
dest = ImportSection.new
|
320
|
-
size = fetch_uleb128(@buf)
|
321
|
-
dest.size = size
|
322
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
323
|
-
|
324
|
-
len = fetch_uleb128(sbuf)
|
325
|
-
len.times do |i|
|
326
|
-
mlen = fetch_uleb128(sbuf)
|
327
|
-
module_name = assert_read(sbuf, mlen)
|
328
|
-
nlen = fetch_uleb128(sbuf)
|
329
|
-
name = assert_read(sbuf, nlen)
|
330
|
-
kind_ = assert_read(sbuf, 1)
|
331
|
-
kind = kind_[0]&.ord
|
332
|
-
if !kind
|
333
|
-
raise "[BUG] empty unpacked string" # guard rbs
|
334
|
-
end
|
335
|
-
|
336
|
-
index = fetch_uleb128(sbuf)
|
337
|
-
dest.add_desc do |desc|
|
338
|
-
desc.module_name = module_name
|
339
|
-
desc.name = name
|
340
|
-
desc.kind = kind
|
341
|
-
desc.sig_index = index
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
dest
|
346
|
-
end
|
347
|
-
|
348
|
-
# @rbs return: MemorySection
|
349
|
-
def self.memory_section
|
350
|
-
dest = MemorySection.new
|
351
|
-
size = fetch_uleb128(@buf)
|
352
|
-
dest.size = size
|
353
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
354
|
-
|
355
|
-
len = fetch_uleb128(sbuf)
|
356
|
-
if len != 1
|
357
|
-
raise LoadError, "memory section has invalid size: #{len}"
|
358
|
-
end
|
359
|
-
len.times do |i|
|
360
|
-
flags = fetch_uleb128(sbuf)
|
361
|
-
min = fetch_uleb128(sbuf)
|
362
|
-
|
363
|
-
max = nil
|
364
|
-
if flags != 0
|
365
|
-
max = fetch_uleb128(sbuf)
|
366
|
-
end
|
367
|
-
dest.limits << [min, max]
|
368
|
-
end
|
369
|
-
dest
|
370
|
-
end
|
371
|
-
|
372
|
-
# @rbs return: FunctionSection
|
373
|
-
def self.function_section
|
374
|
-
dest = FunctionSection.new
|
375
|
-
size = fetch_uleb128(@buf)
|
376
|
-
dest.size = size
|
377
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
378
|
-
|
379
|
-
len = fetch_uleb128(sbuf)
|
380
|
-
len.times do |i|
|
381
|
-
index = fetch_uleb128(sbuf)
|
382
|
-
dest.func_indices << index
|
383
|
-
end
|
384
|
-
dest
|
385
|
-
end
|
386
|
-
|
387
|
-
# @rbs return: CodeSection
|
388
|
-
def self.code_section
|
389
|
-
dest = CodeSection.new
|
390
|
-
size = fetch_uleb128(@buf)
|
391
|
-
dest.size = size
|
392
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
393
|
-
|
394
|
-
len = fetch_uleb128(sbuf)
|
395
|
-
len.times do |i|
|
396
|
-
ilen = fetch_uleb128(sbuf)
|
397
|
-
code = assert_read(sbuf, ilen)
|
398
|
-
last_code = code[-1]
|
399
|
-
if ! last_code
|
400
|
-
raise "[BUG] empty code fetched" # guard for steep check
|
401
|
-
end
|
402
|
-
if last_code.ord != 0x0b
|
403
|
-
$stderr.puts "warning: instruction not ended with inst end(0x0b): 0x0#{last_code.ord}"
|
404
|
-
end
|
405
|
-
cbuf = StringIO.new(code)
|
406
|
-
locals_count = []
|
407
|
-
locals_type = []
|
408
|
-
locals_len = fetch_uleb128(cbuf)
|
409
|
-
locals_len.times do
|
410
|
-
type_count = fetch_uleb128(cbuf)
|
411
|
-
locals_count << type_count
|
412
|
-
value_type = assert_read(cbuf, 1)&.ord
|
413
|
-
locals_type << Op.i2type(value_type || -1)
|
414
|
-
end
|
415
|
-
body = code_body(cbuf)
|
416
|
-
dest.func_codes << CodeSection::CodeBody.new do |b|
|
417
|
-
b.locals_count = locals_count
|
418
|
-
b.locals_type = locals_type
|
419
|
-
b.body = body
|
420
|
-
end
|
421
|
-
end
|
422
|
-
dest
|
423
|
-
end
|
424
|
-
|
425
|
-
# @rbs buf: StringIO
|
426
|
-
# @rbs return: Array[::Wardite::Op]
|
427
|
-
def self.code_body(buf)
|
428
|
-
dest = []
|
429
|
-
while c = buf.read(1)
|
430
|
-
namespace, code = Op.to_sym(c)
|
431
|
-
operand_types = Op.operand_of(code)
|
432
|
-
operand = [] #: Array[Integer|Float|Block]
|
433
|
-
operand_types.each do |typ|
|
434
|
-
case typ
|
435
|
-
when :u32
|
436
|
-
operand << fetch_uleb128(buf)
|
437
|
-
when :i32
|
438
|
-
operand << fetch_sleb128(buf)
|
439
|
-
when :u8_block # :if specific
|
440
|
-
block_ope = buf.read 1
|
441
|
-
if ! block_ope
|
442
|
-
raise "buffer too short for if"
|
443
|
-
end
|
444
|
-
if block_ope.ord == 0x40
|
445
|
-
operand << Block.void
|
446
|
-
else
|
447
|
-
operand << Block.new([block_ope.ord])
|
448
|
-
end
|
449
|
-
else
|
450
|
-
$stderr.puts "warning: unknown type #{typ.inspect}. defaulting to u32"
|
451
|
-
operand << fetch_uleb128(buf)
|
452
|
-
end
|
453
|
-
end
|
454
|
-
|
455
|
-
dest << Op.new(namespace, code, operand)
|
456
|
-
end
|
457
|
-
|
458
|
-
dest
|
459
|
-
end
|
460
|
-
|
461
|
-
# @rbs return: DataSection
|
462
|
-
def self.data_section
|
463
|
-
dest = DataSection.new
|
464
|
-
size = fetch_uleb128(@buf)
|
465
|
-
dest.size = size
|
466
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
467
|
-
|
468
|
-
len = fetch_uleb128(sbuf)
|
469
|
-
len.times do |i|
|
470
|
-
mem_index = fetch_uleb128(sbuf)
|
471
|
-
code = fetch_insn_while_end(sbuf)
|
472
|
-
ops = code_body(StringIO.new(code))
|
473
|
-
offset = decode_expr(ops)
|
474
|
-
|
475
|
-
len = fetch_uleb128(sbuf)
|
476
|
-
data = sbuf.read len
|
477
|
-
if !data
|
478
|
-
raise LoadError, "buffer too short"
|
479
|
-
end
|
480
|
-
|
481
|
-
segment = DataSection::Segment.new do |seg|
|
482
|
-
seg.flags = mem_index
|
483
|
-
seg.offset = offset
|
484
|
-
seg.data = data
|
485
|
-
end
|
486
|
-
dest.segments << segment
|
487
|
-
end
|
488
|
-
dest
|
489
|
-
end
|
490
|
-
|
491
|
-
# @rbs sbuf: StringIO
|
492
|
-
# @rbs return: String
|
493
|
-
def self.fetch_insn_while_end(sbuf)
|
494
|
-
code = String.new("")
|
495
|
-
loop {
|
496
|
-
c = sbuf.read 1
|
497
|
-
if !c
|
498
|
-
break
|
499
|
-
end
|
500
|
-
code << c
|
501
|
-
if c == "\u000b" # :end
|
502
|
-
break
|
503
|
-
end
|
504
|
-
}
|
505
|
-
code
|
506
|
-
end
|
507
|
-
|
508
|
-
# @rbs ops: Array[Op]
|
509
|
-
# @rbs return: Integer
|
510
|
-
def self.decode_expr(ops)
|
511
|
-
# sees first opcode
|
512
|
-
op = ops.first
|
513
|
-
if !op
|
514
|
-
raise LoadError, "empty opcodes"
|
515
|
-
end
|
516
|
-
case op.code
|
517
|
-
when :i32_const
|
518
|
-
arg = op.operand[0]
|
519
|
-
if !arg.is_a?(Integer)
|
520
|
-
raise "Invalid definition of operand"
|
521
|
-
end
|
522
|
-
return arg
|
523
|
-
else
|
524
|
-
raise "Unimplemented offset op: #{op.code.inspect}"
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
# @rbs return: ExportSection
|
529
|
-
def self.export_section
|
530
|
-
dest = ExportSection.new
|
531
|
-
size = fetch_uleb128(@buf)
|
532
|
-
dest.size = size
|
533
|
-
sbuf = StringIO.new(@buf.read(size) || raise("buffer too short"))
|
534
|
-
|
535
|
-
len = fetch_uleb128(sbuf)
|
536
|
-
len.times do |i|
|
537
|
-
nlen = fetch_uleb128(sbuf)
|
538
|
-
name = assert_read(sbuf, nlen)
|
539
|
-
kind_ = assert_read(sbuf, 1)
|
540
|
-
kind = kind_[0]&.ord
|
541
|
-
if !kind
|
542
|
-
raise "[BUG] empty unpacked string" # guard rbs
|
543
|
-
end
|
544
|
-
|
545
|
-
index = fetch_uleb128(sbuf)
|
546
|
-
dest.add_desc do |desc|
|
547
|
-
desc.name = name
|
548
|
-
desc.kind = kind
|
549
|
-
desc.func_index = index
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
|
-
dest
|
554
|
-
end
|
555
|
-
|
556
|
-
# @rbs code: Integer
|
557
|
-
# @rbs return: nil
|
558
|
-
def self.unimplemented_skip_section(code)
|
559
|
-
$stderr.puts "warning: unimplemented section: 0x0#{code}"
|
560
|
-
size = @buf.read(1)&.ord
|
561
|
-
@buf.read(size)
|
562
|
-
nil
|
563
|
-
end
|
564
|
-
|
565
|
-
# @rbs sbuf: StringIO
|
566
|
-
# @rbs n: Integer
|
567
|
-
# @rbs return: String
|
568
|
-
def self.assert_read(sbuf, n)
|
569
|
-
ret = sbuf.read n
|
570
|
-
if !ret
|
571
|
-
raise LoadError, "too short section size"
|
572
|
-
end
|
573
|
-
if ret.size != n
|
574
|
-
raise LoadError, "too short section size"
|
575
|
-
end
|
576
|
-
ret
|
577
|
-
end
|
578
|
-
end
|
579
|
-
|
580
27
|
class Instance
|
581
28
|
attr_accessor :version #: Integer
|
582
29
|
|
@@ -635,6 +82,18 @@ module Wardite
|
|
635
82
|
sec
|
636
83
|
end
|
637
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
|
+
|
638
97
|
# @rbs return: DataSection|nil
|
639
98
|
def data_section
|
640
99
|
sec = @sections.find{|s| s.code == Const::SectionData }
|
@@ -688,7 +147,7 @@ module Wardite
|
|
688
147
|
include ValueHelper
|
689
148
|
|
690
149
|
# TODO: add types of class that the stack accomodates
|
691
|
-
attr_accessor :stack #: Array[I32|I64|F32|F64
|
150
|
+
attr_accessor :stack #: Array[I32|I64|F32|F64]
|
692
151
|
|
693
152
|
attr_accessor :call_stack #: Array[Frame]
|
694
153
|
|
@@ -848,12 +307,25 @@ module Wardite
|
|
848
307
|
# @rbs return: void
|
849
308
|
def eval_insn(frame, insn)
|
850
309
|
case insn.namespace
|
310
|
+
when :convert
|
311
|
+
return Evaluator.convert_eval_insn(self, frame, insn)
|
851
312
|
when :i32
|
852
313
|
return Evaluator.i32_eval_insn(self, frame, insn)
|
314
|
+
when :i64
|
315
|
+
return Evaluator.i64_eval_insn(self, frame, insn)
|
316
|
+
when :f32
|
317
|
+
return Evaluator.f32_eval_insn(self, frame, insn)
|
318
|
+
when :f64
|
319
|
+
return Evaluator.f64_eval_insn(self, frame, insn)
|
853
320
|
end
|
854
321
|
|
855
322
|
# unmached namespace...
|
856
323
|
case insn.code
|
324
|
+
when :unreachable
|
325
|
+
raise Unreachable, "unreachable op"
|
326
|
+
when :nop
|
327
|
+
return
|
328
|
+
|
857
329
|
when :if
|
858
330
|
block = insn.operand[0]
|
859
331
|
raise EvalError, "if op without block" if !block.is_a?(Block)
|
@@ -866,78 +338,6 @@ module Wardite
|
|
866
338
|
|
867
339
|
label = Label.new(:if, next_pc, stack.size, block.result_size)
|
868
340
|
frame.labels.push(label)
|
869
|
-
|
870
|
-
when :local_get
|
871
|
-
idx = insn.operand[0]
|
872
|
-
if !idx.is_a?(Integer)
|
873
|
-
raise EvalError, "[BUG] invalid type of operand"
|
874
|
-
end
|
875
|
-
local = frame.locals[idx]
|
876
|
-
if !local
|
877
|
-
raise EvalError, "local not found"
|
878
|
-
end
|
879
|
-
stack.push(local)
|
880
|
-
when :local_set
|
881
|
-
idx = insn.operand[0]
|
882
|
-
if !idx.is_a?(Integer)
|
883
|
-
raise EvalError, "[BUG] invalid type of operand"
|
884
|
-
end
|
885
|
-
value = stack.pop
|
886
|
-
if !value
|
887
|
-
raise EvalError, "value should be pushed"
|
888
|
-
end
|
889
|
-
if value.is_a?(Block)
|
890
|
-
raise EvalError, "block value detected"
|
891
|
-
end
|
892
|
-
frame.locals[idx] = value
|
893
|
-
|
894
|
-
# when :i32_lts
|
895
|
-
# right, left = stack.pop, stack.pop
|
896
|
-
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
897
|
-
# raise EvalError, "maybe empty stack"
|
898
|
-
# end
|
899
|
-
# value = (left < right) ? 1 : 0
|
900
|
-
# stack.push(value)
|
901
|
-
# when :i32_leu
|
902
|
-
# right, left = stack.pop, stack.pop
|
903
|
-
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
904
|
-
# raise EvalError, "maybe empty stack"
|
905
|
-
# end
|
906
|
-
# value = (left >= right) ? 1 : 0
|
907
|
-
# stack.push(value)
|
908
|
-
# when :i32_add
|
909
|
-
# right, left = stack.pop, stack.pop
|
910
|
-
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
911
|
-
# raise EvalError, "maybe empty stack"
|
912
|
-
# end
|
913
|
-
# stack.push(left + right)
|
914
|
-
# when :i32_sub
|
915
|
-
# right, left = stack.pop, stack.pop
|
916
|
-
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
917
|
-
# raise EvalError, "maybe empty stack"
|
918
|
-
# end
|
919
|
-
# stack.push(left - right)
|
920
|
-
# when :i32_const
|
921
|
-
# const = insn.operand[0]
|
922
|
-
# if !const.is_a?(Integer)
|
923
|
-
# raise EvalError, "[BUG] invalid type of operand"
|
924
|
-
# end
|
925
|
-
# stack.push(const)
|
926
|
-
# when :i32_store
|
927
|
-
# _align = insn.operand[0] # TODO: alignment support?
|
928
|
-
# offset = insn.operand[1]
|
929
|
-
# raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
930
|
-
|
931
|
-
# value = stack.pop
|
932
|
-
# addr = stack.pop
|
933
|
-
# if !value.is_a?(Integer) || !addr.is_a?(Integer)
|
934
|
-
# raise EvalError, "maybe stack too short"
|
935
|
-
# end
|
936
|
-
|
937
|
-
# at = addr + offset
|
938
|
-
# data_end = at + 4 # sizeof(i32)
|
939
|
-
# memory = self.instance.store.memories[0] || raise("[BUG] no memory")
|
940
|
-
# memory.data[at...data_end] = [value].pack("I")
|
941
341
|
|
942
342
|
when :call
|
943
343
|
idx = insn.operand[0]
|
@@ -972,7 +372,93 @@ module Wardite
|
|
972
372
|
end
|
973
373
|
stack_unwind(old_frame.sp, old_frame.arity)
|
974
374
|
end
|
375
|
+
|
376
|
+
when :drop
|
377
|
+
stack.pop
|
378
|
+
|
379
|
+
when :select
|
380
|
+
cond, right, left = stack.pop, stack.pop, stack.pop
|
381
|
+
if !cond.is_a?(I32)
|
382
|
+
raise EvalError, "invalid stack for select"
|
383
|
+
end
|
384
|
+
if !right || !left
|
385
|
+
raise EvalError, "stack too short"
|
386
|
+
end
|
387
|
+
stack.push(cond.value != 0 ? left : right)
|
388
|
+
|
389
|
+
when :local_get
|
390
|
+
idx = insn.operand[0]
|
391
|
+
if !idx.is_a?(Integer)
|
392
|
+
raise EvalError, "[BUG] invalid type of operand"
|
393
|
+
end
|
394
|
+
local = frame.locals[idx]
|
395
|
+
if !local
|
396
|
+
raise EvalError, "local not found"
|
397
|
+
end
|
398
|
+
stack.push(local)
|
399
|
+
|
400
|
+
when :local_set
|
401
|
+
idx = insn.operand[0]
|
402
|
+
if !idx.is_a?(Integer)
|
403
|
+
raise EvalError, "[BUG] invalid type of operand"
|
404
|
+
end
|
405
|
+
value = stack.pop
|
406
|
+
if !value
|
407
|
+
raise EvalError, "value should be pushed"
|
408
|
+
end
|
409
|
+
frame.locals[idx] = value
|
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
|
+
|
441
|
+
when :memory_size
|
442
|
+
memory = instance.store.memories[0] || raise("[BUG] no memory")
|
443
|
+
stack.push(I32(memory.current))
|
444
|
+
|
445
|
+
when :memory_grow
|
446
|
+
delta = stack.pop
|
447
|
+
if !delta.is_a?(I32)
|
448
|
+
raise EvalError, "maybe stack too short"
|
449
|
+
end
|
450
|
+
memory = instance.store.memories[0] || raise("[BUG] no memory")
|
451
|
+
stack.push(I32(memory.grow(delta.value)))
|
452
|
+
|
453
|
+
else
|
454
|
+
raise "TODO! unsupported #{insn.inspect}"
|
975
455
|
end
|
456
|
+
|
457
|
+
rescue => e
|
458
|
+
require "pp"
|
459
|
+
$stderr.puts "frame:::\n#{frame.pretty_inspect}"
|
460
|
+
$stderr.puts "stack:::\n#{stack.pretty_inspect}"
|
461
|
+
raise e
|
976
462
|
end
|
977
463
|
|
978
464
|
# @rbs ops: Array[Op]
|
@@ -1023,7 +509,7 @@ module Wardite
|
|
1023
509
|
end
|
1024
510
|
|
1025
511
|
# @rbs finish: Integer
|
1026
|
-
# @rbs return: Array[I32|I64|F32|F64
|
512
|
+
# @rbs return: Array[I32|I64|F32|F64]
|
1027
513
|
def drained_stack(finish)
|
1028
514
|
drained = stack[0...finish]
|
1029
515
|
if ! drained
|
@@ -1104,9 +590,11 @@ module Wardite
|
|
1104
590
|
attr_accessor :funcs #: Array[WasmFunction|ExternalFunction]
|
1105
591
|
|
1106
592
|
# FIXME: attr_accessor :modules
|
1107
|
-
|
593
|
+
|
1108
594
|
attr_accessor :memories #: Array[Memory]
|
1109
595
|
|
596
|
+
attr_accessor :globals #: Array[Global]
|
597
|
+
|
1110
598
|
# @rbs inst: Instance
|
1111
599
|
# @rbs return: void
|
1112
600
|
def initialize(inst)
|
@@ -1168,6 +656,19 @@ module Wardite
|
|
1168
656
|
end
|
1169
657
|
end
|
1170
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
|
1171
672
|
end
|
1172
673
|
|
1173
674
|
# @rbs idx: Integer
|
@@ -1179,21 +680,57 @@ module Wardite
|
|
1179
680
|
class Memory
|
1180
681
|
attr_accessor :data #: String
|
1181
682
|
|
683
|
+
attr_accessor :current #: Integer
|
684
|
+
|
1182
685
|
attr_accessor :max #: Integer|nil
|
1183
686
|
|
1184
687
|
# @rbs min: Integer
|
1185
688
|
# @rbs max: Integer|nil
|
1186
689
|
# @rbs return: void
|
1187
690
|
def initialize(min, max)
|
1188
|
-
@data = String.new("\0" * (min * 64 * 1024)
|
691
|
+
@data = String.new("\0" * (min * 64 * 1024))
|
692
|
+
@current = min
|
1189
693
|
@max = max
|
1190
694
|
end
|
1191
695
|
|
696
|
+
# @rbs delta: Integer
|
697
|
+
# @rbs return: Integer
|
698
|
+
def grow(delta)
|
699
|
+
prev = current
|
700
|
+
newsize = current + delta
|
701
|
+
if max && (newsize > max)
|
702
|
+
return -1
|
703
|
+
end
|
704
|
+
|
705
|
+
@data += String.new("\0" * (delta * 64 * 1024))
|
706
|
+
prev
|
707
|
+
end
|
708
|
+
|
1192
709
|
def inspect
|
1193
710
|
"#<Wardite::Memory initial=#{@data.size.inspect} max=#{@max.inspect} @data=#{@data[0...64].inspect}...>"
|
1194
711
|
end
|
1195
712
|
end
|
1196
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
|
+
|
1197
734
|
class WasmData
|
1198
735
|
attr_accessor :memory_index #: Integer
|
1199
736
|
|
@@ -1317,6 +854,7 @@ module Wardite
|
|
1317
854
|
class LoadError < StandardError; end
|
1318
855
|
class ArgumentError < StandardError; end
|
1319
856
|
class EvalError < StandardError; end
|
857
|
+
class Unreachable < StandardError; end
|
1320
858
|
|
1321
859
|
# @rbs path: String|nil
|
1322
860
|
# @rbs buffer: File|StringIO|nil
|