kobako 0.26.0 → 0.28.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +103 -0
  4. data/Cargo.lock +117 -140
  5. data/README.md +156 -118
  6. data/ROADMAP.md +23 -22
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +38 -0
  9. data/crates/kobako-runtime/Cargo.toml +2 -2
  10. data/crates/kobako-runtime/README.md +10 -12
  11. data/crates/kobako-runtime/src/error.rs +8 -9
  12. data/crates/kobako-runtime/src/lib.rs +1 -1
  13. data/crates/kobako-runtime/src/profile.rs +1 -1
  14. data/crates/kobako-runtime/src/runtime.rs +31 -17
  15. data/crates/kobako-runtime/src/snapshot.rs +13 -10
  16. data/crates/kobako-runtime/src/yielder.rs +9 -6
  17. data/crates/kobako-transport/CHANGELOG.md +35 -0
  18. data/crates/kobako-transport/Cargo.toml +1 -1
  19. data/crates/kobako-transport/README.md +43 -27
  20. data/crates/kobako-transport/src/abi.rs +11 -14
  21. data/crates/kobako-transport/src/envelope/bytes.rs +18 -6
  22. data/crates/kobako-transport/src/envelope/compile_reply.rs +139 -0
  23. data/crates/kobako-transport/src/envelope/outcome.rs +17 -16
  24. data/crates/kobako-transport/src/envelope/preamble.rs +513 -0
  25. data/crates/kobako-transport/src/envelope/reply.rs +77 -53
  26. data/crates/kobako-transport/src/envelope/request.rs +295 -0
  27. data/crates/kobako-transport/src/envelope/run.rs +1 -2
  28. data/crates/kobako-transport/src/envelope/tag.rs +49 -0
  29. data/crates/kobako-transport/src/envelope/unsupported.rs +105 -0
  30. data/crates/kobako-transport/src/envelope.rs +19 -12
  31. data/crates/kobako-transport/src/lib.rs +0 -2
  32. data/crates/kobako-wasmtime/CHANGELOG.md +51 -0
  33. data/crates/kobako-wasmtime/Cargo.toml +7 -5
  34. data/crates/kobako-wasmtime/README.md +8 -10
  35. data/crates/kobako-wasmtime/src/abi.rs +5 -8
  36. data/crates/kobako-wasmtime/src/ambient.rs +2 -2
  37. data/crates/kobako-wasmtime/src/cache.rs +25 -59
  38. data/crates/kobako-wasmtime/src/capture.rs +4 -11
  39. data/crates/kobako-wasmtime/src/config.rs +11 -14
  40. data/crates/kobako-wasmtime/src/dispatch.rs +70 -80
  41. data/crates/kobako-wasmtime/src/driver.rs +133 -86
  42. data/crates/kobako-wasmtime/src/exports.rs +88 -34
  43. data/crates/kobako-wasmtime/src/guest_mem.rs +145 -138
  44. data/crates/kobako-wasmtime/src/instance_pre.rs +11 -22
  45. data/crates/kobako-wasmtime/src/invocation.rs +55 -372
  46. data/crates/kobako-wasmtime/src/io.rs +286 -0
  47. data/crates/kobako-wasmtime/src/lib.rs +3 -24
  48. data/crates/kobako-wasmtime/src/limiter.rs +253 -0
  49. data/crates/kobako-wasmtime/src/trap.rs +32 -45
  50. data/crates/kobako-wasmtime/tests/preamble_flags.rs +69 -0
  51. data/crates/kobako-wasmtime/tests/snippet_compile.rs +94 -0
  52. data/crates/kobako-wasmtime/tests/support/mod.rs +34 -0
  53. data/crates/kobako-wasmtime/tests/unsupported_call.rs +50 -0
  54. data/data/kobako.wasm +0 -0
  55. data/ext/kobako/Cargo.toml +2 -2
  56. data/ext/kobako/src/runtime/bridge.rs +38 -47
  57. data/ext/kobako/src/runtime/errors.rs +26 -32
  58. data/ext/kobako/src/runtime/gvl.rs +4 -12
  59. data/ext/kobako/src/runtime.rs +169 -212
  60. data/lib/kobako/capture.rb +1 -4
  61. data/lib/kobako/catalog/extensions.rb +21 -59
  62. data/lib/kobako/catalog/flags.rb +41 -0
  63. data/lib/kobako/catalog/handles.rb +7 -54
  64. data/lib/kobako/catalog/services.rb +7 -61
  65. data/lib/kobako/catalog/snippets.rb +42 -66
  66. data/lib/kobako/catalog.rb +2 -10
  67. data/lib/kobako/codec/decoder.rb +9 -28
  68. data/lib/kobako/codec/encoder.rb +3 -22
  69. data/lib/kobako/codec/error.rb +6 -27
  70. data/lib/kobako/codec/ext_types.rb +72 -94
  71. data/lib/kobako/codec/handle_walk.rb +65 -130
  72. data/lib/kobako/codec/nesting.rb +3 -8
  73. data/lib/kobako/codec/state.rb +4 -10
  74. data/lib/kobako/codec/utils.rb +6 -34
  75. data/lib/kobako/codec.rb +3 -22
  76. data/lib/kobako/constant_name.rb +8 -0
  77. data/lib/kobako/context.rb +51 -112
  78. data/lib/kobako/errors.rb +51 -127
  79. data/lib/kobako/execution.rb +16 -21
  80. data/lib/kobako/extension.rb +23 -40
  81. data/lib/kobako/handle.rb +7 -12
  82. data/lib/kobako/outcome.rb +51 -80
  83. data/lib/kobako/payload/arguments.rb +1 -16
  84. data/lib/kobako/payload.rb +4 -12
  85. data/lib/kobako/pool.rb +23 -53
  86. data/lib/kobako/runtime.rb +7 -21
  87. data/lib/kobako/sandbox.rb +96 -133
  88. data/lib/kobako/sandbox_options.rb +38 -84
  89. data/lib/kobako/snippet/binary.rb +1 -16
  90. data/lib/kobako/snippet/source.rb +1 -16
  91. data/lib/kobako/snippet.rb +1 -9
  92. data/lib/kobako/transport/call.rb +1 -14
  93. data/lib/kobako/transport/dispatcher.rb +112 -195
  94. data/lib/kobako/transport/error.rb +6 -20
  95. data/lib/kobako/transport/exposure.rb +5 -10
  96. data/lib/kobako/transport/reflection.rb +39 -50
  97. data/lib/kobako/transport/run.rb +17 -71
  98. data/lib/kobako/transport/yielder.rb +21 -73
  99. data/lib/kobako/transport.rb +3 -15
  100. data/lib/kobako/unresolved.rb +4 -11
  101. data/lib/kobako/usage.rb +2 -6
  102. data/lib/kobako/version.rb +1 -1
  103. data/sig/kobako/catalog/extensions.rbs +2 -2
  104. data/sig/kobako/catalog/flags.rbs +17 -0
  105. data/sig/kobako/catalog/services.rbs +0 -6
  106. data/sig/kobako/catalog/snippets.rbs +4 -4
  107. data/sig/kobako/codec/ext_types.rbs +10 -8
  108. data/sig/kobako/codec/handle_walk.rbs +9 -7
  109. data/sig/kobako/codec/utils.rbs +2 -2
  110. data/sig/kobako/codec.rbs +2 -2
  111. data/sig/kobako/constant_name.rbs +3 -0
  112. data/sig/kobako/context.rbs +2 -3
  113. data/sig/kobako/errors.rbs +6 -0
  114. data/sig/kobako/outcome.rbs +6 -8
  115. data/sig/kobako/runtime.rbs +12 -8
  116. data/sig/kobako/sandbox.rbs +4 -0
  117. data/sig/kobako/sandbox_options.rbs +2 -6
  118. data/sig/kobako/transport/dispatcher.rbs +14 -18
  119. data/sig/kobako/transport/reflection.rbs +5 -3
  120. data/sig/kobako/transport/run.rbs +1 -3
  121. data/sig/kobako/transport/yielder.rbs +3 -3
  122. data/sig/kobako/transport.rbs +7 -6
  123. metadata +16 -4
  124. data/crates/kobako-transport/src/envelope/invocation_frames.rs +0 -241
  125. data/crates/kobako-wasmtime/src/frames.rs +0 -273
  126. data/rust-toolchain.toml +0 -11
