wardite 0.7.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b2773f96babcc390d09312304046e661a1226b14413c307a3f9c082655cedb9
4
- data.tar.gz: 011022bf110c979bb68bf18bb513588e6fe152177d92d8b3e3d69a296a7d73f2
3
+ metadata.gz: 20aa59cedbb81d21c2681737929bb7f29fcf70cd189ca5d0de1ff538fca57782
4
+ data.tar.gz: fed9168236c3d23e377d38e2a6bf175336def008268aa5910215667ff3fac750
5
5
  SHA512:
6
- metadata.gz: d10a3156096c3610a2ac49d6b20d5814e81c106f01c01194b9ca1eda6ad746b9ee74d7755b141b617926e32142a0a62b3c959275ae4d53abc68bf4f527c9fada
7
- data.tar.gz: d5a1cad2ea0b9270e4b8adb0070495cefe2d8347bcaa1e85def5b4befe60a82800a73d9c02f8cfcb73f1546888a2a96c9caa853d5e3b0bd9fa608de770211bbd
6
+ metadata.gz: '098a4d2d2c25221667307bdf49fa207f682dd0f53e9ee33700cf18790961ce0ea627ed3f8ff7292a4cf55d70bf0c6068ed0b4b03502ec95aedd511c547dac7a0'
7
+ data.tar.gz: 9e1b69ba3cea02e033892ad4bf3fafc456d96d0f4741e8b0a9b6c55d2285cbb7d29fc0f0b2e71b1b35812c905b6281d1c4713c7b9215c5f99b42d94c88a6295b
@@ -11,18 +11,21 @@ module Wardite
11
11
  attr_reader :file #: String
12
12
  attr_reader :memsize #: Integer
13
13
  attr_reader :wasi #: bool
14
+ attr_reader :yjit #: bool
14
15
 
15
16
  # @rbs args: Array[String]
16
17
  # @rbs return: void
17
18
  def initialize(args)
18
19
  @invoke = @mapdir = nil
19
20
  @wasi = true # default to true
21
+ @yjit = false
20
22
  @memsize = 1
21
23
  options = OptionParser.new do |opts|
24
+ opts.version = Wardite::VERSION
22
25
  opts.on("--invoke [fnname]", "Invoke the function") { |v|
23
26
  @invoke = v
24
27
  }
