kobako 0.3.0 → 0.4.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1 -1
  3. data/README.md +85 -5
  4. data/data/kobako.wasm +0 -0
  5. data/ext/kobako/Cargo.toml +1 -1
  6. data/ext/kobako/src/wasm/cache.rs +12 -4
  7. data/ext/kobako/src/wasm/dispatch.rs +15 -14
  8. data/ext/kobako/src/wasm/host_state.rs +111 -5
  9. data/ext/kobako/src/wasm/instance.rs +135 -33
  10. data/ext/kobako/src/wasm.rs +1 -0
  11. data/lib/kobako/codec/decoder.rb +0 -2
  12. data/lib/kobako/codec/factory.rb +13 -10
  13. data/lib/kobako/codec/utils.rb +105 -13
  14. data/lib/kobako/handle.rb +62 -0
  15. data/lib/kobako/handle_table.rb +119 -0
  16. data/lib/kobako/invocation.rb +56 -25
  17. data/lib/kobako/outcome.rb +42 -12
  18. data/lib/kobako/rpc/dispatcher.rb +22 -20
  19. data/lib/kobako/rpc/envelope.rb +7 -7
  20. data/lib/kobako/rpc/fault.rb +1 -1
  21. data/lib/kobako/rpc/server.rb +12 -24
  22. data/lib/kobako/rpc/wire_error.rb +23 -0
  23. data/lib/kobako/sandbox.rb +77 -24
  24. data/lib/kobako/usage.rb +41 -0
  25. data/lib/kobako/version.rb +1 -1
  26. data/lib/kobako.rb +1 -0
  27. data/sig/kobako/codec/factory.rbs +1 -1
  28. data/sig/kobako/codec/utils.rbs +10 -0
  29. data/sig/kobako/handle.rbs +19 -0
  30. data/sig/kobako/handle_table.rbs +23 -0
  31. data/sig/kobako/invocation.rbs +3 -1
  32. data/sig/kobako/outcome.rbs +1 -1
  33. data/sig/kobako/rpc/dispatcher.rbs +7 -7
  34. data/sig/kobako/rpc/envelope.rbs +3 -3
  35. data/sig/kobako/rpc/server.rbs +1 -7
  36. data/sig/kobako/rpc/wire_error.rbs +6 -0
  37. data/sig/kobako/sandbox.rbs +7 -1
  38. data/sig/kobako/usage.rbs +11 -0
  39. data/sig/kobako/wasm.rbs +2 -0
  40. metadata +9 -5
  41. data/lib/kobako/rpc/handle.rb +0 -39
  42. data/lib/kobako/rpc/handle_table.rb +0 -107
  43. data/sig/kobako/rpc/handle.rbs +0 -19
  44. data/sig/kobako/rpc/handle_table.rbs +0 -25
@@ -4,24 +4,28 @@ require "forwardable"
4
4
 
5
5
  require_relative "capture"
6
6
  require_relative "errors"
7
+ require_relative "handle_table"
7
8
  require_relative "invocation"
8
9
  require_relative "outcome"
9
10
  require_relative "rpc/server"
10
11
  require_relative "rpc/envelope"
11
12
  require_relative "sandbox_options"
12
13
  require_relative "snippet"
14
+ require_relative "usage"
13
15
 
14
16
  module Kobako
15
17
  # Kobako::Sandbox — the user-facing entry point for executing guest mruby
16
18
  # scripts inside a wasmtime-hosted Wasm module
17
19
  # ({docs/behavior.md B-01}[link:../../docs/behavior.md]).
18
20
  #
19
- # The Sandbox owns the +Kobako::Wasm::Instance+, the per-instance RPC Server
20
- # (which itself owns the per-run HandleTable), and the per-channel byte
21
- # caches for guest stdout / stderr capture. The underlying wasmtime Engine
22
- # and compiled Module are cached at process scope by the native ext and
23
- # never surface to Ruby constructing many Sandboxes amortises both costs
24
- # automatically.
21
+ # The Sandbox owns the +Kobako::Wasm::Instance+, the per-Sandbox
22
+ # +Kobako::HandleTable+ ({docs/behavior.md B-19}[link:../../docs/behavior.md]),
23
+ # the per-instance RPC Server (which receives the HandleTable by
24
+ # injection so guest→host dispatch and host→guest auto-wrap share one
25
+ # allocator), and the per-channel byte caches for guest stdout / stderr
26
+ # capture. The underlying wasmtime Engine and compiled Module are cached
27
+ # at process scope by the native ext and never surface to Ruby —
28
+ # constructing many Sandboxes amortises both costs automatically.
25
29
  #
