wardite 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wardite/wasi.rb CHANGED
@@ -1,16 +1,161 @@
1
1
  # rbs_inline: enabled
2
+
3
+ require "wardite/wasm_module"
4
+ require "wardite/wasi/consts"
5
+ require "securerandom"
6
+ require "fcntl"
7
+
2
8
  module Wardite
3
9
  class WasiSnapshotPreview1
4
10
  include ValueHelper
11
+ include WasmModule
5
12
 
6
- attr_accessor :fd_table #: Array[IO]
13
+ attr_accessor :fd_table #: Array[(IO|File)]
14
+ attr_accessor :argv #: Array[String]
7
15
 
8
- def initialize
16
+ # @rbs argv: Array[String]
17
+ # @rbs return: void
18
+ def initialize(argv: [])
9
19
  @fd_table = [
10
20
  STDIN,
11
21
  STDOUT,
12
22
  STDERR,
13
23
  ]
24
+ @argv = argv
25
+ end
26
+
27
+ # @rbs store: Store
28
+ # @rbs args: Array[wasmValue]
29
+ # @rbs return: Object
30
+ def args_get(store, args)
31
+ arg_offsets_p = args[0].value
32
+ arg_data_buf_p = args[1].value
33
+ if !arg_data_buf_p.is_a?(Integer)
34
+ raise ArgumentError, "invalid type of args: #{args.inspect}"
35
+ end
36
+
37
+ arg_offsets = [] #: Array[Integer]
38
+ arg_data_slice = [] #: Array[String]
39
+ current_offset = arg_data_buf_p
40
+ @argv.each do |arg|
41
+ arg_offsets << current_offset
42
+ arg_data_slice << arg
43
+ current_offset += arg.size + 1
44
+ end
45
+ arg_data = arg_data_slice.join("\0") + "\0"
46
+
47
+ memory = store.memories[0]
48
+ memory.data[arg_data_buf_p...(arg_data_buf_p + arg_data.size)] = arg_data
49
+
50
+ arg_offsets.each_with_index do |offset, i|
51
+ data_begin = arg_offsets_p + i * 4
52
+ memory.data[data_begin...(data_begin + 4)] = [offset].pack("I!")
53
+ end
54
+
55
+ 0
56
+ end
57
+
58
+ # @rbs store: Store
59
+ # @rbs args: Array[wasmValue]
60
+ # @rbs return: Object
61
+ def args_sizes_get(store, args)
62
+ argc_p = args[0].value
63
+ arglen_p = args[1].value
64
+ argc = @argv.length
65
+ arglen = @argv.map{|arg| arg.size + 1}.sum
66
+
67
+ memory = store.memories[0]
68
+ memory.data[argc_p...(argc_p+4)] = [argc].pack("I!")
69
+ memory.data[arglen_p...(arglen_p+4)] = [arglen].pack("I!")
70
+ 0
71
+ end
72
+
73
+ # @rbs store: Store
74
+ # @rbs args: Array[wasmValue]
75
+ # @rbs return: Object
76
+ def environ_sizes_get(store, args)
77
+ envc_p = args[0].value
78
+ envlen_p = args[1].value
79
+ envc = ENV.length
80
+ envlen = ENV.map{|k,v| k.size + v.size + 1}.sum
81
+
82
+ memory = store.memories[0]
83
+ memory.data[envc_p...(envc_p+4)] = [envc].pack("I!")
84
+ memory.data[envlen_p...(envlen_p+4)] = [envlen].pack("I!")
85
+ 0
86
+ end
87
+
88
+ # @rbs store: Store
89
+ # @rbs args: Array[wasmValue]
90
+ # @rbs return: Object
91
+ def environ_get(store, args)
92
+ environ_offsets_p = args[0].value
93
+ environ_data_buf_p = args[1].value
94
+ if !environ_data_buf_p.is_a?(Integer)
95
+ raise ArgumentError, "invalid type of args: #{args.inspect}"
96
+ end
97
+
98
+ environ_offsets = [] #: Array[Integer]
99
+ environ_data_slice = [] #: Array[String]
100
+ current_offset = environ_data_buf_p
101
+ ENV.each do |k, v|
102
+ environ_offsets << current_offset
103
+ environ_data_slice << "#{k}=#{v}"
104
+ current_offset += "#{k}=#{v}".size + 1
105
+ end
106
+ environ_data = environ_data_slice.join("\0") + "\0"
107
+
108
+ memory = store.memories[0]
109
+ memory.data[environ_data_buf_p...(environ_data_buf_p + environ_data.size)] = environ_data
110
+
111
+ environ_offsets.each_with_index do |offset, i|
112
+ data_begin = environ_offsets_p + i * 4
113
+ memory.data[data_begin...(data_begin + 4)] = [offset].pack("I!")
114
+ end
115
+
116
+ 0
117
+ end
118
+
119
+ # @rbs store: Store
120
+ # @rbs args: Array[wasmValue]
121
+ # @rbs return: Object
122
+ def clock_time_get(store, args)
123
+ clock_id = args[0].value
124
+ # we dont use precision...
125
+ _precision = args[1].value
126
+ timebuf64 = args[2].value
127
+ if clock_id != 0 # - CLOCKID_REALTIME
128
+ # raise NotImplementedError, "CLOCKID_REALTIME is an only supported id"
129
+ return -255
130
+ end
131
+ # timestamp in nanoseconds
132
+ now = Time.now.to_i * 1_000_000
133
+
134
+ memory = store.memories[0]
135
+ now_packed = [now].pack("Q!")
136
+ memory.data[timebuf64...(timebuf64+8)] = now_packed
137
+ 0
138
+ end
139
+
140
+ # @rbs store: Store
141
+ # @rbs args: Array[wasmValue]
142
+ # @rbs return: Object
143
+ def fd_prestat_get(store, args)
144
+ fd = args[0].value.to_i
145
+ prestat_offset = args[1].value.to_i
146
+ if fd >= @fd_table.size
147
+ return Wasi::EBADF
148
+ end
149
+ file = @fd_table[fd]
150
+ if !file.is_a?(File)
151
+ return Wasi::EBADF
152
+ end
153
+ name = file.path
154
+ memory = store.memories[0]
155
+ # Zero-value 8-bit tag, and 3-byte zero-value padding
156
+ memory.data[prestat_offset...(prestat_offset+4)] = [0].pack("I!")
157
+ memory.data[(prestat_offset+4)...(prestat_offset+8)] = [name.size].pack("I!")
158
+ 0
14
159
  end