@@ -1,44 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../constant_name"
3
4
  require_relative "../handle"
4
5
  require_relative "../codec"
5
6
  require_relative "../payload"
6
7
 
7
8
  module Kobako
8
- # See lib/kobako/transport.rb for the umbrella module doc; this file
9
- # owns +Run+, the host-side value object for one +#run+ request — the
10
- # native side frames it into the envelope +__kobako_run+ consumes.
11
9
  module Transport
12
- # Host-side value object for a single +Sandbox#run+ invocation
13
- # ({docs/wire-codec.md Invocation channels}[link:../../../docs/wire-codec.md]).
14
- #
15
- # A Run captures the host-layer concept of "a single +#run+
16
- # call": the entrypoint constant name plus its positional and keyword
17
- # arguments. Host pre-flight (entrypoint type / name pattern, forged
18
- # Handle, kwargs-key type) is enforced at construction so the Value
19
- # Object is the single source of truth — anything that passes
20
- # +Run.new+ is safe to ship to the guest.
21
- #
22
- # +#payload+ takes the invocation's +Catalog::Handles+ and routes any
23
- # non-wire-representable +args+ / +kwargs+ leaf through it as a
24
- # +Kobako::Handle+ — the symmetric counterpart of the guest→host wrap
25
- # path in the dispatcher. A +Kobako::Handle+ that arrives **already
26
- # constructed** in the caller's +args+ / +kwargs+ is rejected at
27
- # construction: legitimate Handles only enter Host App code through
28
- # error fields, so a Handle reaching the call site is by definition
29
- # smuggled in. +Runtime#run+ frames that payload with the entrypoint
30
- # into the Run envelope the +__kobako_run+ command buffer carries.
31
- #
32
- # Built on the +class X < Data.define(...)+ subclass form (the
33
- # Steep-friendly shape — see +.rubocop.yml+ for the rationale).
34
- class Run < Data.define(:entrypoint, :args, :kwargs)
35
- # Ruby constant-name pattern enforced on the +entrypoint+ Symbol.
36
- # Parallel to
37
- # +Kobako::Catalog::Snippets::NAME_PATTERN+; the two constants name the
38
- # same regex but cover distinct surfaces (snippet identity vs.
39
- # entrypoint resolution) so a future divergence stays local.
40
- NAME_PATTERN = /\A[A-Z]\w*\z/
41
-
10
+ # A Handle already in the arguments is refused, since a caller never
11
+ # legitimately holds one.
12
+ class Run < Data.define(:entrypoint, :args, :kwargs) # :nodoc:
42
13
  def initialize(entrypoint:, args: [], kwargs: {})