25
- opts.on("--mapdir [dest[:src]]", "Map the directory") { |v|
28
+ opts.on("--mapdir [dirs]", "Map the directory") { |v|
26
29
  @mapdir = v
27
30
  }
28
31
  opts.on("--memsize [size_in_bytes]", "Initial memory size") { |v|
@@ -31,12 +34,19 @@ module Wardite
31
34
  opts.on("--no-wasi", "Disable WASI feature") {|_v|
32
35
  @wasi = false
33
36
  }
37
+ opts.on("--yjit", "Enable yjit if available; setting WARDITE_YJIT_ON=1 has the same effect") {|_v|
38
+ @yjit = true
39
+ }
34
40
  opts.on("FILE.wasm") { }
35
41
  end
36
42
  options.parse!(args)
37
43
  @file = args[0] || raise("require file argument")
38
44
  @args = (args[1..-1] || [])
39
45
  @args.unshift if @args[0] == '--'
46
+
47
+ if (yjit || ENV["WARDITE_YJIT_ON"] == "1") && (defined? RubyVM::YJIT)
48
+ RubyVM::YJIT.enable
49
+ end
40
50
  end
41
51
 
42
52
  # @rbs return: Array[Integer | Float]
@@ -66,7 +76,10 @@ module Wardite
66
76
  if invoke
67
77
  invoke_function
68
78
  else
69
- invoke_wasi if wasi
79
+ if wasi
80
+ invoke_wasi
81
+ return
82
+ end
70
83
  raise("requires function name to invoke")
71
84
  end
72
85
  end
@@ -91,6 +104,7 @@ module Wardite
91
104
  if mapdir && mount_dst && mount_src
92
105
  # TODO: support multiple mapdir
93
106
  instance.wasi.mapdir[mount_dst] = mount_src
107
+ instance.wasi.set_preopened_dir(mount_dst, mount_src)
94
108
  end
95
109
 
96
110
  if defined? Bundler
@@ -99,7 +113,7 @@ module Wardite
99
113
  end
100
114
  else
101
115
  instance.runtime._start
102
- end
116
+ end
103
117
  end
104
118
 
105
119
  # @rbs return: String?
data/lib/wardite/value.rb CHANGED
@@ -383,7 +383,7 @@ module Wardite
383
383
  raise EvalError, "unsupported operation" if to != :f64
384
384
  v = [value].pack("L!").unpack("d")[0]
385
385
  raise EvalError, "[BUG] String#unpack is broke, really?" if !v.is_a?(Float)
386
- F32(v)
386
+ F64(v)
387
387
  end
388
388
 
389
389
  # @rbs from: Symbol
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Wardite
5
- VERSION = "0.7.0" #: String
5
+ VERSION = "0.8.0" #: String
6
6
  end
@@ -103,6 +103,13 @@ module Wardite
103
103
  RIGHT_PATH_REMOVE_DIRECTORY |
104
104
  RIGHT_PATH_UNLINK_FILE
105
105
 
106
+ LOOKUP_SYMLINK_FOLLOW = 1 << 0
107
+
108
+ O_CREAT = 1 << 0
109
+ O_DIRECTORY = 1 << 1
110
+ O_EXCL = 1 << 2
111
+ O_TRUNC = 1 << 3
112
+
106
113
  # @rbs mode_str: String
107
114
  # @rbs return: Integer
108
115
  def self.to_ftype(mode_str)
@@ -0,0 +1,87 @@
1
+ # rbs_inline: enabled
2
+
3
+ module Wardite
4
+ module Wasi
5
+ class Dirent
6
+ attr_reader :path #: String
7
+ attr_reader :ino #: Integer
8
+ attr_reader :type #: Integer
9
+
10
+ # @rbs path: String
11
+ # @rbs ino: Integer
12
+ # @rbs type: Integer
13
+ # @rbs return: void
14
+ def initialize(path, ino, type)
15
+ @path = path
16
+ @ino = ino
17
+ @type = type
18
+ end
19
+ end
20
+
21
+ class DirentCache
22
+ attr_reader :entries #: Array[Dirent]
23
+ attr_accessor :eof #: bool
24
+
25
+ # @rbs path: String
26
+ # @rbs return: void
27
+ def initialize(path)
28
+ @entries = []
29
+ Dir.entries(path).sort.each do |entry|
30
+ case entry
31
+ when "."
32
+ @entries << Dirent.new(entry, File.stat(path).ino, FILETYPE_DIRECTORY)
33
+ when ".."
34
+ @entries << Dirent.new(entry, 0, FILETYPE_DIRECTORY)
35
+ else
36
+ full_path = File.join(path, entry)
37
+ type = case File.ftype(full_path)
38
+ when "directory" then FILETYPE_DIRECTORY
39
+ when "file" then FILETYPE_REGULAR_FILE
40
+ when "link" then FILETYPE_SYMBOLIC_LINK
41
+ else FILETYPE_UNKNOWN
42
+ end
43
+ @entries << Dirent.new(entry, File.stat(full_path).ino, type)
44
+ end
45
+ end
46
+
47
+ @eof = false
48
+ end
49
+
50
+ # @rbs buf_len: Integer
51
+ # @rbs cookie: Integer
52
+ # @rbs return: [String, bool]
53
+ def fetch_entries_binary(buf_len, cookie)
54
+ # d_next is the index of the next file in the list, so it should
55
+ # always be one higher than the requested cookie.
56
+ d_next = cookie + 1
57
+ buf = ""
58
+ entries_slice = entries[cookie..-1]
59
+ return "", false if entries_slice.nil? || entries_slice.empty?
60
+
61
+ entries_slice.each do |entry|
62
+ data = make_dirent_pack1(d_next, entry.ino, entry.path.size, entry.type, entry.path)
63
+ if buf.size + data.size > buf_len
64
+ # truncated
65
+ return buf, true
66
+ end
67
+ buf += data
68
+ d_next += 1
69
+ end
70
+
71
+ return buf, false
72
+ end
73
+
74
+ # @rbs d_next: Integer
75
+ # @rbs ino: Integer
76
+ # @rbs name_len: Integer
77
+ # @rbs type: Integer
78
+ # @rbs name: String
79
+ # @rbs return: String
80
+ def make_dirent_pack1(d_next, ino, name_len, type, name)
81
+ data = [d_next, ino, name_len, type].pack("Q! Q! I! I!")
82
+ data += name
83
+ data
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,17 @@
1
+ # rbs_inline: enabled
2
+
3
+ module Wardite
4
+ module Wasi
5
+ class PreopenedDir
6
+ attr_reader :path #: String
7
+ attr_reader :guest_path #: String
8
+ attr_reader :fd #: Integer
9
+
10
+ def initialize(path, guest_path, fd)
11
+ @path = path
12
+ @guest_path = guest_path
13
+ @fd = fd
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/wardite/wasi.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "wardite/wasm_module"
4
4
  require "wardite/wasi/consts"
5
+ require "wardite/wasi/preopens"
6
+ require "wardite/wasi/dirent_cache"
5
7
  require "securerandom"
6
8
  require "fcntl"
7
9
 
@@ -10,21 +12,136 @@ module Wardite
10
12
  include ValueHelper
11
13
  include WasmModule
12
14
 
13
- attr_accessor :fd_table #: Array[(IO|File)]
15
+ attr_accessor :fd_table #: Hash[Integer, (IO|File|::Wardite::Wasi::PreopenedDir)]
16
+ attr_accessor :fd_count #: Integer
14
17
  attr_accessor :argv #: Array[String]
15
18
  attr_accessor :mapdir #: Hash[String, String]
19
+ attr_accessor :dirent_cache #: Hash[Integer, ::Wardite::Wasi::DirentCache]
16
20
 
17
21
  # @rbs argv: Array[String]
18
22
  # @rbs mapdir: Hash[String, String]
19
23
  # @rbs return: void
20
24
  def initialize(argv: [], mapdir: {})
21
- @fd_table = [
22
- STDIN,
23
- STDOUT,
24
- STDERR,
25
- ]
25
+ @fd_table = {
26
+ 0 => STDIN,
27
+ 1 => STDOUT,
28
+ 2 => STDERR,
29
+ }
30
+ @fd_count = 3
26
31
  @argv = argv
27
32
  @mapdir = mapdir
33
+
34
+ @dirent_cache = {}
35
+ end
36
+
37
+ # @rbs file: IO|File|::Wardite::Wasi::PreopenedDir
38
+ # @rbs return: Integer
39
+ def set_fd(file)
40
+ fd = @fd_count
41
+ @fd_table[fd] = file
42
+ @fd_count += 1
43
+ fd
44
+ end
45
+
46
+ # @rbs path: String
47
+ # @rbs guest_path: String
48
+ # @rbs return: void
49
+ def set_preopened_dir(path, guest_path)
50
+ fd = @fd_count
51
+ set_fd(::Wardite::Wasi::PreopenedDir.new(path, guest_path, fd))
52
+ end
53
+
54
+ # @rbs orig_path: String
55
+ # @rbs return: String
56
+ def resolv_path(orig_path)
57
+ @mapdir.each do |k, v|
58
+ if orig_path.start_with?(k)
59
+ return v + orig_path[k.size..-1].to_s
60
+ end
61
+ end
62
+
63
+ return orig_path
64
+ end
65
+
66
+ # @rbs atfd: Integer
67
+ # @rbs target: String
68
+ # @rbs return: String
69
+ def get_path_at(atfd, target)
70
+ target = resolv_path(target)
71
+
72
+ at = @fd_table[atfd]
73
+ pwd = case at
74
+ when Wasi::PreopenedDir
75
+ at.guest_path
76
+ when File
77
+ Dir.fchdir(at.fileno) do
78
+ Dir.pwd
79
+ end
80
+ else
81
+ raise ArgumentError, "invalid fd: #{atfd}"
82
+ end
83
+
84
+ ret = File.expand_path(target, pwd)
85
+ ret
86
+ end
87
+
88
+ # @rbs dirflags: Integer
89
+ # @rbs oflags: Integer
90
+ # @rbs fdflags: Integer
91
+ # @rbs rights: Integer
92
+ def interpret_open_flags(dirflags, oflags, fdflags, rights)
93
+ open_flags = 0
94
+ if dirflags & Wasi::LOOKUP_SYMLINK_FOLLOW == 0
95
+ open_flags |= File::Constants::NOFOLLOW
96
+ end
97
+ if oflags & Wasi::O_DIRECTORY != 0
98
+ # open_flags |= File::Constants::DIRECTORY
99
+ $stderr.puts "O_DIRECTORY is not supported, ignore" if ENV["WARDITE_TRACE"]
100
+ elsif oflags & Wasi::O_EXCL != 0
101
+ open_flags |= File::Constants::EXCL
102
+ end
103
+
104
+ default_mode = File::Constants::RDONLY
105
+ if oflags & Wasi::O_TRUNC != 0
106
+ open_flags |= File::Constants::TRUNC
107
+ default_mode = File::Constants::RDWR
108
+ end
109
+ if oflags & Wasi::O_CREAT != 0
110
+ open_flags |= File::Constants::CREAT
111
+ default_mode = File::Constants::RDWR
112
+ end
113
+ if fdflags & Wasi::FD_NONBLOCK != 0
114
+ open_flags |= File::Constants::NONBLOCK
115
+ end
116
+ if fdflags & Wasi::FD_APPEND != 0
117
+ open_flags |= File::Constants::APPEND
118
+ default_mode = File::Constants::RDWR
119
+ end
120
+ if fdflags & Wasi::FD_DSYNC != 0
121
+ open_flags |= File::Constants::DSYNC
122
+ end
123
+ if fdflags & Wasi::FD_RSYNC != 0
124
+ open_flags |= File::Constants::RSYNC
125
+ end
126
+ if fdflags & Wasi::FD_SYNC != 0
127
+ open_flags |= File::Constants::SYNC
128
+ end
129
+
130
+ r = Wasi::RIGHT_FD_READ
131
+ w = Wasi::RIGHT_FD_WRITE
132
+ rw = r | w
133
+ case
134
+ when (rights & rw) == rw
135
+ open_flags |= File::Constants::RDWR
136
+ when (rights & w) == w
137
+ open_flags |= File::Constants::WRONLY
138
+ when (rights & r) == r
139
+ open_flags |= File::Constants::RDONLY
140
+ else
141
+ open_flags |= default_mode
142
+ end
143
+
144
+ open_flags
28
145
  end
29
146
 
30
147
  # @rbs store: Store
@@ -140,17 +257,216 @@ module Wardite
140
257
  0
141
258
  end
142
259
 
260
+ # @rbs store: Store
261
+ # @rbs args: Array[wasmValue]
262
+ # @rbs return: Object
263
+ def path_create_directory(store, args)
264
+ fd = args[0].value.to_i
265
+ path = args[1].value.to_i
266
+ path_len = args[2].value.to_i
267
+ path_str = store.memories[0].data[path...(path+path_len)]
268
+ if !path_str
269
+ return Wasi::ENOENT
270
+ end
271
+
272
+ target = get_path_at(fd, path_str)
273
+ Dir.mkdir(target, 0700)
274
+ 0
275
+ # TODO; rescue EBADF, ENOTDIR
276
+ end
277
+
278
+ # @rbs store: Store
279
+ # @rbs args: Array[wasmValue]
280
+ # @rbs return: Object
281
+ def path_filestat_get(store, args)
282
+ fd = args[0].value.to_i
283
+ flags = args[1].value.to_i
284
+ path = args[2].value.to_i
285
+ path_len = args[3].value.to_i
286
+ target = get_path_at(fd, store.memories[0].data[path...(path+path_len)].to_s)
287
+
288
+ stat = File.stat(target)
289
+ memory = store.memories[0]
290
+ binformat = [
291
+ stat.dev, stat.ino, Wasi.to_ftype(stat.ftype), stat.nlink,
292
+ stat.size, stat.atime.to_i, stat.mtime.to_i, stat.ctime.to_i
293
+ ].pack("Q8")
294
+ memory.data[flags...(flags+binformat.size)] = binformat
295
+ 0
296
+ rescue Errno::ENOENT
297
+ return Wasi::ENOENT
298
+ end
299
+
300
+ # @rbs store: Store
301
+ # @rbs args: Array[wasmValue]
302
+ # @rbs return: Object
303
+ def path_filestat_set_times(store, args)
304
+ fd = args[0].value.to_i
305
+ # TODO: flags support
306
+ _flags = args[1].value.to_i
307
+ path = args[2].value.to_i
308
+ path_len = args[3].value.to_i
309
+ atim = args[4].value.to_i # nanoseconds
310
+ mtim = args[5].value.to_i # nanoseconds
311
+ target = get_path_at(fd, store.memories[0].data[path...(path+path_len)].to_s)
312
+
313
+ atime = Time.at(atim.to_f / 1_000_000_000)
314
+ mtime = Time.at(mtim.to_f / 1_000_000_000)
315
+ File.utime(atime, mtime, target)
316
+ 0
317
+ end
318
+
319
+ # @rbs store: Store
320
+ # @rbs args: Array[wasmValue]
321
+ # @rbs return: Object
322
+ def path_link(store, args)
323
+ old_fd = args[0].value.to_i
324
+ old_path = args[1].value.to_i
325
+ old_path_len = args[2].value.to_i
326
+ old_name = get_path_at(old_fd, store.memories[0].data[old_path...(old_path+old_path_len)].to_s)
327
+
328
+ new_fd = args[3].value.to_i
329
+ new_path = args[4].value.to_i
330
+ new_path_len = args[5].value.to_i
331
+ new_name = get_path_at(new_fd, store.memories[0].data[new_path...(new_path+new_path_len)].to_s)
332
+
333
+ File.link(old_name, new_name)
334
+ 0
335
+ end
336
+
337
+ # @rbs store: Store
338
+ # @rbs args: Array[wasmValue]
339
+ # @rbs return: Object
340
+ def path_open(store, args)
341
+ dirfd = args[0].value.to_i
342
+ dirflags = args[1].value.to_i
343
+ path = args[2].value.to_i
344
+ path_len = args[3].value.to_i
345
+ oflags = args[4].value.to_i
346
+ fs_rights_base = args[5].value.to_i
347
+ _fs_rights_inheriting = args[6].value.to_i
348
+ fs_flags = args[7].value.to_i
349
+ fd_off = args[8].value.to_i
350
+
351
+ path_name = get_path_at(dirfd, store.memories[0].data[path...(path+path_len)].to_s)
352
+ open_flags = interpret_open_flags(dirflags, oflags, fs_flags, fs_rights_base)
353
+ is_dir = (oflags & Wasi::O_DIRECTORY) != 0
354
+ if is_dir && (oflags & Wasi::O_CREAT) != 0
355
+ return Wasi::EINVAL
356
+ end
357
+
358
+ file = File.open(path_name, open_flags, 0600)
359
+ fd = set_fd file
360
+
361
+ memory = store.memories[0]
362
+ memory.data[fd_off...(fd_off+4)] = [fd].pack("I!")
363
+ 0
364
+ rescue Errno::ENOENT
365
+ return Wasi::ENOENT
366
+ end
367
+
368
+ # @rbs store: Store
369
+ # @rbs args: Array[wasmValue]
370
+ # @rbs return: Object
371
+ def path_readlink(store, args)
372
+ fd = args[0].value.to_i
373
+ path = args[1].value.to_i
374
+ path_len = args[2].value.to_i
375
+ buf = args[3].value.to_i
376
+ buf_len = args[4].value.to_i
377
+ result_buf = args[5].value.to_i
378
+
379
+ if buf_len <= 0 || path_len <= 0
380
+ return Wasi::EINVAL
381
+ end
382
+ target = get_path_at(fd, store.memories[0].data[path...(path+path_len)].to_s)
383
+
384
+ link_target = File.readlink(target)
385
+ if link_target.size > buf_len
386
+ return Wasi::ENAMETOOLONG
387
+ end
388
+
389
+ memory = store.memories[0]
390
+ memory.data[buf...(buf+link_target.size)] = link_target
391
+ memory.data[result_buf...(result_buf+4)] = [link_target.size].pack("I!")
392
+ 0
393
+ end
394
+
395
+ # @rbs store: Store
396
+ # @rbs args: Array[wasmValue]
397
+ # @rbs return: Object
398
+ def path_remove_directory(store, args)
399
+ fd = args[0].value.to_i
400
+ path = args[1].value.to_i
401
+ path_len = args[2].value.to_i
402
+ path_str = store.memories[0].data[path...(path+path_len)].to_s
403
+ target = get_path_at(fd, path_str)
404
+
405
+ Dir.rmdir(target)
406
+ 0
407
+ end
408
+
409
+ # @rbs store: Store
410
+ # @rbs args: Array[wasmValue]
411
+ # @rbs return: Object
412
+ def path_rename(store, args)
413
+ fd = args[0].value.to_i
414
+ old_path = args[1].value.to_i
415
+ old_path_len = args[2].value.to_i
416
+
417
+ new_fd = args[3].value.to_i
418
+ new_path = args[4].value.to_i
419
+ new_path_len = args[5].value.to_i
420
+
421
+ old_target = get_path_at(fd, store.memories[0].data[old_path...(old_path+old_path_len)].to_s)
422
+ new_target = get_path_at(new_fd, store.memories[0].data[new_path...(new_path+new_path_len)].to_s)
423
+
424
+ File.rename(old_target, new_target)
425
+ 0
426
+ end
427
+
428
+ # @rbs store: Store
429
+ # @rbs args: Array[wasmValue]
430
+ # @rbs return: Object
431
+ def path_symlink(store, args)
432
+ old_path = args[0].value.to_i
433
+ old_path_len = args[1].value.to_i
434
+ old_name = store.memories[0].data[old_path...(old_path+old_path_len)].to_s
435
+
436
+ fd = args[2].value.to_i
437
+ new_path = args[3].value.to_i
438
+ new_path_len = args[4].value.to_i
439
+ new_name = get_path_at(fd, store.memories[0].data[new_path...(new_path+new_path_len)].to_s)
440
+
441
+ File.symlink(old_name, new_name)
442
+ 0
443
+ end
444
+
445
+ # @rbs store: Store
446
+ # @rbs args: Array[wasmValue]
447
+ # @rbs return: Object
448
+ def path_unlink_file(store, args)
449
+ fd = args[0].value.to_i
450
+ path = args[1].value.to_i
451
+ path_len = args[2].value.to_i
452
+ path_str = store.memories[0].data[path...(path+path_len)].to_s
453
+ target = get_path_at(fd, path_str)
454
+
455
+ File.unlink(target)
456
+ 0
457
+ end
458
+
143
459
  # @rbs store: Store
144
460
  # @rbs args: Array[wasmValue]
145
461
  # @rbs return: Object
146
462
  def fd_prestat_get(store, args)
147
463
  fd = args[0].value.to_i
148
464
  prestat_offset = args[1].value.to_i
149
- if fd >= @fd_table.size
465
+ if fd >= fd_count
150
466
  return Wasi::EBADF
151
467
  end
152
468
  file = @fd_table[fd]
153
- if !file.is_a?(File)
469
+ if !file.is_a?(Wasi::PreopenedDir)
154
470
  return Wasi::EBADF
155
471
  end
156
472
  name = file.path
@@ -161,6 +477,31 @@ module Wardite
161
477
  0
162
478
  end
163
479
 
480
+ # @rbs store: Store
481
+ # @rbs args: Array[wasmValue]
482
+ # @rbs return: Object
483
+ def fd_prestat_dir_name(store, args)
484
+ fd = args[0].value.to_i
485
+ path = args[1].value.to_i
486
+ path_len = args[2].value.to_i
487
+ if fd >= fd_count
488
+ return Wasi::EBADF
489
+ end
490
+ file = @fd_table[fd]
491
+ if !file.is_a?(Wasi::PreopenedDir)
492
+ return Wasi::EBADF
493
+ end
494
+ name = file.path
495
+ if name.size > path_len
496
+ return Wasi::ENAMETOOLONG
497
+ end
498
+ name += ("\0" * (path_len - name.size))
499
+
500
+ memory = store.memories[0]
501
+ memory.data[path...(path+name.size)] = name
502
+ 0
503
+ end
504
+
164
505
  # @rbs store: Store
165
506
  # @rbs args: Array[wasmValue]
166
507
  # @rbs return: Object
@@ -177,7 +518,7 @@ module Wardite
177
518
  raise Wardite::ArgumentError, "args too short"
178
519
  end
179
520
  file = self.fd_table[fd]
180
- return Wasi::EBADF if !file
521
+ return Wasi::EBADF if !file || file.is_a?(Wasi::PreopenedDir)
181
522
  memory = store.memories[0]
182
523
  nwritten = 0
183
524
  iovs_len.times do
@@ -210,7 +551,7 @@ module Wardite
210
551
  raise Wardite::ArgumentError, "args too short"
211
552
  end
212
553
  file = self.fd_table[fd]
213
- return Wasi::EBADF if !file
554
+ return Wasi::EBADF if !file || file.is_a?(Wasi::PreopenedDir)
214
555
  memory = store.memories[0]
215
556
  nread = 0
216
557
 
@@ -221,10 +562,10 @@ module Wardite
221
562
  iovs += 4
222
563
  buf = file.read(slen)
223
564
  if !buf
224
- return Wasi::EFAULT
565
+ break 0
225
566
  end
226
- memory.data[start...(start+slen)] = buf
227
- nread += slen
567
+ memory.data[start...(start+buf.size)] = buf
568
+ nread += buf.size
228
569
  end
229
570
 
230
571
  memory.data[rp...(rp+4)] = [nread].pack("I!")
@@ -237,12 +578,16 @@ module Wardite
237
578
  def fd_fdstat_get(store, args)
238
579
  fd = args[0].value.to_i
239
580
  fdstat_offset = args[1].value.to_i
240
- if fd >= @fd_table.size
581
+ if fd >= fd_count
241
582
  return Wasi::EBADF
242
583
  end
243
584
  file = @fd_table[fd]
244
585
  fdflags = 0
245
- if file.is_a?(IO)
586
+ if !file
587
+ return Wasi::EBADF
588
+ elsif file.is_a?(Wasi::PreopenedDir)
589
+ file = File.open(file.guest_path) # reopen directory
590
+ elsif file.is_a?(IO) && !file.is_a?(File)
246
591
  fdflags |= Wasi::FD_APPEND
247
592
  else
248
593
  if (Fcntl::O_APPEND & file.fcntl(Fcntl::F_GETFL, 0)) != 0
@@ -285,10 +630,14 @@ module Wardite
285
630
  def fd_filestat_get(store, args)
286
631
  fd = args[0].value.to_i
287
632
  filestat_offset = args[1].value.to_i
288
- if fd >= @fd_table.size
633
+ if fd >= fd_count
289
634
  return Wasi::EBADF
290
635
  end
291
636
  file = @fd_table[fd]
637
+ if !file || file.is_a?(Wasi::PreopenedDir)
638
+ return Wasi::EBADF
639
+ end
640
+
292
641
  stat = file.stat #: File::Stat
293
642
  memory = store.memories[0]
294
643
  binformat = [stat.dev, stat.ino, Wasi.to_ftype(stat.ftype), stat.nlink, stat.size, stat.atime.to_i, stat.mtime.to_i, stat.ctime.to_i].pack("Q8")
@@ -296,6 +645,94 @@ module Wardite
296
645
  0
297
646
  end
298
647
 
648
+ # @rbs store: Store
649
+ # @rbs args: Array[wasmValue]
650
+ # @rbs return: Object
651
+ def fd_readdir(store, args)
652
+ fd = args[0].value.to_i
653
+ buf = args[1].value.to_i
654
+ buf_len = args[2].value.to_i
655
+ cookie = args[3].value.to_i
656
+ result_buf_used = args[4].value.to_i
657
+
658
+ if buf_len < 24 # when smaller than Dirent header size: Q! Q! I! I!
659
+ return Wasi::EINVAL
660
+ end
661
+
662
+ memory = store.memories[0]
663
+
664
+ if dirent_cache[fd]&.eof
665
+ dirent_cache.delete(fd)
666
+ end
667
+ dir = @fd_table[fd]
668
+ if dir.is_a?(Wasi::PreopenedDir) || (dir.is_a?(File) && dir.stat.ftype == "directory")
669
+ dirent_cache[fd] ||= Wasi::DirentCache.new(dir.path)
670
+ else
671
+ return Wasi::EBADF
672
+ end
673
+
674
+ dirent = dirent_cache[fd]
675
+ bindata, is_truncated = dirent.fetch_entries_binary(buf_len, cookie)
676
+
677
+ bufused = bindata.size
678
+ # bufused == buf_len means more dirents exist
679
+ if is_truncated
680
+ bufused = buf_len
681
+ memory.data[buf...(buf+buf_len)] = bindata + "\0" * (buf_len - bindata.size)
682
+ else
683
+ dirent.eof = true
684
+ memory.data[buf...(buf+bindata.size)] = bindata
685
+ end
686
+
687
+ memory.data[result_buf_used...(result_buf_used+4)] = [bufused].pack("I!")
688
+ 0
689
+ end
690
+
691
+ # @rbs store: Store
692
+ # @rbs args: Array[wasmValue]
693
+ def fd_tell(store, args)
694
+ fd = args[0].value.to_i
695
+ result_buf = args[1].value.to_i
696
+ if fd >= fd_count
697
+ return Wasi::EBADF
698
+ end
699
+ file = @fd_table[fd]
700
+ if !file || file.is_a?(Wasi::PreopenedDir)
701
+ return Wasi::EBADF
702
+ end
703
+
704
+ memory = store.memories[0]
705
+ memory.data[result_buf...(result_buf+4)] = [file.tell].pack("I!")
706
+ 0
707
+ rescue Errno::EBADF
708
+ return Wasi::EBADF
709
+ end
710
+
711
+ # @rbs store: Store
712
+ # @rbs args: Array[wasmValue]
713
+ # @rbs return: Object
714
+ def fd_close(store, args)
715
+ fd = args[0].value.to_i
716
+ if fd >= fd_count
717
+ return Wasi::EBADF
718
+ end
719
+ file = @fd_table[fd]
720
+ if !file
721
+ return Wasi::EBADF
722
+ end
723
+ if file.is_a?(Wasi::PreopenedDir)
724
+ # do nothing for preopened dir...?
725
+ $stderr.puts "close preopened dir?: #{file.guest_path}"
726
+ @fd_table.delete(fd)
727
+ return 0
728
+ end
729
+ file.close
730
+ @fd_table.delete(fd)
731
+ 0
732
+ rescue Errno::EBADF
733
+ return Wasi::EBADF
734
+ end
735
+
299
736
  # @rbs store: Store
300
737
  # @rbs args: Array[wasmValue]
301
738
  # @rbs return: Object
data/lib/wardite.rb CHANGED
@@ -354,6 +354,7 @@ module Wardite
354
354
  # @rbs external_function: ExternalFunction
355
355
  # @rbs return: wasmValue|nil
356
356
  def invoke_external(external_function)
357
+ $stderr.puts "[trace] call external function: #{external_function.name}" if ENV["WARDITE_TRACE"]
357
358
  local_start = stack.size - external_function.callsig.size
358
359
  args = stack[local_start..]
359
360
  if !args
@@ -15,6 +15,8 @@ module Wardite
15
15
 
16
16
  attr_reader wasi: bool
17
17
 
18
+ attr_reader yjit: bool
19
+
18
20
  # @rbs args: Array[String]
19
21
  # @rbs return: void
20
22
  def initialize: (Array[String] args) -> void
@@ -130,6 +130,16 @@ module Wardite
130
130
 
131
131
  RIGHT_DIR_RIGHT_BASE: untyped
132
132
 
133
+ LOOKUP_SYMLINK_FOLLOW: untyped
134
+
135
+ O_CREAT: untyped
136
+
137
+ O_DIRECTORY: untyped
138
+
139
+ O_EXCL: untyped
140
+
141
+ O_TRUNC: untyped
142
+
133
143
  # @rbs mode_str: String
134
144
  # @rbs return: Integer
135
145
  def self.to_ftype: (String mode_str) -> Integer
@@ -0,0 +1,42 @@
1
+ # Generated from lib/wardite/wasi/dirent_cache.rb with RBS::Inline
2
+
3
+ module Wardite
4
+ module Wasi
5
+ class Dirent
6
+ attr_reader path: String
7
+
8
+ attr_reader ino: Integer
9
+
10
+ attr_reader type: Integer
11
+
12
+ # @rbs path: String
13
+ # @rbs ino: Integer
14
+ # @rbs type: Integer
15
+ # @rbs return: void
16
+ def initialize: (String path, Integer ino, Integer type) -> void
17
+ end
18
+
19
+ class DirentCache
20
+ attr_reader entries: Array[Dirent]
21
+
22
+ attr_accessor eof: bool
23
+
24
+ # @rbs path: String
25
+ # @rbs return: void
26
+ def initialize: (String path) -> void
27
+
28
+ # @rbs buf_len: Integer
29
+ # @rbs cookie: Integer
30
+ # @rbs return: [String, bool]
31
+ def fetch_entries_binary: (Integer buf_len, Integer cookie) -> [ String, bool ]
32
+
33
+ # @rbs d_next: Integer
34
+ # @rbs ino: Integer
35
+ # @rbs name_len: Integer
36
+ # @rbs type: Integer
37
+ # @rbs name: String
38
+ # @rbs return: String
39
+ def make_dirent_pack1: (Integer d_next, Integer ino, Integer name_len, Integer type, String name) -> String
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ # Generated from lib/wardite/wasi/preopens.rb with RBS::Inline
2
+
3
+ module Wardite
4
+ module Wasi
5
+ class PreopenedDir
6
+ attr_reader path: String
7
+
8
+ attr_reader guest_path: String
9
+
10
+ attr_reader fd: Integer
11
+
12
+ def initialize: (untyped path, untyped guest_path, untyped fd) -> untyped
13
+ end
14
+ end
15
+ end
@@ -6,17 +6,45 @@ module Wardite
6
6
 
7
7
  include WasmModule
8
8
 
9
- attr_accessor fd_table: Array[IO | File]
9
+ attr_accessor fd_table: Hash[Integer, IO | File | ::Wardite::Wasi::PreopenedDir]
10
+
11
+ attr_accessor fd_count: Integer
10
12
 
11
13
  attr_accessor argv: Array[String]
12
14
 
13
15
  attr_accessor mapdir: Hash[String, String]
14
16
 
17
+ attr_accessor dirent_cache: Hash[Integer, ::Wardite::Wasi::DirentCache]
18
+
15
19
  # @rbs argv: Array[String]
16
20
  # @rbs mapdir: Hash[String, String]
17
21
  # @rbs return: void
18
22
  def initialize: (?argv: Array[String], ?mapdir: Hash[String, String]) -> void
19
23
 
24
+ # @rbs file: IO|File|::Wardite::Wasi::PreopenedDir
25
+ # @rbs return: Integer
26
+ def set_fd: (IO | File | ::Wardite::Wasi::PreopenedDir file) -> Integer
27
+
28
+ # @rbs path: String
29
+ # @rbs guest_path: String
30
+ # @rbs return: void
31
+ def set_preopened_dir: (String path, String guest_path) -> void
32
+
33
+ # @rbs orig_path: String
34
+ # @rbs return: String
35
+ def resolv_path: (String orig_path) -> String
36
+
37
+ # @rbs atfd: Integer
38
+ # @rbs target: String
39
+ # @rbs return: String
40
+ def get_path_at: (Integer atfd, String target) -> String
41
+
42
+ # @rbs dirflags: Integer
43
+ # @rbs oflags: Integer
44
+ # @rbs fdflags: Integer
45
+ # @rbs rights: Integer
46
+ def interpret_open_flags: (Integer dirflags, Integer oflags, Integer fdflags, Integer rights) -> untyped
47
+
20
48
  # @rbs store: Store
21
49
  # @rbs args: Array[wasmValue]
22
50
  # @rbs return: Object
@@ -42,11 +70,66 @@ module Wardite
42
70
  # @rbs return: Object
43
71
  def clock_time_get: (Store store, Array[wasmValue] args) -> Object
44
72
 
73
+ # @rbs store: Store
74
+ # @rbs args: Array[wasmValue]
75
+ # @rbs return: Object
76
+ def path_create_directory: (Store store, Array[wasmValue] args) -> Object
77
+
78
+ # @rbs store: Store
79
+ # @rbs args: Array[wasmValue]
80
+ # @rbs return: Object
81
+ def path_filestat_get: (Store store, Array[wasmValue] args) -> Object
82
+
83
+ # @rbs store: Store
84
+ # @rbs args: Array[wasmValue]
85
+ # @rbs return: Object
86
+ def path_filestat_set_times: (Store store, Array[wasmValue] args) -> Object
87
+
88
+ # @rbs store: Store
89
+ # @rbs args: Array[wasmValue]
90
+ # @rbs return: Object
91
+ def path_link: (Store store, Array[wasmValue] args) -> Object
92
+
93
+ # @rbs store: Store
94
+ # @rbs args: Array[wasmValue]
95
+ # @rbs return: Object
96
+ def path_open: (Store store, Array[wasmValue] args) -> Object
97
+
98
+ # @rbs store: Store
99
+ # @rbs args: Array[wasmValue]
100
+ # @rbs return: Object
101
+ def path_readlink: (Store store, Array[wasmValue] args) -> Object
102
+
103
+ # @rbs store: Store
104
+ # @rbs args: Array[wasmValue]
105
+ # @rbs return: Object
106
+ def path_remove_directory: (Store store, Array[wasmValue] args) -> Object
107
+
108
+ # @rbs store: Store
109
+ # @rbs args: Array[wasmValue]
110
+ # @rbs return: Object
111
+ def path_rename: (Store store, Array[wasmValue] args) -> Object
112
+
113
+ # @rbs store: Store
114
+ # @rbs args: Array[wasmValue]
115
+ # @rbs return: Object
116
+ def path_symlink: (Store store, Array[wasmValue] args) -> Object
117
+
118
+ # @rbs store: Store
119
+ # @rbs args: Array[wasmValue]
120
+ # @rbs return: Object
121
+ def path_unlink_file: (Store store, Array[wasmValue] args) -> Object
122
+
45
123
  # @rbs store: Store
46
124
  # @rbs args: Array[wasmValue]
47
125
  # @rbs return: Object
48
126
  def fd_prestat_get: (Store store, Array[wasmValue] args) -> Object
49
127
 
128
+ # @rbs store: Store
129
+ # @rbs args: Array[wasmValue]
130
+ # @rbs return: Object
131
+ def fd_prestat_dir_name: (Store store, Array[wasmValue] args) -> Object
132
+
50
133
  # @rbs store: Store
51
134
  # @rbs args: Array[wasmValue]
52
135
  # @rbs return: Object
@@ -67,6 +150,20 @@ module Wardite
67
150
  # @rbs return: Object
68
151
  def fd_filestat_get: (Store store, Array[wasmValue] args) -> Object
69
152
 
153
+ # @rbs store: Store
154
+ # @rbs args: Array[wasmValue]
155
+ # @rbs return: Object
156
+ def fd_readdir: (Store store, Array[wasmValue] args) -> Object
157
+
158
+ # @rbs store: Store
159
+ # @rbs args: Array[wasmValue]
160
+ def fd_tell: (Store store, Array[wasmValue] args) -> untyped
161
+
162
+ # @rbs store: Store
163
+ # @rbs args: Array[wasmValue]
164
+ # @rbs return: Object
165
+ def fd_close: (Store store, Array[wasmValue] args) -> Object
166
+
70
167
  # @rbs store: Store
71
168
  # @rbs args: Array[wasmValue]
72
169
  # @rbs return: Object
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wardite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uchio Kondo
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-10 00:00:00.000000000 Z
10
+ date: 2025-04-04 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: A pure-ruby webassembly runtime
13
13
  email:
@@ -58,6 +58,8 @@ files:
58
58
  - lib/wardite/version.rb
59
59
  - lib/wardite/wasi.rb
60
60
  - lib/wardite/wasi/consts.rb
61
+ - lib/wardite/wasi/dirent_cache.rb
62
+ - lib/wardite/wasi/preopens.rb
61
63
  - lib/wardite/wasm_module.rb
62
64
  - misc/bench-value.rb
63
65
  - misc/slides/fib.c
@@ -92,6 +94,8 @@ files:
92
94
  - sig/generated/wardite/version.rbs
93
95
  - sig/generated/wardite/wasi.rbs
94
96
  - sig/generated/wardite/wasi/consts.rbs
97
+ - sig/generated/wardite/wasi/dirent_cache.rbs
98
+ - sig/generated/wardite/wasi/preopens.rbs
95
99
  - sig/generated/wardite/wasm_module.rbs
96
100
  - sig/wardite.rbs
97
101
  homepage: https://github.com/udzura/wardite