15
160
 
16
161
  # @rbs store: Store
@@ -29,6 +174,7 @@ module Wardite
29
174
  raise Wardite::ArgumentError, "args too short"
30
175
  end
31
176
  file = self.fd_table[fd]
177
+ return Wasi::EBADF if !file
32
178
  memory = store.memories[0]
33
179
  nwritten = 0
34
180
  iovs_len.times do
@@ -45,11 +191,126 @@ module Wardite
45
191
  0
46
192
  end
47
193
 
48
- # @rbs return: Hash[Symbol, wasmCallable]
49
- def to_module
50
- {
51
- fd_write: lambda{|store, args| self.fd_write(store, args) },
52
- }
194
+ # @rbs store: Store
195
+ # @rbs args: Array[wasmValue]
196
+ # @rbs return: Object
197
+ def fd_read(store, args)
198
+ iargs = args.map do |elm|
199
+ if elm.is_a?(I32)
200
+ elm.value
201
+ else
202
+ raise Wardite::ArgumentError, "invalid type of args: #{args.inspect}"
203
+ end
204
+ end #: Array[Integer]
205
+ fd, iovs, iovs_len, rp = *iargs
206
+ if !fd || !iovs || !iovs_len || !rp
207
+ raise Wardite::ArgumentError, "args too short"
208
+ end
209
+ file = self.fd_table[fd]
210
+ return Wasi::EBADF if !file
211
+ memory = store.memories[0]
212
+ nread = 0
213
+
214
+ iovs_len.times do
215
+ start = unpack_le_int(memory.data[iovs...(iovs+4)])
216
+ iovs += 4
217
+ slen = unpack_le_int(memory.data[iovs...(iovs+4)])
218
+ iovs += 4
219
+ buf = file.read(slen)
220
+ if !buf
221
+ return Wasi::EFAULT
222
+ end
223
+ memory.data[start...(start+slen)] = buf
224
+ nread += slen
225
+ end
226
+
227
+ memory.data[rp...(rp+4)] = [nread].pack("I!")
228
+ 0
229
+ end
230
+
231
+ # @rbs store: Store
232
+ # @rbs args: Array[wasmValue]
233
+ # @rbs return: Object
234
+ def fd_fdstat_get(store, args)
235
+ fd = args[0].value.to_i
236
+ fdstat_offset = args[1].value.to_i
237
+ if fd >= @fd_table.size
238
+ return Wasi::EBADF
239
+ end
240
+ file = @fd_table[fd]
241
+ fdflags = 0
242
+ if file.is_a?(IO)
243
+ fdflags |= Wasi::FD_APPEND
244
+ else
245
+ if (Fcntl::O_APPEND & file.fcntl(Fcntl::F_GETFL, 0)) != 0
246
+ fdflags |= Wasi::FD_APPEND
247
+ end
248
+ end
249
+
250
+ if (Fcntl::O_NONBLOCK & file.fcntl(Fcntl::F_GETFL, 0)) != 0
251
+ fdflags |= Wasi::FD_NONBLOCK
252
+ end
253
+
254
+ stat = file.stat #: File::Stat
255
+ ftype = Wasi.to_ftype(stat.ftype)
256
+
257
+ fs_right_base = 0
258
+ fs_right_inheriting = 0
259
+
260
+ case ftype
261
+ when Wasi::FILETYPE_DIRECTORY
262
+ fs_right_base = Wasi::RIGHT_DIR_RIGHT_BASE
263
+ fs_right_inheriting = Wasi::RIGHT_FILE_RIGHT_BASE | Wasi::RIGHT_DIR_RIGHT_BASE
264
+ when Wasi::FILETYPE_CHARACTER_DEVICE
265
+ fs_right_base = Wasi::RIGHT_FILE_RIGHT_BASE & \
266
+ (~Wasi::RIGHT_FD_SEEK) & (~Wasi::RIGHT_FD_TELL)
267
+ else
268
+ fs_right_base = Wasi::RIGHT_FILE_RIGHT_BASE
269
+ end
270
+
271
+ memory = store.memories[0]
272
+
273
+ binformat = [fdflags, ftype, 0, 0, 0, 0, fs_right_base, fs_right_inheriting]
274
+ .pack("SSC4QQ")
275
+ memory.data[fdstat_offset...(fdstat_offset+binformat.size)] = binformat
276
+ 0
277
+ end
278
+
279
+ # @rbs store: Store
280
+ # @rbs args: Array[wasmValue]
281
+ # @rbs return: Object
282
+ def fd_filestat_get(store, args)
283
+ fd = args[0].value.to_i
284
+ filestat_offset = args[1].value.to_i
285
+ if fd >= @fd_table.size
286
+ return Wasi::EBADF
287
+ end
288
+ file = @fd_table[fd]
289
+ stat = file.stat #: File::Stat
290
+ memory = store.memories[0]
291
+ 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")
292
+ memory.data[filestat_offset...(filestat_offset+binformat.size)] = binformat
293
+ 0
294
+ end
295
+
296
+ # @rbs store: Store
297
+ # @rbs args: Array[wasmValue]
298
+ # @rbs return: Object
299
+ def proc_exit(store, args)
300
+ exit_code = args[0].value
301
+ exit(exit_code)
302
+ end
303
+
304
+ # @rbs store: Store
305
+ # @rbs args: Array[wasmValue]
306
+ # @rbs return: Object
307
+ def random_get(store, args)
308
+ buf = args[0].value.to_i
309
+ buflen = args[1].value.to_i
310
+ randoms = SecureRandom.random_bytes(buflen) #: String
311
+ memory = store.memories[0]
312
+ memory.data[buf...(buf+buflen)] = randoms
313
+ 0
53
314
  end