26
30
  # Output capture policy ({docs/behavior.md B-04}[link:../../docs/behavior.md]): the
27
31
  # per-channel cap (+stdout_limit+ / +stderr_limit+) is enforced inside the
@@ -72,6 +76,17 @@ module Kobako
72
76
  @stderr_capture.truncated?
73
77
  end
74
78
 
79
+ # Returns the +Kobako::Usage+ value object for the most recent
80
+ # invocation ({docs/behavior.md B-35}[link:../../docs/behavior.md]).
81
+ # Carries +wall_time+ (Float seconds the guest export call spent
82
+ # inside wasmtime) and +memory_peak+ (Integer bytes, high-water of
83
+ # the per-invocation +memory.grow+ delta past the entry-time
84
+ # baseline). Returns +Kobako::Usage::EMPTY+ before any invocation;
85
+ # populated on every outcome — including +TrapError+ — so the Host
86
+ # App can read it after rescuing a trap to diagnose budget
87
+ # consumption.
88
+ attr_reader :usage
89
+
75
90
  # Build a fresh Sandbox.
76
91
  #
77
92
  # +wasm_path+ is the absolute path to the Guest Binary; defaults to the
@@ -87,12 +102,13 @@ module Kobako
87
102
  @wasm_path = wasm_path || Kobako::Wasm.default_path
88
103
  @options = SandboxOptions.new(timeout: timeout, memory_limit: memory_limit, stdout_limit: stdout_limit,
89
104
  stderr_limit: stderr_limit)
90
- @services = Kobako::RPC::Server.new
105
+ @handle_table = HandleTable.new
106
+ @services = Kobako::RPC::Server.new(handle_table: @handle_table)
91
107
  @snippets = Snippet::Table.new
92
108
  @instance = Kobako::Wasm::Instance.from_path(@wasm_path, @options.timeout, @options.memory_limit,
93
109
  @options.stdout_limit, @options.stderr_limit)
94
110
  @instance.server = @services
95
- clear_captures!
111
+ reset_invocation_state!
96
112
  end
97
113
 
98
114
  # Declare or retrieve the Namespace named +name+ on this Sandbox
@@ -154,7 +170,7 @@ module Kobako
154
170
  def run(target, *args, **kwargs)
155
171
  invocation = Invocation.new(entrypoint: target, args: args, kwargs: kwargs)
156
172
  invoke!(:run) do
157
- @instance.run(@services.encoded_preamble, @snippets.encode, invocation.encode)
173
+ @instance.run(@services.encoded_preamble, @snippets.encode, invocation.encode(@handle_table))
158
174
  end
159
175
  end
160
176
 
@@ -195,28 +211,36 @@ module Kobako
195
211
  # B-33}[link:../../docs/behavior.md]). Seals the Service / snippet
196
212
  # registries on first call (idempotent) and zeros the per-invocation
197
213
  # capability state — capture buffers, truncation predicates, and the
198
- # HandleTable counter — before the guest runs.
214
+ # HandleTable counter — before the guest runs. The HandleTable
215
+ # itself is held as +@handle_table+ and never exposed beyond
216
+ # this class: SPEC.md Terminology pins it as "Not exposed to the
217
+ # Host App" (B-19 / B-20 / E-29).
199
218
  def begin_invocation!
200
219
  @services.seal!
201
- @services.reset_handles!
202
- clear_captures!
220
+ @handle_table.reset!
221
+ reset_invocation_state!
203
222
  end
204
223
 
205
- # Reset both per-channel captures to the pre-invocation sentinel
206
- # ({docs/behavior.md B-05}[link:../../docs/behavior.md]). Shared by +#initialize+
207
- # (first-time setup) and +#begin_invocation!+ (between-invocation
208
- # reset) so both paths agree on what "empty capture" means.
209
- def clear_captures!
224
+ # Reset all per-invocation observable state to its pre-invocation
225
+ # sentinels both per-channel captures
226
+ # ({docs/behavior.md B-05}[link:../../docs/behavior.md]) and the
227
+ # per-last-invocation usage record
228
+ # ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Shared by
229
+ # +#initialize+ (first-time setup) and +#begin_invocation!+
230
+ # (between-invocation reset) so both paths agree on what
231
+ # "pre-invocation state" means.
232
+ def reset_invocation_state!
210
233
  @stdout_capture = Capture::EMPTY