43
14
  entrypoint = normalize_entrypoint(entrypoint)
44
15
  validate_args!(args)
@@ -46,61 +17,42 @@ module Kobako
46
17
  super
47
18
  end
48
19
 
49
- # Encode this Run's arguments as the codec payload the Run
50
- # envelope carries — +Runtime#run+ frames it with the entrypoint.
51
- # Walks +args+ / +kwargs+ through Codec::HandleWalk.deep_wrap so
52
- # any non-wire-representable leaf is allocated into +handler+ and
53
- # replaced with a +Kobako::Handle+; the +handler+ argument is the
54
- # invocation's table, sharing the same allocator the guest→host
55
- # return path uses. A wrapped leaf rides as ext 0x01 in its
56
- # original position (docs/wire/payload-msgpack.md § ext 0x01).
57
- # The walk starts one level down, since +args+ and +kwargs+ ride
58
- # inside the payload document the wire's nesting bound is counted
59
- # from.
60
- def payload(handler)
20
+ # A leaf with no wire form becomes a Capability Handle in the
21
+ # invocation's table. The walk starts one level down, since +args+ and
22
+ # +kwargs+ ride inside the payload document the wire's nesting bound
23
+ # is counted from.
24
+ def payload(handles)
61
25
  Payload::Arguments.new(
62
- args: Codec::HandleWalk.deep_wrap(args, handler, 1),
63
- kwargs: Codec::HandleWalk.deep_wrap(kwargs, handler, 1)
26
+ args: Codec::HandleWalk.deep_wrap(args, handles, 1),
27
+ kwargs: Codec::HandleWalk.deep_wrap(kwargs, handles, 1)
64
28
  ).encode
65
29
  end
66
30
 
67
31
  private
68
32
 
69
- # The target must be a Symbol or String (TypeError, not
70
- # ArgumentError — the wrong-type case is a Host App programming
71
- # error before the run reaches the guest). After +.to_s+
72
- # the value must match NAME_PATTERN (ArgumentError), rejecting
73
- # +::+-segmented names and any non-constant form.
33
+ # A target of the wrong type raises TypeError, a Host App programming
34
+ # error caught before the run reaches the guest.
74
35
  def normalize_entrypoint(target)
75
36
  unless target.is_a?(Symbol) || target.is_a?(String)
76
37
  raise TypeError, "entrypoint must be a Symbol or String, got #{target.class}"
77
38
  end
78
39
 
79
40
  target_str = target.to_s
80
- unless NAME_PATTERN.match?(target_str)
41
+ unless CONSTANT_NAME.match?(target_str)
81
42
  raise ArgumentError,
82
- "entrypoint must match #{NAME_PATTERN.inspect} (got #{target.inspect})"
43
+ "entrypoint must match #{CONSTANT_NAME.inspect} (got #{target.inspect})"
83
44
  end
84
45
 
85
46
  target_str.to_sym
86
47
  end
87
48
 