54
315
 
55
316
  private
@@ -0,0 +1,53 @@
1
+ # rbs_inline: enabled
2
+
3
+ module Wardite
4
+ # @rbs!
5
+ # interface _WasmCallable
6
+ # def call: (Store, Array[wasmValue]) -> wasmFuncReturn
7
+ # def []: (Store, Array[wasmValue]) -> wasmFuncReturn
8
+ # end
9
+
10
+ # @rbs!
11
+ # type wasmModuleSrc = Hash[Symbol, _WasmCallable] | WasmModule | HashModule
12
+ # type wasmModule = WasmModule | HashModule
13
+
14
+ module WasmModule
15
+ # @rbs fnname: Symbol
16
+ # @rbs store: Store
17
+ # @rbs args: Array[wasmValue]
18
+ # @rbs return: wasmFuncReturn
19
+ def invoke(fnname, store, *args)
20
+ self.__send__(fnname, store, args)
21
+ end
22
+
23
+ # @rbs fnname: Symbol
24
+ # @rbs return: _WasmCallable
25
+ def callable(fnname)
26
+ self.method(fnname)
27
+ end
28
+ end
29
+
30
+ class HashModule
31
+ attr_accessor :hash #: Hash[Symbol, _WasmCallable]
32
+
33
+ # @rbs ha: Hash[Symbol, _WasmCallable]
34
+ def initialize(hash)
35
+ @hash = hash
36
+ end
37
+
38
+ # @rbs fnname: Symbol
39
+ # @rbs store: Store
40
+ # @rbs args: Array[wasmValue]
41
+ # @rbs return: wasmFuncReturn
42
+ def invoke(fnname, store, *args)
43
+ fn = self.hash[fnname.to_sym]
44
+ fn.call(store, args)
45
+ end
46
+
47
+ # @rbs fnname: Symbol
48
+ # @rbs return: _WasmCallable
49
+ def callable(fnname)
50
+ self.hash[fnname.to_sym]
51
+ end
52
+ end
53
+ end
data/lib/wardite.rb CHANGED
@@ -20,8 +20,6 @@ require_relative "wardite/alu_f32.generated"
20
20
  require_relative "wardite/alu_f64.generated"