211
234
  @stderr_capture = Capture::EMPTY
235
+ @usage = Usage::EMPTY
212
236
  end
213
237
 
214
238
  # Read the per-channel capture pairs (+[bytes, truncated]+) from the
215
239
  # ext after an invocation completes and wrap each as a +Capture+ value
216
240
  # object. The ext clips +bytes+ to the configured cap and sets
217
241
  # +truncated+ when the guest produced strictly more than +cap+ bytes
218
- # ({docs/behavior.md B-04}[link:../../docs/behavior.md]). Mirror of {#clear_captures!}
219
- # at the post-invocation boundary.
242
+ # ({docs/behavior.md B-04}[link:../../docs/behavior.md]). Mirror of
243
+ # {#reset_invocation_state!} at the post-invocation boundary.
220
244
  def read_captures!
221
245
  out_bytes, out_truncated = @instance.stdout
222
246
  err_bytes, err_truncated = @instance.stderr
@@ -224,6 +248,37 @@ module Kobako
224
248
  @stderr_capture = Capture.from_ext(err_bytes, err_truncated)
225
249
  end
226
250
 
251
+ # Read the per-last-invocation +wall_time+ and +memory_peak+ from
252
+ # the ext and wrap them as a +Kobako::Usage+ value object
253
+ # ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Runs in
254
+ # the +invoke!+ +ensure+ block so the usage record is populated on
255
+ # every outcome — value return, +Kobako::TrapError+ (including
256
+ # +TimeoutError+ / +MemoryLimitError+), +Kobako::SandboxError+,
257
+ # and +Kobako::ServiceError+.
258
+ #
259
+ # The ext returns a positional 2-tuple +[wall_time, memory_peak]+
260
+ # whose order matches the +Kobako::Usage+ field order; the
261
+ # destructure-then-kwargs handoff below is the explicit
262
+ # positional→keyword conversion point, mirroring
263
+ # +#read_captures!+'s +Capture.from_ext(bytes, truncated)+ shape.
264
+ def read_usage!
265
+ wall_time, memory_peak = @instance.usage
266
+ @usage = Usage.new(wall_time: wall_time, memory_peak: memory_peak)
267
+ end
268
+
269
+ # Map a wasmtime trap class to the matching three-layer Ruby
270
+ # exception class. Cap-trap subclasses
271
+ # ({docs/behavior.md E-19 / E-20}[link:../../docs/behavior.md])
272
+ # select their named +TrapError+ subclass; everything else
273
+ # collapses to the base +Kobako::TrapError+.
274
+ def trap_class_for(err)
275
+ case err
276
+ when Kobako::Wasm::TimeoutError then TimeoutError
277
+ when Kobako::Wasm::MemoryLimitError then MemoryLimitError
278
+ else TrapError
279
+ end
280
+ end
281
+
227
282
  # Shared prologue / epilogue + trap-class translator for both
228
283
  # invocation verbs. +verb+ is +:eval+ or +:run+; it tags the
229
284
  # TrapError message so the failing export is identifiable. The
@@ -239,12 +294,10 @@ module Kobako
239
294
  yield
240
295
  read_captures!
241
296
  Outcome.decode(@instance.outcome!)
242
- rescue Kobako::Wasm::TimeoutError => e
243
- raise TimeoutError, "guest exceeded timeout: #{e.message}"
244
- rescue Kobako::Wasm::MemoryLimitError => e
245
- raise MemoryLimitError, "guest exceeded memory_limit: #{e.message}"
246
297
  rescue Kobako::Wasm::Error => e
247
- raise TrapError, "guest __kobako_#{verb} trapped: #{e.message}"
298
+ raise trap_class_for(e), "Sandbox##{verb} failed: #{e.message}"
299
+ ensure
300
+ read_usage!
248
301
  end
249
302
  end