88
- # +args+ must not contain a +Kobako::Handle+. The Handle
89
- # allocator lives inside the Host Gem; legitimate paths surface
90
- # Handle objects only through raised error fields, so a Handle
91
- # reaching +args+ is a forged or smuggled token. Non-wire-
92
- # representable arguments that are not Handles are handled by
93
- # auto-wrap inside +#payload+ — the reject path is reserved
94
- # for Handle objects specifically.
95
49
  def validate_args!(args)
96
50
  raise ArgumentError, "arguments must be an Array" unless args.is_a?(Array)
97
51
  raise ArgumentError, forged_handle_message("arguments") if args.any?(Kobako::Handle)
98
52
  end
99
53
 
100
- # Reject a non-Symbol kwargs key, and a +Kobako::Handle+ arriving
101
- # as a kwargs value (same forged-token principle as the +args+
102
- # branch). Both checks live here so the Host App sees the
103
- # host-side error message before any encode / decode boundary.
54
+ # Checked here so the Host App sees the host-side message before any
55
+ # encode boundary.
104
56
  def validate_kwargs!(kwargs)
105
57
  raise ArgumentError, "keyword arguments must be a Hash" unless kwargs.is_a?(Hash)
106
58
 
@@ -112,12 +64,6 @@ module Kobako
112
64
  raise ArgumentError, forged_handle_message("keyword argument values") if kwargs.each_value.any?(Kobako::Handle)
113
65
  end
114
66
 
115
- # Single source of truth for the forged-Handle reject message so the
116
- # args and kwargs branches stay phrased identically. Message stays in
117
- # caller vocabulary: it names the affected slot and the reason
118
- # without leaking internal SPEC identifiers or self-referential
119
- # architecture terms — the error is raised BY kobako, so saying
120
- # "allocated by the Host Gem" reads as third-person about self.
121
67
  def forged_handle_message(slot)
122
68
  "#{slot} must not contain a Kobako::Handle — " \
123
69
  "Handles are created internally by the Sandbox and cannot be passed in"
@@ -4,51 +4,19 @@ require_relative "../codec"
4
4
  require_relative "../errors"
5
5
 
6
6
  module Kobako
7
- # See lib/kobako/transport.rb for the umbrella module doc; this file
8
- # owns the host-side object that materialises a guest-supplied block as
9
- # a Ruby callable the Service method can yield into.
10
7
  module Transport
11
- # Host-side stand-in for a guest-supplied block.
12
- #
13
- # Each guest call that carries +block_given: true+ gets a Yielder
14
- # that the Dispatcher hands to the Service method as +&block+. The
15
- # Service method observes it as an ordinary Ruby Proc through
16
- # #to_proc; +yield val+ / +block.call(val)+ invokes #yield, which
17
- # serialises the positional args, re-enters the guest via the injected
18
- # +yield_to_guest+ lambda, and reifies the Yield Reply into Ruby
19
- # control flow:
20
- #
21
- # * ok — return the decoded value to +yield+'s caller
22
- # * break — +throw break_tag, value+ so the Dispatcher's +catch+
23
- # frame unwinds the Service method
24
- # * error — raise a BlockError at the Service's yield site,
25
- # carrying the guest's class on +#klass+
26
- #
27
- # The Dispatcher calls #invalidate! from its +ensure+ block once
28
- # dispatch completes; any later call to a stashed Yielder then raises
29
- # +LocalJumpError+ — the observable shape of an escaped Yielder.
30
- class Yielder
31
- # +yield_to_guest+ is the ext's per-dispatch
32
- # +Kobako::Runtime::GuestYielder+, which #yield invokes to re-enter
33
- # the guest: it takes the argument payload and answers the reply
34
- # already split into +[arm, body, class]+. +break_tag+ is the
35
- # +catch+ throw tag the Dispatcher matches against to unwind the
36
- # Service on a break. +handler+ is the invocation's +Kobako::Catalog::Handles+,
37
- # used to restore a Capability Handle in the block's ok value back to
38
- # its host object before it reaches the Service +yield+ site.
39
- def initialize(yield_to_guest, break_tag, handler)
40
- @yield_to_guest = yield_to_guest
8
+ class Yielder # :nodoc:
9
+ def initialize(guest_yielder, break_tag, handles)
10
+ @guest_yielder = guest_yielder
41
11
  @break_tag = break_tag
42
- @handler = handler
12
+ @handles = handles
43
13
  @active = true
44
14
  @raised = nil
45
15
  end
46
16
 
47
- # The text the guest's own block failure crosses back as, or +nil+
48
- # when +error+ is not the BlockError this Yielder raised. Identity
49
- # rather than class: a Service that rescued the block's failure and
50
- # raised its own has reported something else, and the guest must
51
- # hear about that instead.
17
+ # Identity rather than class: a Service that rescued the block's
18
+ # failure and raised its own has reported something else, and the
19
+ # guest must hear about that instead.
52
20
  #