21
21
  require_relative "wardite/convert.generated"
22
22
 
23
- require_relative "wardite/wasi"
24
-
25
23
  require "stringio"
26
24
 
27
25
  module Wardite
@@ -38,13 +36,23 @@ module Wardite
38
36
 
39
37
  attr_accessor :exports #: Exports
40
38
 
41
- attr_reader :import_object #: Hash[Symbol, Hash[Symbol, wasmCallable]]
39
+ attr_reader :import_object #: Hash[Symbol, wasmModule]
40
+
41
+ attr_accessor :wasi #: WasiSnapshotPreview1?
42
42
 
43
- # @rbs import_object: Hash[Symbol, Hash[Symbol, wasmCallable]]
43
+ # @rbs import_object: Hash[Symbol, wasmModuleSrc]
44
44
  # @rbs &blk: (Instance) -> void
45
45
  def initialize(import_object, &blk)
46
+ @wasi = nil
47
+
46
48
  blk.call(self)
47
- @import_object = import_object
49
+ import_object.each_pair do |k, v|
50
+ if v.is_a?(Hash)
51
+ import_object[k] = HashModule.new(v)
52
+ end
53
+ end
54
+
55
+ @import_object = import_object #: Hash[Symbol, wasmModule]
48
56
 
49
57
  @store = Store.new(self)
50
58
  @exports = Exports.new(self.export_section, store)
@@ -281,7 +289,8 @@ module Wardite
281
289
 
282
290
  case fn
283
291
  when WasmFunction
284
- invoke_internal(fn)
292
+ r = invoke_internal(fn)
293
+ r
285
294
  when ExternalFunction
286
295
  invoke_external(fn)
287
296
  else
@@ -313,25 +322,7 @@ module Wardite
313
322
  raise LoadError, "stack too short"
314
323
  end
315
324
  self.stack = drained_stack(local_start)
316
-
317
- wasm_function.locals_count.each_with_index do |count, i|
318
- typ = wasm_function.locals_type[i]
319
- count.times do
320
- case typ
321
- when :i32, :u32
322
- locals.push I32(0)
323
- when :i64, :u64
324
- locals.push I64(0)
325
- when :f32
326
- locals.push F32(0.0)
327
- when :f64
328
- locals.push F64(0.0)
329
- else
330
- $stderr.puts "warning: unknown type #{typ.inspect}. default to I32"
331
- locals.push I32(0)
332
- end
333
- end
334
- end
325
+ locals.concat(wasm_function.default_locals)
335
326
 
336
327
  arity = wasm_function.retsig.size
337
328
  frame = Frame.new(-1, stack.size, wasm_function.body, arity, locals)
@@ -473,7 +464,7 @@ module Wardite
473
464
  when :block
474
465
  block = insn.operand[0]
475
466
  raise EvalError, "block op without block" if !block.is_a?(Block)
476
- next_pc = fetch_ops_while_end(frame.body, frame.pc)
467
+ next_pc = insn.meta[:end_pos]
477
468
  label = Label.new(:block, next_pc, stack.size, block.result_size)
478
469
  frame.labels.push(label)
479
470
 
@@ -481,19 +472,19 @@ module Wardite
481
472
  block = insn.operand[0]
482
473
  raise EvalError, "loop op without block" if !block.is_a?(Block)
483
474
  start = frame.pc
484
- end_pc = fetch_ops_while_end(frame.body, frame.pc)
485
- label = Label.new(:loop, end_pc, stack.size, block.result_size, start)
475
+ next_pc = insn.meta[:end_pos]
476
+ label = Label.new(:loop, next_pc, stack.size, block.result_size, start)
486
477
  frame.labels.push(label)