250
303
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kobako
4
+ # Per-last-invocation resource accounting for a +Kobako::Sandbox+
5
+ # ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Carries two
6
+ # readers populated by every +#eval+ / +#run+ invocation:
7
+ #
8
+ # * +wall_time+ — the Float number of seconds the guest export call
9
+ # spent inside wasmtime during the most recent invocation. The
10
+ # measurement bracket aligns with the +timeout+ deadline
11
+ # ({docs/behavior.md B-01}[link:../../docs/behavior.md]); time spent
12
+ # in host Service callbacks is included, but everything that runs
13
+ # after the guest export returns — the post-export
14
+ # +OUTCOME_BUFFER+ fetch and decode, plus stdout / stderr capture
15
+ # readout — is excluded.
16
+ # * +memory_peak+ — the Integer high-water mark, in bytes, of the
17
+ # per-invocation +memory.grow+ delta past the linear-memory size
18
+ # captured at invocation entry. Same baseline accounting as
19
+ # +memory_limit+ ({docs/behavior.md E-20}[link:../../docs/behavior.md]):
20
+ # the mruby image's initial allocation and any prior-invocation
21
+ # watermark sit outside the measurement. On +MemoryLimitError+
22
+ # +memory_peak+ never exceeds the configured cap because the
23
+ # rejected +desired+ value is not promoted into the high-water.
24
+ #
25
+ # Both readers are populated on every outcome, including +TrapError+
26
+ # branches, so the Host App can read +Sandbox#usage+ after rescuing a
27
+ # trap to diagnose how much of the budget the failing invocation
28
+ # consumed. Before the first invocation +Sandbox#usage+ returns the
29
+ # pre-invocation sentinel +Kobako::Usage::EMPTY+.
30
+ #
31
+ # Built on the +class X < Data.define(...)+ subclass form so the
32
+ # class body is fully Steep-visible; ruby/rbs upstream documents this
33
+ # as the Steep-friendly shape and the +Style/DataInheritance+ cop is
34
+ # disabled on that basis (see +.rubocop.yml+).
35
+ class Usage < Data.define(:wall_time, :memory_peak)
36
+ # Pre-invocation sentinel ({docs/behavior.md B-35}[link:../../docs/behavior.md]).
37
+ # Reused by +Sandbox+ before any invocation has run so callers do not
38
+ # need to handle a +nil+ +#usage+.
39
+ EMPTY = new(wall_time: 0.0, memory_peak: 0)
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/kobako.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "kobako/version"
4
4
  require "kobako/kobako"
5
5
  require_relative "kobako/errors"
6
6
  require_relative "kobako/rpc"
7
+ require_relative "kobako/rpc/wire_error"
7
8
  require_relative "kobako/rpc/server"
8
9
  require_relative "kobako/wasm"
9
10
  require_relative "kobako/sandbox"
@@ -23,7 +23,7 @@ module Kobako
23
23
  def unpack_symbol: (String payload) -> Symbol
24
24
  def register_handle: () -> void
25
25
  def register_fault: () -> void
26
- def unpack_handle: (String payload) -> Kobako::RPC::Handle
26
+ def unpack_handle: (String payload) -> Kobako::Handle
27
27
  def pack_fault: (Kobako::RPC::Fault fault) -> String
28
28
  def unpack_fault: (String payload) -> Kobako::RPC::Fault
29
29
  end
@@ -1,9 +1,19 @@
1
1
  module Kobako
2
2
  module Codec
3
3
  module Utils
4
+ MSGPACK_INT_RANGE: Range[Integer]
5
+
4
6
  def self?.assert_utf8!: (String string, String label) -> void
5
7
 
6
8
  def self?.wire_boundary: [T] () { () -> T } -> T
9
+
10
+ def self?.wire_representable?: (untyped value) -> bool
11
+
12
+ def self?.deep_wrap: (untyped value, Kobako::HandleTable handle_table) -> untyped
13
+
14
+ def self?.primitive_wire_type?: (untyped value) -> bool
15
+
16
+ def self?.container_wire_representable?: (untyped value) -> bool
7
17
  end
8
18
  end
9
19
  end