53
21
  # The class travels with the message because not every block failure
54
22
  # is an exception the guest holds — a block value the guest refused
@@ -60,17 +28,15 @@ module Kobako
60
28
  "#{raised.klass}: #{raised.message}"
61
29
  end
62
30
 
63
- # Re-enter the guest with +args+ and reify the Yield Reply into
64
- # Ruby control flow. Raises +LocalJumpError+ if called after
65
- # #invalidate!. The ok value is consumed by the host Service
66
- # method, so a Capability Handle in it is restored to its host object.
67
- # The break value unwinds past the Service back to the guest
68
- # bound-constant call, so it passes through verbatim — a Handle stays a
69
- # Handle and rides back on the same id rather than churning a new one.
31
+ # The ok value is consumed by the host Service, so a Capability Handle
32
+ # in it is restored. The break value unwinds back to the guest, so it
33
+ # passes through verbatim and rides back on the same id rather than
34
+ # churning a new one.
70
35
  def yield(*args)
71
36
  raise LocalJumpError, "guest block invoked after host dispatch frame returned" unless @active
72
37
 
73
- arm, body, klass = @yield_to_guest.call(encode_args(args))
38
+ arm, body, klass = @guest_yielder.call(encode_args(args))
39
+ raise UnsupportedError, "the Sandbox runtime does not support yielding to a block" if arm == :unsupported
74
40
  raise remember(BlockError.new(body, klass: klass)) if arm == :error
75
41
 
76
42
  value, carried_handle = decode_body(body)
@@ -79,26 +45,18 @@ module Kobako
79
45
  restore(value, carried_handle)
80
46
  end
81
47
 
82
- # The Proc the Dispatcher passes as +&block+, binding #yield so a
83
- # Service method's +yield+ / +block.call+ drives the round-trip.
84
48
  def to_proc
85
49
  method(:yield).to_proc
86
50
  end
87
51
 
88
- # Mark this Yielder dead. Called by the Dispatcher's +ensure+ block
89
- # when the originating dispatch frame returns; any later #yield
90
- # call then raises +LocalJumpError+.
91
52
  def invalidate!
92
53
  @active = false
93
54
  end
94
55
 
95
56
  private
96
57
 
97
- # Encode what the Service is yielding. Arguments outside the wire
98
- # type set, or nesting past the wire's bound as one list, fail here,
99
- # before the guest is re-entered, and are restated so the Service
100
- # reads a refusal of its own argument rather than a codec class it
101
- # never named.
58
+ # Restated so the Service reads a refusal of its own argument rather
59
+ # than a codec class it never named.
102
60
  def encode_args(args)
103
61
  Kobako::Codec::Nesting.assert_within_bound!(args)
104
62
  Kobako::Codec::Encoder.encode(args)
@@ -106,33 +64,23 @@ module Kobako
106
64
  raise YieldValueError, "Service yielded a value the block cannot receive: #{e.message}"
107
65
  end
108
66
 
109
- # Hold onto the BlockError being raised so #fault_text can recognise
110
- # it if it comes back unrescued, and return it so the raise site reads
111
- # as one statement. Only the newest is kept: a Service that rescued
112
- # an earlier one and yielded again has already handled it.
67
+ # Only the newest is kept: a Service that rescued an earlier one and
68
+ # yielded again has already handled it.
113
69
  def remember(error)
114
70
  @raised = error
115
71
  end
116
72
 
117
- # Decode a value-carrying arm's payload, answering the value and
118
- # whether the decode carried a Capability Handle. The tracking
119
- # bracket opens only around this decode: the guest re-entry may run
120
- # nested dispatches whose own brackets would otherwise pollute the
121
- # signal.
73
+ # The tracking bracket opens only around this decode: the guest
74
+ # re-entry may run nested dispatches whose own brackets would
75
+ # otherwise pollute the signal.
122
76
  def decode_body(body)
123
77
  Kobako::Codec.track_handles { Kobako::Codec::Decoder.decode(body) }
124
78
  end
125
79
 
126
- # Restore any Capability Handle in a block's ok value to its host
127
- # object via the injected +Catalog::Handles+. Only the
128
- # ok path calls this — host code consumes the ok value, whereas a
129
- # break value returns to the guest and stays a Handle. A response
130
- # whose decode carried no Handle resolves to itself, so the walk is
131
- # skipped entirely.
132
80
  def restore(value, carried_handle)
133
81
  return value unless carried_handle
134
82
 
135
- Kobako::Codec::HandleWalk.deep_restore(value, @handler)
83
+ Kobako::Codec::HandleWalk.deep_restore(value, @handles)
136
84
  end
137
85
  end
138
86
  end
@@ -9,20 +9,8 @@ require_relative "transport/reflection"
9
9
  require_relative "transport/dispatcher"