487
478
 
488
479
  when :if
489
480
  block = insn.operand[0]
490
481
  raise EvalError, "if op without block" if !block.is_a?(Block)
491
- cond = stack.pop
482
+ cond = stack.pop
492
483
  raise EvalError, "cond not found" if !cond.is_a?(I32)
493
- next_pc = fetch_ops_while_end(frame.body, frame.pc)
484
+ next_pc = insn.meta[:end_pos]
494
485
 
495
486
  if cond.value.zero?
496
- frame.pc = fetch_ops_while_else_or_end(frame.body, frame.pc)
487
+ frame.pc = insn.meta[:else_pos]
497
488
  end
498
489
 
499
490
  if frame.pc == next_pc
@@ -740,38 +731,6 @@ module Wardite
740
731
  raise e
741
732
  end
742
733
 
743
- # @rbs ops: Array[Op]
744
- # @rbs pc_start: Integer
745
- # @rbs return: Integer
746
- def fetch_ops_while_else_or_end(ops, pc_start)
747
- cursor = pc_start
748
- depth = 0
749
- loop {
750
- cursor += 1
751
- inst = ops[cursor]
752
- case inst&.code
753
- when nil
754
- raise EvalError, "end op not found"
755
- when :if
756
- depth += 1
757
- when :else
758
- if depth == 0
759
- return cursor
760
- end
761
- # do not touch depth
762
- when :end
763
- if depth == 0
764
- return cursor
765
- else
766
- depth -= 1
767
- end
768
- else
769
- # nop
770
- end
771
- }
772
- raise "[BUG] unreachable"
773
- end
774
-
775
734
  # @rbs labels: Array[Label]
776
735
  # @rbs stack: Array[wasmValue]
777
736
  # @rbs level: Integer
@@ -797,33 +756,6 @@ module Wardite
797
756
  pc
798
757
  end
799
758
 
800
- # @rbs ops: Array[Op]
801
- # @rbs pc_start: Integer
802
- # @rbs return: Integer
803
- def fetch_ops_while_end(ops, pc_start)
804
- cursor = pc_start
805
- depth = 0
806
- loop {
807
- cursor += 1
808
- inst = ops[cursor]
809
- case inst&.code
810
- when nil
811
- raise EvalError, "end op not found"
812
- when :if, :block, :loop
813
- depth += 1
814
- when :end
815
- if depth == 0
816
- return cursor
817
- else
818
- depth -= 1
819
- end
820
- else
821
- # nop
822
- end
823
- }
824
- raise "[BUG] unreachable"
825
- end
826
-
827
759
  # unwind the stack and put return value if exists
828
760
  # @rbs sp: Integer
829
761
  # @rbs arity: Integer
@@ -947,7 +879,7 @@ module Wardite
947
879
  class Store
948
880
  attr_accessor :funcs #: Array[WasmFunction|ExternalFunction]
949
881
 
950
- # FIXME: attr_accessor :modules
882
+ attr_accessor :modules #: Hash[Symbol, wasmModule]
951
883
 
952
884
  attr_accessor :memories #: Array[Memory]
953
885
 
@@ -966,21 +898,19 @@ module Wardite
966
898
 
967
899
  import_section = inst.import_section
968
900
  @funcs = []
901
+ @modules = inst.import_object
969
902
 
970
903
  if type_section && func_section && code_section
971
904
  import_section.imports.each do |desc|
972
905
  callsig = type_section.defined_types[desc.sig_index]
973
906
  retsig = type_section.defined_results[desc.sig_index]
974
- imported_module = inst.import_object[desc.module_name.to_sym]
975
- if !imported_module
907
+ target_module = self.modules[desc.module_name.to_sym]
908
+ if target_module.nil?
976
909
  raise ::NameError, "module #{desc.module_name} not found"
977
910
  end
978
- imported_proc = imported_module[desc.name.to_sym]
979
- if !imported_proc
980
- raise ::NameError, "function #{desc.module_name}.#{desc.name} not found"
981
- end
911
+ target_name = desc.name.to_sym
982
912
 
983
- ext_function = ExternalFunction.new(callsig, retsig, imported_proc)
913
+ ext_function = ExternalFunction.new(target_module, target_name, callsig, retsig)
984
914
  self.funcs << ext_function
985
915
  end
986
916
 
@@ -1272,7 +1202,10 @@ module Wardite
1272
1202
  end