@@ -0,0 +1,19 @@
1
+ module Kobako
2
+ class Handle
3
+ MIN_ID: Integer
4
+ MAX_ID: Integer
5
+
6
+ attr_reader id: Integer
7
+
8
+ def initialize: (Integer id) -> void
9
+ | (id: Integer) -> void
10
+
11
+ def self.from_wire: (Integer id) -> Handle
12
+
13
+ def with: (?id: Integer) -> Handle
14
+
15
+ def ==: (untyped other) -> bool
16
+
17
+ def hash: () -> Integer
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Kobako
2
+ class HandleTable
3
+ def initialize: (?next_id: Integer) -> void
4
+
5
+ def alloc: (untyped object) -> Kobako::Handle
6
+
7
+ def fetch: (Integer id) -> untyped
8
+
9
+ def release: (Integer id) -> untyped
10
+
11
+ def reset!: () -> self
12
+
13
+ def mark_disconnected: (Integer id) -> self
14
+
15
+ def size: () -> Integer
16
+
17
+ def include?: (Integer id) -> bool
18
+
19
+ private
20
+
21
+ def require_bound!: (Integer id) -> void
22
+ end
23
+ end
@@ -10,7 +10,7 @@ module Kobako
10
10
 
11
11
  def initialize: (entrypoint: Symbol | String, ?args: Array[untyped], ?kwargs: Hash[untyped, untyped]) -> void
12
12
 
13
- def encode: () -> String
13
+ def encode: (Kobako::HandleTable handle_table) -> String
14
14
 
15
15
  private
16
16
 
@@ -19,5 +19,7 @@ module Kobako
19
19
  def validate_args!: (Array[untyped] args) -> Array[untyped]
20
20
 
21
21
  def validate_kwargs!: (Hash[untyped, untyped] kwargs) -> Hash[Symbol, untyped]
22
+
23
+ def forged_handle_message: (String slot) -> String
22
24
  end
23
25
  end
@@ -19,6 +19,6 @@ module Kobako
19
19
 
20
20
  def self?.panic_target_class: (Panic panic) -> (singleton(SandboxError) | singleton(ServiceError))
21
21
 
22
- def self?.build_wire_violation_error: (String message) -> SandboxError
22
+ def self?.build_wire_violation_error: (String message, ?wire_error: String?) -> RPC::WireError
23
23
  end
24
24
  end
@@ -7,25 +7,25 @@ module Kobako
7
7
  class DisconnectedTargetError < StandardError
8
8
  end
9
9
 
10
- def self?.dispatch: (String request_bytes, Server server) -> String
10
+ def self?.dispatch: (String request_bytes, Server server, Kobako::HandleTable handle_table) -> String
11
11
 
12
12
  def self?.encode_caught_error: (StandardError error) -> String
13
13
 