10
10
 
11
11
  module Kobako
12
- # Kobako::Transport — host↔guest message transport namespace. Houses the
13
- # host side of one Call/Reply exchange: the call value objects +Call+
14
- # (guest→host, as the native side decoded it) and +Run+ (host→guest), the
15
- # +Dispatcher+ that answers a routed Call, the +Yielder+ that re-enters
16
- # the guest for a block, the +Reflection+ floor a dispatch must clear, the
17
- # +Exposure+ each reference authorizes a call against, and +Error+ for a
18
- # wire violation the host detects. Each invocation's
19
- # +Context+ composes them into the dispatch +Proc+ it passes +Runtime+ for
20
- # that run.
21
- #
22
- # "RPC" was deliberately not chosen — it implies a cross-process boundary that
23
- # kobako does not have, since host and guest share one OS thread and
24
- # one wasm linear memory. See
25
- # {SPEC.md Refinement → Internal Concepts}[link:../../SPEC.md].
26
- module Transport
12
+ # The host side of the guest↔host exchange. Only Transport::Error is
13
+ # public; the rest serves one invocation's dispatch.
14
+ module Transport # :nodoc:
27
15
  end
28
16
  end
@@ -1,17 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- # Kobako::Unresolved — the sentinel backing a fillable Service path: one
5
- # declared with +Sandbox#bind(path)+ carrying no object. It reserves the
6
- # path's Frame 1 slot so the guest sees the bound constant while the host
7
- # defers the object it stands for. A guest dispatch to an unfilled fillable
8
- # is refused as an unresolved target and surfaces as +Kobako::ServiceError+
9
- # when the guest leaves it unrescued — the same capability-failure channel
10
- # as an idiom with no backend bound.
11
- #
12
- # A single shared value; only its identity distinguishes it, so the dispatch
13
- # layer recognises it with +equal?+. A Host App may name it at a +bind+ site
14
- # to declare a fillable explicitly.
4
+ # Stands for the object of a fillable Service, one declared with
5
+ # Sandbox#bind and no object. Context#bind supplies the object for an
6
+ # invocation; a call to one left unfilled fails as a ServiceError. Passing
7
+ # it to Sandbox#bind declares a fillable explicitly.
15
8
  module Unresolved
16
9
  end
17
10
  end
data/lib/kobako/usage.rb CHANGED
@@ -12,14 +12,10 @@ module Kobako
12
12
  # cap, even when the cap was hit.
13
13
  #
14
14
  # Filled on every outcome, traps included, so a Host App that rescues a
15
- # trap can read from the error's +#execution+ how much of the budget the
15
+ # trap reads from the error's +execution+ how much of the budget the
16
16
  # invocation used.
17
- #
18
- # Built on the +class X < Data.define(...)+ subclass form (the
19
- # Steep-friendly shape — see +.rubocop.yml+ for the rationale).
20
17
  class Usage < Data.define(:wall_time, :memory_peak)
21
- # Pre-run sentinel. A fresh +Kobako::Context+ holds it until its guest
22
- # runs, so an Execution's +#usage+ is never +nil+.
18
+ # The usage of a run that has not started: no time, no memory.
23
19
  EMPTY = new(wall_time: 0.0, memory_peak: 0)
24
20
  end
25
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- VERSION = "0.26.0"
4
+ VERSION = "0.28.0"
5
5
  end
@@ -5,7 +5,7 @@ module Kobako
5
5
 
6
6
  def install: (untyped extension, snippets: Kobako::Catalog::Snippets, services: Kobako::Catalog::Services) -> self
7
7
 
8
- def seal!: () -> self
8
+ def assert_dependencies!: () -> self
9
9
 
10
10
  def resolve: () -> Hash[String, untyped]
11
11
 
@@ -17,7 +17,7 @@ module Kobako
17
17
 
18
18
  def validate!: (untyped extension) -> void
19
19
 
20
- def assert_dependencies!: () -> void
20
+ def unmet_dependency: () -> [untyped, untyped]?
21
21
 
22
22
  def symbolize: (untyped name) -> untyped
23
23
  end
@@ -0,0 +1,17 @@
1
+ module Kobako
2
+ module Catalog
3
+ class Flags
4
+ type tuple = [String, String, bool]
5
+
6
+ def initialize: () -> void
7
+
8
+ def entries: () -> Array[tuple]
9
+
10
+ def register: (Symbol | String name, String value, advisory: bool) -> nil
11
+
12
+ private
13
+
14
+ def utf8!: (String text, String what) -> String
15
+ end
16
+ end
17
+ end
@@ -1,8 +1,6 @@
1
1
  module Kobako
2
2
  module Catalog
3
3
  class Services
