kobako 0.9.2 → 0.11.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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +32 -0
- data/Cargo.lock +3 -1
- data/README.md +47 -19
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +12 -2
- data/ext/kobako/src/runtime/ambient.rs +1 -1
- data/ext/kobako/src/runtime/cache.rs +170 -6
- data/ext/kobako/src/runtime/capture.rs +1 -1
- data/ext/kobako/src/runtime/config.rs +3 -4
- data/ext/kobako/src/runtime/dispatch.rs +8 -8
- data/ext/kobako/src/runtime/exports.rs +32 -21
- data/ext/kobako/src/runtime/instance_pre.rs +97 -0
- data/ext/kobako/src/runtime/invocation.rs +36 -93
- data/ext/kobako/src/runtime/trap.rs +5 -5
- data/ext/kobako/src/runtime.rs +389 -403
- data/ext/kobako/src/snapshot.rs +2 -2
- data/lib/kobako/capture.rb +5 -7
- data/lib/kobako/catalog/handles.rb +28 -39
- data/lib/kobako/catalog/namespaces.rb +31 -20
- data/lib/kobako/catalog/snippets.rb +18 -16
- data/lib/kobako/codec/decoder.rb +5 -1
- data/lib/kobako/codec/utils.rb +6 -9
- data/lib/kobako/errors.rb +40 -36
- data/lib/kobako/handle.rb +2 -3
- data/lib/kobako/namespace.rb +17 -6
- data/lib/kobako/outcome.rb +12 -14
- data/lib/kobako/pool.rb +176 -0
- data/lib/kobako/sandbox.rb +68 -88
- data/lib/kobako/sandbox_options.rb +5 -9
- data/lib/kobako/snapshot.rb +2 -4
- data/lib/kobako/snippet/binary.rb +1 -3
- data/lib/kobako/snippet/source.rb +1 -2
- data/lib/kobako/snippet.rb +1 -2
- data/lib/kobako/transport/dispatcher.rb +39 -38
- data/lib/kobako/transport/request.rb +1 -1
- data/lib/kobako/transport/run.rb +23 -28
- data/lib/kobako/transport/yielder.rb +11 -17
- data/lib/kobako/transport.rb +2 -3
- data/lib/kobako/usage.rb +10 -13
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/release-please-config.json +16 -1
- data/sig/kobako/catalog/handles.rbs +0 -2
- data/sig/kobako/errors.rbs +3 -0
- data/sig/kobako/namespace.rbs +2 -0
- data/sig/kobako/pool.rbs +44 -0
- data/sig/kobako/sandbox.rbs +2 -2
- data/sig/kobako/transport/dispatcher.rbs +2 -0
- metadata +4 -1
|
@@ -20,8 +20,7 @@ module Kobako
|
|
|
20
20
|
# The module is stateless — all mutable state is threaded through
|
|
21
21
|
# arguments so Dispatcher has no instance variables and no side
|
|
22
22
|
# effects beyond mutating the Catalog::Handles via +alloc+ when a
|
|
23
|
-
# non-wire-representable return value must be wrapped
|
|
24
|
-
# ({docs/behavior.md B-14}[link:../../../docs/behavior.md]).
|
|
23
|
+
# non-wire-representable return value must be wrapped.
|
|
25
24
|
#
|
|
26
25
|
# Entry point:
|
|
27
26
|
#
|
|
@@ -29,7 +28,7 @@ module Kobako
|
|
|
29
28
|
# # => msgpack-encoded Response bytes (never raises)
|
|
30
29
|
module Dispatcher
|
|
31
30
|
# Throw tag for the {Yielder}'s break unwind back to the
|
|
32
|
-
# dispatcher's +catch+ frame
|
|
31
|
+
# dispatcher's +catch+ frame. +private_constant+ is a
|
|
33
32
|
# convention boundary — not a defence.
|
|
34
33
|
BREAK_THROW = :__kobako_break__
|
|
35
34
|
private_constant :BREAK_THROW
|
|
@@ -38,16 +37,14 @@ module Kobako
|
|
|
38
37
|
|
|
39
38
|
# Internal sentinel raised when target resolution fails. Mapped to
|
|
40
39
|
# Response.error with type="undefined". Contained at the wire boundary —
|
|
41
|
-
# not part of the public Kobako error taxonomy
|
|
42
|
-
# ({docs/behavior.md E-12}[link:../../../docs/behavior.md]).
|
|
40
|
+
# not part of the public Kobako error taxonomy.
|
|
43
41
|
class UndefinedTargetError < StandardError; end
|
|
44
42
|
|
|
45
43
|
# Modules whose instance methods are ambient Ruby reflection /
|
|
46
44
|
# metaprogramming surface (+send+, +public_send+, +instance_eval+,
|
|
47
45
|
# +method+, +tap+, +instance_variable_get+, ...) rather than Service
|
|
48
46
|
# behaviour. A guest-supplied method name resolving to one of these is
|
|
49
|
-
# rejected
|
|
50
|
-
# only methods the bound object itself exposes as Service behaviour are
|
|
47
|
+
# rejected: only methods the bound object itself exposes as Service behaviour are
|
|
51
48
|
# reachable, and +public_send(:send, ...)+ would otherwise let a guest
|
|
52
49
|
# pivot through +send+ into the private +Kernel#eval+ / +#system+
|
|
53
50
|
# surface (host RCE).
|
|
@@ -58,7 +55,7 @@ module Kobako
|
|
|
58
55
|
# (+Proc#binding+ reaches +Binding#eval+, +Method#receiver+ / +#unbind+
|
|
59
56
|
# hand back the underlying object) rather than Service behaviour. Only
|
|
60
57
|
# {CALLABLE_ALLOW} is reachable on a target of these types; a bound
|
|
61
|
-
# lambda stays invocable, its reflective surface does not
|
|
58
|
+
# lambda stays invocable, its reflective surface does not.
|
|
62
59
|
GADGET_OWNERS = [Proc, Method, UnboundMethod, Binding].freeze
|
|
63
60
|
private_constant :GADGET_OWNERS
|
|
64
61
|
|
|
@@ -69,8 +66,7 @@ module Kobako
|
|
|
69
66
|
private_constant :CALLABLE_ALLOW
|
|
70
67
|
|
|
71
68
|
# Dispatch a single transport request and return the encoded
|
|
72
|
-
# Response bytes
|
|
73
|
-
# Invoked from the +Runtime#on_dispatch+ Proc that
|
|
69
|
+
# Response bytes. Invoked from the +Runtime#on_dispatch+ Proc that
|
|
74
70
|
# +Kobako::Sandbox#initialize+ installs on the ext side; +namespaces+,
|
|
75
71
|
# +handler+, and +yield_to_guest+ are captured in that Proc's
|
|
76
72
|
# closure so the Dispatcher stays stateless and the registry doesn't
|
|
@@ -99,18 +95,17 @@ module Kobako
|
|
|
99
95
|
# round-trip back to the host-side Ruby object before the call
|
|
100
96
|
# reaches +public_send+.
|
|
101
97
|
def resolve_call_args(request, handler)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
[args, kwargs]
|
|
98
|
+
[request.args.map { |v| resolve_arg(v, handler) },
|
|
99
|
+
request.kwargs.transform_values { |v| resolve_arg(v, handler) }]
|
|
105
100
|
end
|
|
106
101
|
|
|
107
102
|
# Map an error caught at the dispatch boundary to a +Response.error+
|
|
108
103
|
# envelope. +error+ is the +StandardError+ caught by {#dispatch}'s
|
|
109
|
-
# rescue. Returns a msgpack-encoded Response envelope (binary).
|
|
110
|
-
# error buckets
|
|
104
|
+
# rescue. Returns a msgpack-encoded Response envelope (binary). Four
|
|
105
|
+
# error buckets:
|
|
111
106
|
# +Kobako::Codec::Error+ → type="runtime" (malformed request);
|
|
112
|
-
# +UndefinedTargetError+ → type="undefined"
|
|
113
|
-
# type="argument" (
|
|
107
|
+
# +UndefinedTargetError+ → type="undefined"; +ArgumentError+ →
|
|
108
|
+
# type="argument" (arity mismatch); everything else →
|
|
114
109
|
# type="runtime".
|
|
115
110
|
def encode_caught_error(error)
|
|
116
111
|
case error
|
|
@@ -129,14 +124,14 @@ module Kobako
|
|
|
129
124
|
# uniform empty-map shape.
|
|
130
125
|
#
|
|
131
126
|
# +yielder+ is the host-side {Yielder} materialised when the guest
|
|
132
|
-
# call site supplied a block
|
|
133
|
-
# B-23}[link:../../../docs/behavior.md]); its {Yielder#to_proc}
|
|
127
|
+
# call site supplied a block; its {Yielder#to_proc}
|
|
134
128
|
# rides the +&block+ slot. +&nil+ is a no-op block argument in Ruby,
|
|
135
129
|
# so the same call site handles both cases without an explicit
|
|
136
130
|
# conditional.
|
|
137
131
|
def invoke(target, method, args, kwargs, yielder = nil)
|
|
138
132
|
name = method.to_sym
|
|
139
133
|
reject_meta_method!(target, name)
|
|
134
|
+
reject_unexposed!(target, name)
|
|
140
135
|
block = yielder&.to_proc
|
|
141
136
|
if kwargs.empty?
|
|
142
137
|
target.public_send(name, *args, &block)
|
|
@@ -145,9 +140,8 @@ module Kobako
|
|
|
145
140
|
end
|
|
146
141
|
end
|
|
147
142
|
|
|
148
|
-
# Guard the +public_send+ below against ambient reflection methods
|
|
149
|
-
#
|
|
150
|
-
# method whose owner is a {META_OWNERS} or {GADGET_OWNERS} module is
|
|
143
|
+
# Guard the +public_send+ below against ambient reflection methods.
|
|
144
|
+
# A public method whose owner is a {META_OWNERS} or {GADGET_OWNERS} module is
|
|
151
145
|
# rejected, except {CALLABLE_ALLOW} on a gadget target (a bound lambda
|
|
152
146
|
# stays invocable). A name with no concrete public method is allowed
|
|
153
147
|
# only when the target opts into it via +respond_to?+ (dynamic
|
|
@@ -166,17 +160,26 @@ module Kobako
|
|
|
166
160
|
raise UndefinedTargetError, "no public method #{name.inspect} on target"
|
|
167
161
|
end
|
|
168
162
|
|
|
169
|
-
#
|
|
163
|
+
# Consult the target's opt-in narrowing predicate. A bound object
|
|
164
|
+
# may define a private +respond_to_guest?(name)+ to restrict which of its
|
|
165
|
+
# methods the guest reaches; a falsy answer rejects the dispatch.
|
|
166
|
+
# The predicate composes beneath {#reject_meta_method!} — it only narrows,
|
|
167
|
+
# never re-opening the reflection surface the floor rejects — and is
|
|
168
|
+
# consulted with the private surface included so the guest's +public_send+
|
|
169
|
+
# dispatch can never reach +respond_to_guest?+ itself.
|
|
170
|
+
def reject_unexposed!(target, name)
|
|
171
|
+
return unless target.respond_to?(:respond_to_guest?, true)
|
|
172
|
+
return if target.__send__(:respond_to_guest?, name)
|
|
173
|
+
|
|
174
|
+
raise UndefinedTargetError, "method #{name.inspect} is not exposed to the guest"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# A Kobako::Handle arriving as a positional or keyword
|
|
170
178
|
# argument identifies a host-side object previously allocated by a prior
|
|
171
|
-
# transport call's Handle wrap
|
|
179
|
+
# transport call's Handle wrap. Resolve it back to the Ruby object before
|
|
172
180
|
# the dispatch reaches +public_send+.
|
|
173
181
|
def resolve_arg(value, handler)
|
|
174
|
-
|
|
175
|
-
when Kobako::Handle
|
|
176
|
-
require_live_object!(value.id, handler)
|
|
177
|
-
else
|
|
178
|
-
value
|
|
179
|
-
end
|
|
182
|
+
value.is_a?(Kobako::Handle) ? require_live_object!(value.id, handler) : value
|
|
180
183
|
end
|
|
181
184
|
|
|
182
185
|
# Resolve a Request target to the Ruby object the registry (or
|
|
@@ -205,7 +208,7 @@ module Kobako
|
|
|
205
208
|
require_live_object!(handle.id, handler)
|
|
206
209
|
end
|
|
207
210
|
|
|
208
|
-
# Resolve +id+ through the Catalog::Handles. An unknown id
|
|
211
|
+
# Resolve +id+ through the Catalog::Handles. An unknown id
|
|
209
212
|
# surfaces as UndefinedTargetError.
|
|
210
213
|
def require_live_object!(id, handler)
|
|
211
214
|
handler.fetch(id)
|
|
@@ -214,11 +217,10 @@ module Kobako
|
|
|
214
217
|
end
|
|
215
218
|
|
|
216
219
|
# Encode +value+ as a +Response.ok+ envelope. When the value is not
|
|
217
|
-
# wire-representable per
|
|
218
|
-
#
|
|
220
|
+
# wire-representable per the codec's type mapping, the
|
|
221
|
+
# +UnsupportedType+ rescue routes it through the
|
|
219
222
|
# Catalog::Handles via {#wrap_as_handle} and re-encodes with the Capability
|
|
220
|
-
# Handle in place
|
|
221
|
-
# path encodes exactly once.
|
|
223
|
+
# Handle in place. The happy path encodes exactly once.
|
|
222
224
|
def encode_ok(value, handler)
|
|
223
225
|
response = Kobako::Transport::Response.ok(value)
|
|
224
226
|
response.encode
|
|
@@ -227,9 +229,8 @@ module Kobako
|
|
|
227
229
|
end
|
|
228
230
|
|
|
229
231
|
# Allocate +value+ in the Sandbox's Catalog::Handles and return a +Handle+
|
|
230
|
-
# that the wire codec can carry
|
|
231
|
-
#
|
|
232
|
-
# representation.
|
|
232
|
+
# that the wire codec can carry. Used as the fallback path of
|
|
233
|
+
# {#encode_ok} when +value+ has no wire representation.
|
|
233
234
|
def wrap_as_handle(value, handler)
|
|
234
235
|
handler.alloc(value)
|
|
235
236
|
end
|
|
@@ -16,7 +16,7 @@ module Kobako
|
|
|
16
16
|
# or a {Handle}. SPEC pins +kwargs+ map keys to ext 0x00 Symbol;
|
|
17
17
|
# enforced at construction so the Value Object is the single source of
|
|
18
18
|
# truth. +block_given+ is a Boolean signalling whether the guest call
|
|
19
|
-
# site supplied a block
|
|
19
|
+
# site supplied a block; the block body itself never crosses the
|
|
20
20
|
# wire.
|
|
21
21
|
#
|
|
22
22
|
# Built on the +class X < Data.define(...)+ subclass form so the
|
data/lib/kobako/transport/run.rb
CHANGED
|
@@ -9,26 +9,24 @@ module Kobako
|
|
|
9
9
|
# consumed by +__kobako_run+.
|
|
10
10
|
module Transport
|
|
11
11
|
# Host-side value object for a single +Sandbox#run+ invocation
|
|
12
|
-
# ({docs/wire-codec.md Invocation channels}[link:../../../docs/wire-codec.md]
|
|
13
|
-
# {docs/behavior.md B-31}[link:../../../docs/behavior.md]).
|
|
12
|
+
# ({docs/wire-codec.md Invocation channels}[link:../../../docs/wire-codec.md]).
|
|
14
13
|
#
|
|
15
14
|
# A Run captures the host-layer concept of "a single +#run+
|
|
16
15
|
# call": the entrypoint constant name plus its positional and keyword
|
|
17
|
-
# arguments. Host pre-flight (
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# the guest.
|
|
16
|
+
# arguments. Host pre-flight (entrypoint type / name pattern, forged
|
|
17
|
+
# Handle, kwargs-key type) is enforced at construction so the Value
|
|
18
|
+
# Object is the single source of truth — anything that passes
|
|
19
|
+
# +Run.new+ is safe to encode and ship to the guest.
|
|
21
20
|
#
|
|
22
21
|
# Run is the host→guest entrypoint dispatch envelope (the +#run+
|
|
23
22
|
# request shape), the symmetric counterpart to the guest→host
|
|
24
23
|
# +Request+ envelope. +#encode+ takes the Sandbox's
|
|
25
24
|
# +Catalog::Handles+ and routes any non-wire-representable +args+ /
|
|
26
|
-
# +kwargs+ leaf through it as a +Kobako::Handle+
|
|
27
|
-
# ({docs/behavior.md B-34}[link:../../../docs/behavior.md]) — the
|
|
25
|
+
# +kwargs+ leaf through it as a +Kobako::Handle+ — the
|
|
28
26
|
# symmetric counterpart of the guest→host wrap path in the
|
|
29
|
-
# dispatcher
|
|
27
|
+
# dispatcher. A +Kobako::Handle+ that arrives **already
|
|
30
28
|
# constructed** in the caller's +args+ / +kwargs+ is rejected at
|
|
31
|
-
# construction
|
|
29
|
+
# construction: legitimate Handles only enter Host App code
|
|
32
30
|
# through error fields, so a Handle reaching the call site is by
|
|
33
31
|
# definition smuggled in. The +#encode+ output is the "Run envelope"
|
|
34
32
|
# that ships through the +__kobako_run+ command buffer.
|
|
@@ -36,8 +34,8 @@ module Kobako
|
|
|
36
34
|
# Built on the +class X < Data.define(...)+ subclass form (the
|
|
37
35
|
# Steep-friendly shape — see +lib/kobako/outcome/panic.rb+).
|
|
38
36
|
class Run < Data.define(:entrypoint, :args, :kwargs)
|
|
39
|
-
# Ruby constant-name pattern enforced on the +entrypoint+ Symbol
|
|
40
|
-
#
|
|
37
|
+
# Ruby constant-name pattern enforced on the +entrypoint+ Symbol.
|
|
38
|
+
# Parallel to
|
|
41
39
|
# +Kobako::Catalog::Snippets::NAME_PATTERN+; the two constants name the
|
|
42
40
|
# same regex but cover distinct surfaces (snippet identity vs.
|
|
43
41
|
# entrypoint resolution) so a future divergence stays local.
|
|
@@ -55,10 +53,9 @@ module Kobako
|
|
|
55
53
|
# ({docs/wire-codec.md Invocation channels}[link:../../../docs/wire-codec.md]).
|
|
56
54
|
# Walks +args+ / +kwargs+ through {Codec::Utils.deep_wrap} so any
|
|
57
55
|
# non-wire-representable leaf is allocated into +handler+ and
|
|
58
|
-
# replaced with a +Kobako::Handle
|
|
59
|
-
# ({docs/behavior.md B-34}[link:../../../docs/behavior.md]); the
|
|
56
|
+
# replaced with a +Kobako::Handle+; the
|
|
60
57
|
# +handler+ argument is the Sandbox's table, sharing the same
|
|
61
|
-
# allocator the guest→host return path
|
|
58
|
+
# allocator the guest→host return path uses.
|
|
62
59
|
#
|
|
63
60
|
# Layout: msgpack map with string keys +"entrypoint"+ (Symbol via
|
|
64
61
|
# ext 0x00), +"args"+ (Array), +"kwargs"+ (Map with Symbol keys);
|
|
@@ -74,9 +71,9 @@ module Kobako
|
|
|
74
71
|
|
|
75
72
|
private
|
|
76
73
|
|
|
77
|
-
#
|
|
74
|
+
# The target must be a Symbol or String (TypeError, not
|
|
78
75
|
# ArgumentError — the wrong-type case is a Host App programming
|
|
79
|
-
# error before the run reaches the guest).
|
|
76
|
+
# error before the run reaches the guest). After +.to_s+
|
|
80
77
|
# the value must match NAME_PATTERN (ArgumentError), rejecting
|
|
81
78
|
# +::+-segmented names and any non-constant form.
|
|
82
79
|
def normalize_entrypoint(target)
|
|
@@ -93,12 +90,12 @@ module Kobako
|
|
|
93
90
|
target_str.to_sym
|
|
94
91
|
end
|
|
95
92
|
|
|
96
|
-
#
|
|
93
|
+
# +args+ must not contain a +Kobako::Handle+. The Handle
|
|
97
94
|
# allocator lives inside the Host Gem; legitimate paths surface
|
|
98
95
|
# Handle objects only through raised error fields, so a Handle
|
|
99
96
|
# reaching +args+ is a forged or smuggled token. Non-wire-
|
|
100
97
|
# representable arguments that are not Handles are handled by
|
|
101
|
-
# auto-wrap inside +#encode+
|
|
98
|
+
# auto-wrap inside +#encode+ — the reject path is reserved
|
|
102
99
|
# for Handle objects specifically.
|
|
103
100
|
def validate_args!(args)
|
|
104
101
|
raise ArgumentError, "arguments must be an Array" unless args.is_a?(Array)
|
|
@@ -107,11 +104,10 @@ module Kobako
|
|
|
107
104
|
args
|
|
108
105
|
end
|
|
109
106
|
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
# decode boundary.
|
|
107
|
+
# Reject a non-Symbol kwargs key, and a +Kobako::Handle+ arriving
|
|
108
|
+
# as a kwargs value (same forged-token principle as the +args+
|
|
109
|
+
# branch). Both checks live here so the Host App sees the
|
|
110
|
+
# host-side error message before any encode / decode boundary.
|
|
115
111
|
def validate_kwargs!(kwargs)
|
|
116
112
|
raise ArgumentError, "keyword arguments must be a Hash" unless kwargs.is_a?(Hash)
|
|
117
113
|
|
|
@@ -125,11 +121,10 @@ module Kobako
|
|
|
125
121
|
kwargs
|
|
126
122
|
end
|
|
127
123
|
|
|
128
|
-
# Single source of truth for the
|
|
129
|
-
# and kwargs branches stay phrased identically. Message stays in
|
|
124
|
+
# Single source of truth for the forged-Handle reject message so the
|
|
125
|
+
# args and kwargs branches stay phrased identically. Message stays in
|
|
130
126
|
# caller vocabulary: it names the affected slot and the reason
|
|
131
|
-
# without leaking SPEC
|
|
132
|
-
# source comments, not user-visible errors) or self-referential
|
|
127
|
+
# without leaking internal SPEC identifiers or self-referential
|
|
133
128
|
# architecture terms — the error is raised BY kobako, so saying
|
|
134
129
|
# "allocated by the Host Gem" reads as third-person about self.
|
|
135
130
|
def forged_handle_message(slot)
|
|
@@ -8,29 +8,25 @@ module Kobako
|
|
|
8
8
|
# owns the host-side object that materialises a guest-supplied block as
|
|
9
9
|
# a Ruby callable the Service method can yield into.
|
|
10
10
|
module Transport
|
|
11
|
-
# Host-side stand-in for a guest-supplied block
|
|
11
|
+
# Host-side stand-in for a guest-supplied block.
|
|
12
12
|
#
|
|
13
13
|
# Each guest call that carries +block_given: true+ gets a Yielder
|
|
14
14
|
# that the Dispatcher hands to the Service method as +&block+. The
|
|
15
15
|
# Service method observes it as an ordinary Ruby Proc through
|
|
16
16
|
# {#to_proc}; +yield val+ / +block.call(val)+ invokes {#yield}, which
|
|
17
17
|
# serialises the positional args, re-enters the guest via the injected
|
|
18
|
-
# +yield_to_guest+ lambda
|
|
19
|
-
#
|
|
20
|
-
# reifies the +YieldResponse+ into Ruby control flow:
|
|
18
|
+
# +yield_to_guest+ lambda, and reifies the +YieldResponse+ into Ruby
|
|
19
|
+
# control flow:
|
|
21
20
|
#
|
|
22
21
|
# * +tag 0x01+ ok — return the decoded value to +yield+'s caller
|
|
23
22
|
# * +tag 0x02+ break — +throw break_tag, value+ so the Dispatcher's
|
|
24
23
|
# +catch+ frame unwinds the Service method
|
|
25
|
-
# ({docs/behavior.md B-25}[link:../../../docs/behavior.md])
|
|
26
24
|
# * +tag 0x04+ error — raise the +{class, message}+ payload at the
|
|
27
25
|
# Service's yield site
|
|
28
26
|
#
|
|
29
27
|
# The Dispatcher calls {#invalidate!} from its +ensure+ block once
|
|
30
28
|
# dispatch completes; any later call to a stashed Yielder then raises
|
|
31
|
-
# +LocalJumpError+ — the observable shape of
|
|
32
|
-
# {docs/behavior.md E-23}[link:../../../docs/behavior.md] (escaped
|
|
33
|
-
# Yielder).
|
|
29
|
+
# +LocalJumpError+ — the observable shape of an escaped Yielder.
|
|
34
30
|
class Yielder
|
|
35
31
|
# +yield_to_guest+ is a +String → String+ callable (typically
|
|
36
32
|
# +Runtime#yield_to_active_invocation+ bound through a lambda) that
|
|
@@ -38,8 +34,7 @@ module Kobako
|
|
|
38
34
|
# throw tag the Dispatcher matches against to unwind the Service on
|
|
39
35
|
# +tag 0x02+. +handler+ is the Sandbox's +Kobako::Catalog::Handles+,
|
|
40
36
|
# used to restore a Capability Handle in the block's ok value back to
|
|
41
|
-
# its host object before it reaches the Service +yield+ site
|
|
42
|
-
# ({docs/behavior.md B-37}[link:../../../docs/behavior.md]).
|
|
37
|
+
# its host object before it reaches the Service +yield+ site.
|
|
43
38
|
def initialize(yield_to_guest, break_tag, handler)
|
|
44
39
|
@yield_to_guest = yield_to_guest
|
|
45
40
|
@break_tag = break_tag
|
|
@@ -49,10 +44,10 @@ module Kobako
|
|
|
49
44
|
|
|
50
45
|
# Re-enter the guest with +args+ and reify the YieldResponse into
|
|
51
46
|
# Ruby control flow. Raises +LocalJumpError+ if called after
|
|
52
|
-
# {#invalidate!}
|
|
53
|
-
# method, so a Capability Handle in it is restored to its host object
|
|
54
|
-
#
|
|
55
|
-
# Member call
|
|
47
|
+
# {#invalidate!}. The ok value is consumed by the host Service
|
|
48
|
+
# method, so a Capability Handle in it is restored to its host object.
|
|
49
|
+
# The break value unwinds past the Service back to the guest
|
|
50
|
+
# Member call, so it passes through verbatim — a Handle stays a
|
|
56
51
|
# Handle and rides back on the same id rather than churning a new one.
|
|
57
52
|
def yield(*args)
|
|
58
53
|
raise LocalJumpError, "guest block invoked after host dispatch frame returned" unless @active
|
|
@@ -73,7 +68,7 @@ module Kobako
|
|
|
73
68
|
|
|
74
69
|
# Mark this Yielder dead. Called by the Dispatcher's +ensure+ block
|
|
75
70
|
# when the originating dispatch frame returns; any later {#yield}
|
|
76
|
-
# call then raises +LocalJumpError
|
|
71
|
+
# call then raises +LocalJumpError+.
|
|
77
72
|
def invalidate!
|
|
78
73
|
@active = false
|
|
79
74
|
end
|
|
@@ -81,8 +76,7 @@ module Kobako
|
|
|
81
76
|
private
|
|
82
77
|
|
|
83
78
|
# Restore any Capability Handle in a block's ok value to its host
|
|
84
|
-
# object via the injected +Catalog::Handles
|
|
85
|
-
# ({docs/behavior.md B-37}[link:../../../docs/behavior.md]). Only the
|
|
79
|
+
# object via the injected +Catalog::Handles+. Only the
|
|
86
80
|
# ok path calls this — host code consumes the ok value, whereas a
|
|
87
81
|
# break value returns to the guest and stays a Handle. Walks nested
|
|
88
82
|
# Array / Hash one level at a time; a plain value passes through
|
data/lib/kobako/transport.rb
CHANGED
|
@@ -13,9 +13,8 @@ module Kobako
|
|
|
13
13
|
# Houses the envelope value objects (Request / Response / Run / Yield),
|
|
14
14
|
# the guest→host +Dispatcher+, and the host→guest +Yielder+.
|
|
15
15
|
# +Sandbox#initialize+ composes them onto the
|
|
16
|
-
# +Runtime+ as a dispatch +Proc+ + +yield_to_guest+ lambda pair
|
|
17
|
-
#
|
|
18
|
-
# deliberately not chosen — it implies a cross-process boundary that
|
|
16
|
+
# +Runtime+ as a dispatch +Proc+ + +yield_to_guest+ lambda pair.
|
|
17
|
+
# "RPC" was deliberately not chosen — it implies a cross-process boundary that
|
|
19
18
|
# kobako does not have, since host and guest share one OS thread and
|
|
20
19
|
# one wasm linear memory. See
|
|
21
20
|
# {SPEC.md Refinement → Internal Concepts}[link:../../SPEC.md].
|
data/lib/kobako/usage.rb
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Kobako
|
|
4
|
-
# Per-last-invocation resource accounting for a +Kobako::Sandbox
|
|
5
|
-
#
|
|
6
|
-
# readers populated by every +#eval+ / +#run+ invocation:
|
|
4
|
+
# Per-last-invocation resource accounting for a +Kobako::Sandbox+.
|
|
5
|
+
# Carries two readers populated by every +#eval+ / +#run+ invocation:
|
|
7
6
|
#
|
|
8
7
|
# * +wall_time+ — the Float number of seconds the guest export call
|
|
9
8
|
# 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
|
|
9
|
+
# measurement bracket aligns with the +timeout+ deadline; time spent
|
|
12
10
|
# in host Service callbacks is included, but everything that runs
|
|
13
11
|
# after the guest export returns — the post-export
|
|
14
12
|
# +OUTCOME_BUFFER+ fetch and decode, plus stdout / stderr capture
|
|
@@ -16,11 +14,11 @@ module Kobako
|
|
|
16
14
|
# * +memory_peak+ — the Integer high-water mark, in bytes, of the
|
|
17
15
|
# per-invocation +memory.grow+ delta past the linear-memory size
|
|
18
16
|
# captured at invocation entry. Same baseline accounting as
|
|
19
|
-
# +memory_limit
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# +
|
|
23
|
-
#
|
|
17
|
+
# +memory_limit+: the mruby image's initial allocation and any
|
|
18
|
+
# prior-invocation watermark sit outside the measurement. On
|
|
19
|
+
# +MemoryLimitError+ +memory_peak+ never exceeds the configured
|
|
20
|
+
# cap because the rejected +desired+ value is not promoted into
|
|
21
|
+
# the high-water.
|
|
24
22
|
#
|
|
25
23
|
# Both readers are populated on every outcome, including +TrapError+
|
|
26
24
|
# branches, so the Host App can read +Sandbox#usage+ after rescuing a
|
|
@@ -31,9 +29,8 @@ module Kobako
|
|
|
31
29
|
# Built on the +class X < Data.define(...)+ subclass form (the
|
|
32
30
|
# Steep-friendly shape — see +lib/kobako/outcome/panic.rb+).
|
|
33
31
|
class Usage < Data.define(:wall_time, :memory_peak)
|
|
34
|
-
# Pre-invocation sentinel
|
|
35
|
-
#
|
|
36
|
-
# need to handle a +nil+ +#usage+.
|
|
32
|
+
# Pre-invocation sentinel. Reused by +Sandbox+ before any invocation
|
|
33
|
+
# has run so callers do not need to handle a +nil+ +#usage+.
|
|
37
34
|
EMPTY = new(wall_time: 0.0, memory_peak: 0)
|
|
38
35
|
end
|
|
39
36
|
end
|
data/lib/kobako/version.rb
CHANGED
data/lib/kobako.rb
CHANGED
data/release-please-config.json
CHANGED
|
@@ -73,13 +73,28 @@
|
|
|
73
73
|
"path": "/wasm/kobako-regexp/README.md"
|
|
74
74
|
}
|
|
75
75
|
]
|
|
76
|
+
},
|
|
77
|
+
"wasm/kobako-baker": {
|
|
78
|
+
"component": "kobako-baker",
|
|
79
|
+
"release-type": "rust",
|
|
80
|
+
"extra-files": [
|
|
81
|
+
{
|
|
82
|
+
"type": "toml",
|
|
83
|
+
"path": "/wasm/kobako-baker/Cargo.lock",
|
|
84
|
+
"jsonpath": "$.package[?(@.name=='kobako-baker')].version"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "generic",
|
|
88
|
+
"path": "/wasm/kobako-baker/README.md"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
76
91
|
}
|
|
77
92
|
},
|
|
78
93
|
"plugins": [
|
|
79
94
|
{
|
|
80
95
|
"type": "linked-versions",
|
|
81
96
|
"groupName": "kobako guest crates",
|
|
82
|
-
"components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-regexp"]
|
|
97
|
+
"components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-regexp", "kobako-baker"]
|
|
83
98
|
}
|
|
84
99
|
],
|
|
85
100
|
"extra-files": [
|
data/sig/kobako/errors.rbs
CHANGED
data/sig/kobako/namespace.rbs
CHANGED
data/sig/kobako/pool.rbs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Kobako
|
|
2
|
+
class Pool
|
|
3
|
+
DEFAULT_CHECKOUT_TIMEOUT_SECONDS: Float
|
|
4
|
+
|
|
5
|
+
@slots: Integer
|
|
6
|
+
@checkout_timeout: Float?
|
|
7
|
+
@sandbox_options: Hash[Symbol, untyped]
|
|
8
|
+
@setup: ^(Kobako::Sandbox) -> void | nil
|
|
9
|
+
@idle: Array[Kobako::Sandbox]
|
|
10
|
+
@constructed: Integer
|
|
11
|
+
@mutex: Thread::Mutex
|
|
12
|
+
@slot_freed: Thread::ConditionVariable
|
|
13
|
+
|
|
14
|
+
def initialize: (
|
|
15
|
+
slots: Integer,
|
|
16
|
+
?checkout_timeout: (Float | Integer)?,
|
|
17
|
+
**untyped sandbox_options
|
|
18
|
+
) ?{ (Kobako::Sandbox) -> void } -> void
|
|
19
|
+
|
|
20
|
+
def with: [T] () { (Kobako::Sandbox) -> T } -> T
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def checkout: () -> Kobako::Sandbox
|
|
25
|
+
|
|
26
|
+
def acquire: () -> Kobako::Sandbox
|
|
27
|
+
|
|
28
|
+
def claim_or_wait: (Float? deadline) -> [Symbol, Kobako::Sandbox?]
|
|
29
|
+
|
|
30
|
+
def await_slot!: (Float? deadline) -> void
|
|
31
|
+
|
|
32
|
+
def construct_slot: () -> Kobako::Sandbox
|
|
33
|
+
|
|
34
|
+
def checkin: (Kobako::Sandbox sandbox) -> void
|
|
35
|
+
|
|
36
|
+
def release_capacity!: () -> void
|
|
37
|
+
|
|
38
|
+
def monotonic_now: () -> Float
|
|
39
|
+
|
|
40
|
+
def validate_slots!: (untyped slots) -> void
|
|
41
|
+
|
|
42
|
+
def normalize_checkout_timeout: ((Float | Integer)? checkout_timeout) -> Float?
|
|
43
|
+
end
|
|
44
|
+
end
|
data/sig/kobako/sandbox.rbs
CHANGED
|
@@ -38,12 +38,12 @@ module Kobako
|
|
|
38
38
|
|
|
39
39
|
def eval: (String code) -> untyped
|
|
40
40
|
|
|
41
|
+
def reset_invocation_state!: () -> void
|
|
42
|
+
|
|
41
43
|
private
|
|
42
44
|
|
|
43
45
|
def install_dispatch_proc!: () -> void
|
|
44
46
|
|
|
45
|
-
def reset_invocation_state!: () -> void
|
|
46
|
-
|
|
47
47
|
def begin_invocation!: () -> void
|
|
48
48
|
|
|
49
49
|
def read_usage!: () -> void
|
|
@@ -22,6 +22,8 @@ module Kobako
|
|
|
22
22
|
|
|
23
23
|
def self?.reject_meta_method!: (untyped target, Symbol name) -> void
|
|
24
24
|
|
|
25
|
+
def self?.reject_unexposed!: (untyped target, Symbol name) -> void
|
|
26
|
+
|
|
25
27
|
def self?.resolve_arg: (untyped value, Kobako::Catalog::Handles handler) -> untyped
|
|
26
28
|
|
|
27
29
|
def self?.resolve_target: (String | Kobako::Handle target, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler) -> untyped
|
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.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aotokitsuruya
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- ext/kobako/src/runtime/dispatch.rs
|
|
66
66
|
- ext/kobako/src/runtime/exports.rs
|
|
67
67
|
- ext/kobako/src/runtime/guest_mem.rs
|
|
68
|
+
- ext/kobako/src/runtime/instance_pre.rs
|
|
68
69
|
- ext/kobako/src/runtime/invocation.rs
|
|
69
70
|
- ext/kobako/src/runtime/trap.rs
|
|
70
71
|
- ext/kobako/src/snapshot.rs
|
|
@@ -86,6 +87,7 @@ files:
|
|
|
86
87
|
- lib/kobako/namespace.rb
|
|
87
88
|
- lib/kobako/outcome.rb
|
|
88
89
|
- lib/kobako/outcome/panic.rb
|
|
90
|
+
- lib/kobako/pool.rb
|
|
89
91
|
- lib/kobako/runtime.rb
|
|
90
92
|
- lib/kobako/sandbox.rb
|
|
91
93
|
- lib/kobako/sandbox_options.rb
|
|
@@ -123,6 +125,7 @@ files:
|
|
|
123
125
|
- sig/kobako/namespace.rbs
|
|
124
126
|
- sig/kobako/outcome.rbs
|
|
125
127
|
- sig/kobako/outcome/panic.rbs
|
|
128
|
+
- sig/kobako/pool.rbs
|
|
126
129
|
- sig/kobako/runtime.rbs
|
|
127
130
|
- sig/kobako/sandbox.rbs
|
|
128
131
|
- sig/kobako/sandbox_options.rbs
|