14
14
  def self?.invoke: (untyped target, String method, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> untyped
15
15
 
16
- def self?.resolve_arg: (untyped value, HandleTable handle_table) -> untyped
16
+ def self?.resolve_arg: (untyped value, Kobako::HandleTable handle_table) -> untyped
17
17
 
18
- def self?.resolve_target: (String | Kobako::RPC::Handle target, Server server, HandleTable handle_table) -> untyped
18
+ def self?.resolve_target: (String | Kobako::Handle target, Server server, Kobako::HandleTable handle_table) -> untyped
19
19
 
20
20
  def self?.resolve_path: (String path, Server server) -> untyped
21
21
 
22
- def self?.resolve_handle: (Kobako::RPC::Handle handle, HandleTable handle_table) -> untyped
22
+ def self?.resolve_handle: (Kobako::Handle handle, Kobako::HandleTable handle_table) -> untyped
23
23
 
24
- def self?.require_live_object!: (Integer id, HandleTable handle_table) -> untyped
24
+ def self?.require_live_object!: (Integer id, Kobako::HandleTable handle_table) -> untyped
25
25
 
26
- def self?.encode_ok: (untyped value, Server server) -> String
26
+ def self?.encode_ok: (untyped value, Kobako::HandleTable handle_table) -> String
27
27
 
28
- def self?.wrap_as_handle: (untyped value, Server server) -> Kobako::RPC::Handle
28
+ def self?.wrap_as_handle: (untyped value, Kobako::HandleTable handle_table) -> Kobako::Handle
29
29
 
30
30
  def self?.encode_error: (String type, String message) -> String
31
31
  end
@@ -4,14 +4,14 @@ module Kobako
4
4
  STATUS_ERROR: Integer
5
5
 
6
6
  class Request
7
- attr_reader target: String | Kobako::RPC::Handle
7
+ attr_reader target: String | Kobako::Handle
8
8
  attr_reader method_name: String
9
9
  attr_reader args: Array[untyped]
10
10
  attr_reader kwargs: Hash[Symbol, untyped]
11
11
 
12
- def initialize: (target: String | Kobako::RPC::Handle, method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> void
12
+ def initialize: (target: String | Kobako::Handle, method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> void
13
13
 
14
- def with: (?target: String | Kobako::RPC::Handle, ?method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> Request
14
+ def with: (?target: String | Kobako::Handle, ?method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> Request
15
15
 
16
16
  def ==: (untyped other) -> bool
17
17
 
@@ -1,9 +1,7 @@
1
1
  module Kobako
2
2
  module RPC
3
3
  class Server
4
- attr_reader handle_table: HandleTable
5
-
6
- def initialize: (?handle_table: HandleTable) -> void
4
+ def initialize: (?handle_table: Kobako::HandleTable) -> void
7
5
 
8
6
  def define: (Symbol | String name) -> Kobako::RPC::Namespace
9
7
 
@@ -11,8 +9,6 @@ module Kobako
11
9
 
12
10
  def bound?: (String target) -> bool
13
11
 
14
- def namespaces: () -> Array[Kobako::RPC::Namespace]
15
-
16
12
  def size: () -> Integer
17
13
 
18
14
  def empty?: () -> bool
@@ -25,8 +21,6 @@ module Kobako
25
21
 
26
22
  def sealed?: () -> bool
27
23
 
28
- def reset_handles!: () -> HandleTable
29
-
30
24
  def dispatch: (String request_bytes) -> String
31
25
 
32
26
  private
@@ -0,0 +1,6 @@
1
+ module Kobako
2
+ module RPC
3
+ class WireError < Kobako::SandboxError
4
+ end
5
+ end
6
+ end
@@ -30,6 +30,8 @@ module Kobako
30
30
 
31
31
  def stderr_truncated?: () -> bool
32
32
 
33
+ attr_reader usage: Kobako::Usage
34
+
33
35
  def define: (Symbol | String name) -> Kobako::RPC::Namespace
34
36
 
35
37
  def preload: (code: String, name: Symbol | String) -> Kobako::Sandbox
@@ -41,12 +43,16 @@ module Kobako
41
43
 
42
44
  private
43
45
 
44
- def clear_captures!: () -> void
46
+ def reset_invocation_state!: () -> void
45
47
 
46
48
  def begin_invocation!: () -> void
47
49
 
48
50
  def read_captures!: () -> void
49
51
 
52
+ def read_usage!: () -> void
53
+
54
+ def trap_class_for: (Kobako::Wasm::Error err) -> singleton(Kobako::TrapError)
55
+
50
56
  def invoke!: (Symbol verb) { () -> void } -> untyped
51
57
  end
52
58
  end
@@ -0,0 +1,11 @@
1
+ module Kobako
2
+ class Usage < Data
3
+ EMPTY: Usage
4
+
5
+ attr_reader wall_time: Float
6
+ attr_reader memory_peak: Integer
7
+
8
+ def self.new: (wall_time: Float, memory_peak: Integer) -> Usage
9
+ | (Float wall_time, Integer memory_peak) -> Usage
10
+ end
11
+ end
data/sig/kobako/wasm.rbs CHANGED
@@ -34,6 +34,8 @@ module Kobako
34
34
  def stderr: () -> [String, bool]
35
35
 
36
36
  def outcome!: () -> String
37
+
38
+ def usage: () -> [Float, Integer]
37
39
  end
38
40
  end
39
41
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobako
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aotokitsuruya
@@ -69,6 +69,8 @@ files:
69
69
  - lib/kobako/codec/factory.rb
70
70
  - lib/kobako/codec/utils.rb
71
71
  - lib/kobako/errors.rb
72
+ - lib/kobako/handle.rb
73
+ - lib/kobako/handle_table.rb
72
74
  - lib/kobako/invocation.rb
73
75
  - lib/kobako/outcome.rb
74
76
  - lib/kobako/outcome/panic.rb
@@ -76,16 +78,16 @@ files:
76
78
  - lib/kobako/rpc/dispatcher.rb
77
79
  - lib/kobako/rpc/envelope.rb
78
80
  - lib/kobako/rpc/fault.rb
79
- - lib/kobako/rpc/handle.rb
80
- - lib/kobako/rpc/handle_table.rb
81
81
  - lib/kobako/rpc/namespace.rb
82
82
  - lib/kobako/rpc/server.rb
83
+ - lib/kobako/rpc/wire_error.rb
83
84
  - lib/kobako/sandbox.rb
84
85
  - lib/kobako/sandbox_options.rb
85
86
  - lib/kobako/snippet.rb
86
87
  - lib/kobako/snippet/binary.rb
87
88
  - lib/kobako/snippet/source.rb
88
89
  - lib/kobako/snippet/table.rb
90
+ - lib/kobako/usage.rb
89
91
  - lib/kobako/version.rb
90
92
  - lib/kobako/wasm.rb
91
93
  - sig/kobako.rbs
@@ -96,6 +98,8 @@ files:
96
98
  - sig/kobako/codec/factory.rbs
97
99
  - sig/kobako/codec/utils.rbs
98
100
  - sig/kobako/errors.rbs
101
+ - sig/kobako/handle.rbs
102
+ - sig/kobako/handle_table.rbs
99
103
  - sig/kobako/invocation.rbs
100
104
  - sig/kobako/outcome.rbs
101
105
  - sig/kobako/outcome/panic.rbs
@@ -103,16 +107,16 @@ files:
103
107
  - sig/kobako/rpc/dispatcher.rbs
104
108
  - sig/kobako/rpc/envelope.rbs
105
109
  - sig/kobako/rpc/fault.rbs
106
- - sig/kobako/rpc/handle.rbs
107
- - sig/kobako/rpc/handle_table.rbs
108
110
  - sig/kobako/rpc/namespace.rbs
109
111
  - sig/kobako/rpc/server.rbs
112
+ - sig/kobako/rpc/wire_error.rbs
110
113
  - sig/kobako/sandbox.rbs
111
114
  - sig/kobako/sandbox_options.rbs
112
115
  - sig/kobako/snippet.rbs
113
116
  - sig/kobako/snippet/binary.rbs
114
117
  - sig/kobako/snippet/source.rbs
115
118
  - sig/kobako/snippet/table.rbs
119
+ - sig/kobako/usage.rbs
116
120
  - sig/kobako/wasm.rbs
117
121
  homepage: https://github.com/elct9620/kobako
118
122
  licenses:
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kobako
4
- module RPC
5
- # Wire-level value object for an ext-0x01 Capability Handle.
6
- #
7
- # SPEC pins the binary layout to fixext 4 with a 4-byte big-endian u32
8
- # payload ({docs/wire-codec.md}[link:../../../docs/wire-codec.md]
9
- # § Ext Types → ext 0x01). ID 0 is reserved as the invalid sentinel;
10
- # the maximum valid ID is 0x7fff_ffff (2^31 - 1).
11
- #
12
- # This is intentionally a thin value object built on +Data.define+ so
13
- # equality, hash, and immutability are inherited. The runtime-facing
14
- # +Kobako::RPC::Handle+ class lives at a higher layer and may add behaviour
15
- # (HandleTable bookkeeping, reset semantics). The codec only needs to
16
- # carry the opaque integer ID across the wire.
17
- Handle = Data.define(:id) do
18
- # +MIN_ID+ / +MAX_ID+ live on the Handle class (defined below this
19
- # block), not in this block's binding — Data.define's block scope
20
- # resolves bare constants against the enclosing +Wire+ module, so
21
- # +MIN_ID+ would raise +NameError+. Use +self.class::CONST+ to
22
- # reach the constants attached to the Handle class itself. Do not
23
- # "simplify" this back to bare +MIN_ID+/+MAX_ID+.
24
- # steep:ignore:start
25
- def initialize(id:)
26
- min = self.class::MIN_ID
27
- max = self.class::MAX_ID
28
- raise ArgumentError, "Handle id must be Integer" unless id.is_a?(Integer)
29
- raise ArgumentError, "Handle id #{id} out of range [#{min}, #{max}]" unless id.between?(min, max)
30
-
31
- super
32
- end
33
- # steep:ignore:end
34
- end
35
-
36
- Handle::MIN_ID = 1
37
- Handle::MAX_ID = 0x7fff_ffff
38
- end
39
- end