4
- NAME_PATTERN: Regexp
5
-
6
4
  def initialize: () -> void
7
5
 
8
6
  def bind: (Symbol | String path, untyped object) -> self
@@ -13,10 +11,6 @@ module Kobako
13
11
 
14
12
  def paths: () -> Array[String]
15
13
 
16
- def seal!: () -> self
17
-
18
- def sealed?: () -> bool
19
-
20
14
  private
21
15
 
22
16
  def validate_path!: (Symbol | String path) -> String
@@ -1,21 +1,21 @@
1
1
  module Kobako
2
2
  module Catalog
3
3
  class Snippets
4
- NAME_PATTERN: Regexp
5
-
6
4
  type entry = Kobako::Snippet::Source | Kobako::Snippet::Binary
7
5
 
8
6
  type tuple = [Symbol, String?, String]
9
7
 
10
8
  def initialize: () -> void
11
9
 
12
- def register: (?code: String?, ?name: (Symbol | String)?, ?binary: String?) -> (Symbol | nil)
10
+ def register: (?code: String?, ?name: (Symbol | String)?, ?binary: String?) -> nil
13
11
 
14
12
  def entries: () -> Array[tuple]
15
13
 
14
+ def compile: () { (Array[tuple] entries) -> Array[String]? } -> nil
15
+
16
16
  private
17
17
 
18
- def register_source!: (String? code, (Symbol | String)? name) -> Symbol
18
+ def register_source!: (String? code, (Symbol | String)? name) -> nil
19
19
 
20
20
  def ensure_source_args!: (String? code, (Symbol | String)? name) -> [String, Symbol | String]
21
21
 
@@ -5,21 +5,23 @@ module Kobako
5
5
  EXT_HANDLE: Integer
6
6
  UNREPRESENTABLE_GUARD_ID: Integer
7
7
 
8
- def self?.build_factory: () -> MessagePack::Factory
8
+ def self.build_factory: () -> MessagePack::Factory
9
9
 
10
- def self?.pack_symbol: (Symbol symbol) -> String
10
+ private
11
11
 
12
- def self?.unpack_symbol: (String payload) -> Symbol
12
+ def self.pack_symbol: (Symbol symbol) -> String
13
13
 
14
- def self?.pack_handle: (Kobako::Handle handle) -> String
14
+ def self.unpack_symbol: (String payload) -> Symbol
15
15
 
16
- def self?.unpack_handle: (String payload, State state) -> Kobako::Handle
16
+ def self.pack_handle: (Kobako::Handle handle) -> String
17
17
 
18
- def self?.register_symbol: (MessagePack::Factory factory) -> void
18
+ def self.unpack_handle: (String payload, State state) -> Kobako::Handle
19
19
 
20
- def self?.register_handle: (MessagePack::Factory factory) -> void
20
+ def self.register_symbol: (MessagePack::Factory factory) -> void
21
21
 
22
- def self?.register_unrepresentable: (MessagePack::Factory factory) -> void
22
+ def self.register_handle: (MessagePack::Factory factory) -> void
23
+
24
+ def self.register_unrepresentable: (MessagePack::Factory factory) -> void
23
25
  end
24
26
 
25
27
  FACTORY: MessagePack::Factory
@@ -3,19 +3,21 @@ module Kobako
3
3
  module HandleWalk
4
4
  MSGPACK_INT_RANGE: Range[Integer]
5
5
 
6
- def self?.representable?: (untyped value, ?Integer depth) -> bool
6
+ def self.representable?: (untyped value, ?Integer depth) -> bool
7
7
 
8
- def self?.deep_wrap: (untyped value, Kobako::Codec::_HandleTable handler, ?Integer depth) -> untyped
8
+ def self.deep_wrap: (untyped value, Kobako::Codec::_HandleTable handles, ?Integer depth) -> untyped
9
9
 
10
- def self?.guard_nesting!: (Integer depth) -> void
10
+ def self.deep_restore: (untyped value, Kobako::Codec::_HandleTable handles) -> untyped
11
11
 
12
- def self?.deep_wrap_hash: (Hash[untyped, untyped] hash, Kobako::Codec::_HandleTable handler, Integer depth) -> Hash[untyped, untyped]
12
+ private
13
13
 
14
- def self?.deep_restore: (untyped value, Kobako::Codec::_HandleTable handler) -> untyped
14
+ def self.guard_nesting!: (Integer depth) -> void
15
15
 
16
- def self?.primitive_type?: (untyped value) -> bool
16
+ def self.deep_wrap_hash: (Hash[untyped, untyped] hash, Kobako::Codec::_HandleTable handles, Integer depth) -> Hash[untyped, untyped]
17
17
 