1273
1203
 
1274
1204
  # TODO: common interface btw. WasmFunction and ExternalFunction?
1205
+ # may be _WasmCallable?
1275
1206
  class WasmFunction
1207
+ include ValueHelper
1208
+
1276
1209
  attr_accessor :callsig #: Array[Symbol]
1277
1210
 
1278
1211
  attr_accessor :retsig #: Array[Symbol]
@@ -1281,6 +1214,8 @@ module Wardite
1281
1214
 
1282
1215
  attr_accessor :findex #: Integer
1283
1216
 
1217
+ attr_accessor :default_locals #: Array[wasmValue]
1218
+
1284
1219
  # @rbs callsig: Array[Symbol]
1285
1220
  # @rbs retsig: Array[Symbol]
1286
1221
  # @rbs code_body: CodeSection::CodeBody
@@ -1291,6 +1226,7 @@ module Wardite
1291
1226
 
1292
1227
  @code_body = code_body
1293
1228
  @findex = 0 # for debug
1229
+ @default_locals = construct_default_locals
1294
1230
  end
1295
1231
 
1296
1232
  # @rbs return: Array[Op]
@@ -1308,6 +1244,30 @@ module Wardite
1308
1244
  code_body.locals_count
1309
1245
  end
1310
1246
 
1247
+ # @rbs return: Array[wasmValue]
1248
+ def construct_default_locals
1249
+ locals = [] #: Array[wasmValue]
1250
+ locals_count.each_with_index do |count, i|
1251
+ typ = locals_type[i]
1252
+ count.times do
1253
+ case typ
1254
+ when :i32, :u32
1255
+ locals.push I32(0)
1256
+ when :i64, :u64
1257
+ locals.push I64(0)
1258
+ when :f32
1259
+ locals.push F32(0.0)
1260
+ when :f64
1261
+ locals.push F64(0.0)
1262
+ else
1263
+ $stderr.puts "warning: unknown type #{typ.inspect}. default to I32"
1264
+ locals.push I32(0)
1265
+ end
1266
+ end
1267
+ end
1268
+ locals
1269
+ end
1270
+
1311
1271
  # @rbs override_type: Type?
1312
1272
  # @rbs return: WasmFunction
1313
1273
  def clone(override_type: nil)
@@ -1322,23 +1282,32 @@ module Wardite
1322
1282
 
1323
1283
  # @rbs!
1324
1284
  # type wasmFuncReturn = Object|nil
1325
- # type wasmCallable = ^(Store, Array[wasmValue]) -> wasmFuncReturn
1326
1285
 
1327
1286
  class ExternalFunction
1287
+ attr_accessor :target_module #: wasmModule
1288
+
1289
+ attr_accessor :name #: Symbol
1290
+
1328
1291
  attr_accessor :callsig #: Array[Symbol]
1329
1292
 
1330
1293
  attr_accessor :retsig #: Array[Symbol]
1331
1294
 
1332
- attr_accessor :callable #: wasmCallable
1295
+ #attr_accessor :callable #: _WasmCallable
1333
1296
 
1334
1297
  # @rbs callsig: Array[Symbol]
1335
1298
  # @rbs retsig: Array[Symbol]
1336
- # @rbs callable: wasmCallable
1299
+ # @rbs callable: _WasmCallable
1337
1300
  # @rbs return: void
1338
- def initialize(callsig, retsig, callable)
1301
+ def initialize(target_module, name, callsig, retsig)
1302
+ @target_module = target_module
1303
+ @name = name
1339
1304
  @callsig = callsig
1340
1305
  @retsig = retsig
1341
- @callable = callable
1306
+ end
1307
+
1308
+ # @rbs return: _WasmCallable
1309
+ def callable()
1310
+ target_module.callable(self.name)
1342
1311
  end
1343
1312
 
1344
1313
  # @rbs override_type: Type?
@@ -1346,9 +1315,9 @@ module Wardite
1346
1315
  def clone(override_type: nil)
1347
1316
  if override_type
1348
1317
  # callable is assumed to be frozen, so we can copy its ref
1349
- ExternalFunction.new(override_type.callsig, override_type.retsig, callable)
1318
+ ExternalFunction.new(target_module, name, override_type.callsig, override_type.retsig)
1350
1319
  else
1351
- ExternalFunction.new(callsig, retsig, callable)
1320
+ ExternalFunction.new(target_module, name, callsig, retsig)
1352
1321
  end
1353
1322
  end
1354
1323
  end