18
- def self?.container_representable?: (untyped value, Integer depth) -> bool
18
+ def self.primitive_type?: (untyped value) -> bool
19
+
20
+ def self.container_representable?: (untyped value, Integer depth) -> bool
19
21
  end
20
22
  end
21
23
  end
@@ -1,9 +1,9 @@
1
1
  module Kobako
2
2
  module Codec
3
3
  module Utils
4
- def self?.assert_utf8!: (String string, String label) -> void
4
+ def self.assert_utf8!: (String string, String label) -> void
5
5
 
6
- def self?.with_boundary: [T] () { () -> T } -> T
6
+ def self.with_boundary: [T] () { () -> T } -> T
7
7
  end
8
8
  end
9
9
  end
data/sig/kobako/codec.rbs CHANGED
@@ -17,8 +17,8 @@ module Kobako
17
17
  # The Handle allocator/resolver a wire walk works against: alloc
18
18
  # registers a non-wire-representable object and returns its Handle,
19
19
  # fetch resolves a wire Handle id back to the live object. The
20
- # Sandbox's Kobako::Catalog::Handles is the production conformer;
21
- # modelled structurally so the Codec and Transport tiers need no
20
+ # Kobako::Catalog::Handles each invocation mints is the production
21
+ # conformer; modelled structurally so the Codec and Transport tiers need no
22
22
  # upward dependency on Catalog.
23
23
  interface _HandleTable
24
24
  def alloc: (untyped object) -> Kobako::Handle
@@ -0,0 +1,3 @@
1
+ module Kobako
2
+ CONSTANT_NAME: Regexp
3
+ end
@@ -4,6 +4,7 @@ module Kobako
4
4
  runtime: Kobako::Runtime,
5
5
  services: Kobako::Catalog::Services,
6
6
  snippets: Kobako::Catalog::Snippets,
7
+ flags: Kobako::Catalog::Flags,
7
8
  extensions: Kobako::Catalog::Extensions
8
9
  ) -> void
9
10
 
@@ -23,9 +24,7 @@ module Kobako
23
24
 
24
25
  def populate_observability!: (Kobako::Runtime::Snapshot snapshot) -> void
25
26
 
26
- def trap_class_for: (Kobako::TrapError err) -> singleton(Kobako::TrapError)
27
-
28
- def trap_error_for: (Kobako::Runtime::Snapshot snapshot, Symbol verb) -> Kobako::TrapError
27
+ def with_verb: (Kobako::TrapError err, Symbol verb) -> Kobako::TrapError
29
28
 
30
29
  def build_execution: (untyped value, failed: bool) -> Kobako::Execution
31
30
 
@@ -60,12 +60,18 @@ module Kobako
60
60
  class YieldValueError < Error
61
61
  end
62
62
 
63
+ class UnsupportedError < Error
64
+ end
65
+
63
66
  class HandleExhaustedError < SandboxError
64
67
  end
65
68
 
66
69
  class BytecodeError < SandboxError
67
70
  end
68
71
 
72
+ class UnsupportedFlagError < SandboxError
73
+ end
74
+
69
75
  class UndefinedEntrypointError < SandboxError
70
76
  attr_reader name: Symbol?
71
77
  attr_reader available: Array[Symbol]
@@ -6,18 +6,16 @@ module Kobako
6
6
 
7
7
  type panic_fields = [String, String, String, Array[String], Array[String]]
8
8
 
9
- def self?.reify: (Symbol kind, String payload, panic_fields? panic, ?entrypoint: Symbol?) -> Kobako::Codec::wire_value
9
+ def self.reify: (String payload, panic_fields? panic, ?entrypoint: Symbol?) -> Kobako::Codec::wire_value
10
10
 
11
- # Everything below is private_class_method at runtime; RBS cannot mark a
12
- # `self?` method private, so the declarations stay visible to the checker.
13
- def self?.panic_error: (panic_fields panic, Symbol? entrypoint) -> (SandboxError | ServiceError)
11
+ private
14
12
 
15
- def self?.error_class: (String origin, String klass) -> (singleton(SandboxError) | singleton(ServiceError))
13
+ def self.panic_error: (panic_fields panic, Symbol? entrypoint) -> (SandboxError | ServiceError)
16
14
 
17
- def self?.trap_error: (Symbol kind) -> TrapError
15
+ def self.error_class: (String origin, String klass) -> (singleton(SandboxError) | singleton(ServiceError))
18
16
 
19
- def self?.decode_value: (String payload) -> Kobako::Codec::wire_value
17
+ def self.decode_value: (String payload) -> Kobako::Codec::wire_value
20
18
 
21
- def self?.wire_error: (String message, ?diagnostic: String?) -> Transport::Error
19
+ def self.wire_error: (String message, ?diagnostic: String?) -> Transport::Error
22
20
  end
23
21
  end