kobako 0.8.0 → 0.9.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 +39 -0
- data/Cargo.lock +1 -1
- data/README.md +3 -1
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +2 -4
- data/ext/kobako/src/runtime/dispatch.rs +3 -3
- data/ext/kobako/src/runtime/guest_mem.rs +1 -1
- data/ext/kobako/src/runtime/invocation.rs +3 -3
- data/ext/kobako/src/runtime.rs +32 -32
- data/lib/kobako/catalog/namespaces.rb +3 -2
- data/lib/kobako/codec/factory.rb +2 -5
- data/lib/kobako/codec/utils.rb +11 -15
- data/lib/kobako/errors.rb +3 -3
- data/lib/kobako/fault.rb +2 -4
- data/lib/kobako/sandbox.rb +3 -3
- data/lib/kobako/transport/dispatcher.rb +1 -1
- data/lib/kobako/usage.rb +2 -4
- data/lib/kobako/version.rb +1 -1
- data/release-please-config.json +63 -1
- data/rust-toolchain.toml +2 -1
- data/sig/kobako/codec.rbs +4 -0
- data/sig/kobako/version.rbs +3 -0
- data/sig/kobako.rbs +0 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77815cccc84108dd3ece9b1571ecaa2b089867bf7b9ac395e561c940300c78fb
|
|
4
|
+
data.tar.gz: 49199331280ce4f7324039be281dd4cdfcedfaa03ece5141e56b70c24895f6de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b812aea26a6c6196fcd5dbb54b38d20d38e23577b0374fb89c620c725281dc569c3ef9dd44e2383842afc37dd2db0614601b0ba8c08d423338c38f1c511966f0
|
|
7
|
+
data.tar.gz: a55933ff5d6e425b6f98be8d2d57adcf733097a74a3524fd086005f463ba4372e0a3ced4cf2ce783e4049b3e2f7a7364baa090d76d70781cba8857334e230963
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{".":"0.
|
|
1
|
+
{".":"0.9.0","wasm/kobako-core":"0.4.0","wasm/kobako":"0.4.0","wasm/kobako-io":"0.4.0","wasm/kobako-regexp":"0.4.0"}
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.0](https://github.com/elct9620/kobako/compare/v0.8.0...v0.9.0) (2026-06-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **regexp:** add Kernel#=~ fallback returning nil ([d461781](https://github.com/elct9620/kobako/commit/d4617815b8e888a09a548c7f4664819cdddc34c8))
|
|
9
|
+
* **regexp:** add regexp-aware String#[]= ([34807f5](https://github.com/elct9620/kobako/commit/34807f5ba5f6e17efff27af3c5c24ae42b0b651d))
|
|
10
|
+
* **regexp:** add Regexp.last_match and last_match= ([03649e8](https://github.com/elct9620/kobako/commit/03649e81cfda0c43ac22777f70ea38a0ac4a93c7))
|
|
11
|
+
* **regexp:** add Regexp#named_captures and #names ([7cf018d](https://github.com/elct9620/kobako/commit/7cf018d39529be1d1384297b1b38b9d1670523e7))
|
|
12
|
+
* **regexp:** add String#slice! ([2857e0e](https://github.com/elct9620/kobako/commit/2857e0ee55df9f6295a25391ff76613b8dd5d555))
|
|
13
|
+
* **regexp:** align Regexp#match position handling with MRI ([c448c94](https://github.com/elct9620/kobako/commit/c448c9402a5a78076b5a81be0e07b7e1c90b1014))
|
|
14
|
+
* **regexp:** align Regexp#to_s flag rendering with MRI ([67d0414](https://github.com/elct9620/kobako/commit/67d04145f116a72a0f84d0ddf6674559e97046e8))
|
|
15
|
+
* **regexp:** copy the compiled pattern on Regexp dup/clone ([be97ea1](https://github.com/elct9620/kobako/commit/be97ea1cbd9556196f06229cd17d0288c062f133))
|
|
16
|
+
* **regexp:** copy the match snapshot on MatchData dup/clone ([0719d99](https://github.com/elct9620/kobako/commit/0719d99c41df9ddc8905580d61b32f0e6d88b6ba))
|
|
17
|
+
* **regexp:** define RegexpError in the gem instead of borrowing it ([ca57ca6](https://github.com/elct9620/kobako/commit/ca57ca6effb93ad05a175f2641f4b15aa971e31c))
|
|
18
|
+
* **regexp:** escape the source in Regexp#inspect ([9142d6c](https://github.com/elct9620/kobako/commit/9142d6cd32e08271639548af7801284e5d198892))
|
|
19
|
+
* **regexp:** expand backreferences and Hash in gsub/sub replacements ([8a0bc2d](https://github.com/elct9620/kobako/commit/8a0bc2dda8fb46a91fb1a5ee1c7482d6da9dffee))
|
|
20
|
+
* **regexp:** forbid MatchData.new ([5e2b3f5](https://github.com/elct9620/kobako/commit/5e2b3f527ff68dd118c370bb1b0bd01bf3dc4f8f))
|
|
21
|
+
* **regexp:** honour MatchData#named_captures(symbolize_names:) ([2a754d3](https://github.com/elct9620/kobako/commit/2a754d3d2ca3d4159471c8f9d17cc71ac59e0543))
|
|
22
|
+
* **regexp:** honour the position argument in String#index ([4dfbb41](https://github.com/elct9620/kobako/commit/4dfbb41086b302eada56efea4cbbfd6579adbdab))
|
|
23
|
+
* **regexp:** memoize compiled patterns per invocation ([f764d66](https://github.com/elct9620/kobako/commit/f764d66a574da51b9e714db1ae6d917cce4cf611))
|
|
24
|
+
* **regexp:** raise IndexError for out-of-range MatchData#begin/#end/#offset ([85fc8d6](https://github.com/elct9620/kobako/commit/85fc8d67d0fcc137a7f43695ead34317d557ec1a))
|
|
25
|
+
* **regexp:** reproduce the C match-family operand handling ([9e30d2d](https://github.com/elct9620/kobako/commit/9e30d2d6d6ddd42c84d5e2fb55cc2c06076c4fd4))
|
|
26
|
+
* **regexp:** set the $+ last-group match global ([b65a424](https://github.com/elct9620/kobako/commit/b65a42434b2edd5b01ed879b09895d17ff778888))
|
|
27
|
+
* **regexp:** support length and Range forms of MatchData#[] ([7ac4dee](https://github.com/elct9620/kobako/commit/7ac4deec79a215b35fdc2786522145ce6d34263c))
|
|
28
|
+
* **regexp:** yield the MatchData to a block in Regexp#match / String#match ([f5a6e53](https://github.com/elct9620/kobako/commit/f5a6e53ec5993237805c2360fa70684f84acf6b8))
|
|
29
|
+
* **wasm:** split the Guest Binary into a pure default and regexp variants ([21695b1](https://github.com/elct9620/kobako/commit/21695b1cd527e334c68f9730f01f92fef057e52f))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* **regexp:** align String#=~ with MRI semantics ([c8f3e70](https://github.com/elct9620/kobako/commit/c8f3e70c9f0797c614aab1639934d280fe20b90a))
|
|
35
|
+
* **regexp:** bound backtracking, clamp match positions, harden engine errors ([0177f71](https://github.com/elct9620/kobako/commit/0177f71cccf61c140912aab3c2e639286fd768d0))
|
|
36
|
+
* **regexp:** correct String#split group and zero-width handling ([c0150bc](https://github.com/elct9620/kobako/commit/c0150bc04c5dccdd430b173219c42c8f607112d1))
|
|
37
|
+
* **regexp:** honour capturing groups and the limit arg in String#split ([66e7398](https://github.com/elct9620/kobako/commit/66e73984f8318b582cc7aa0db48deacfb10e8671))
|
|
38
|
+
* **regexp:** make Regexp.last_match= refresh the numbered globals ([e47b257](https://github.com/elct9620/kobako/commit/e47b257715b73263ae5d1f9bae67442197330ee0))
|
|
39
|
+
* **regexp:** name the pattern in match-time errors and snap String#index pos ([5835f42](https://github.com/elct9620/kobako/commit/5835f42ae6b27e2a2a0d0bc5bf8023024a20642a))
|
|
40
|
+
* **regexp:** stop escaping the slash in Regexp.escape ([6c6f17a](https://github.com/elct9620/kobako/commit/6c6f17a63dbddf30ccdba19ddf3c9b7fbb7772cd))
|
|
41
|
+
|
|
3
42
|
## [0.8.0](https://github.com/elct9620/kobako/compare/v0.7.0...v0.8.0) (2026-06-05)
|
|
4
43
|
|
|
5
44
|
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -322,6 +322,8 @@ Order-of-magnitude figures on macOS arm64, Ruby 3.4.7, YJIT off. Absolute values
|
|
|
322
322
|
|
|
323
323
|
Construct one Sandbox at boot so the ~600 ms JIT cost lands off the request hot path. `ext/` does not release the GVL during wasmtime execution, so wasm work is GVL-serialized: aggregate throughput stays around 7-8k `#eval`/s regardless of Thread count, though Ruby-side `#eval` setup still overlaps. A +10% regression on any of the six SPEC-mandated benchmarks blocks release.
|
|
324
324
|
|
|
325
|
+
Regexp is an opt-in capability gem, excluded from the default binary and the gated set; its throughput is tracked in a separate non-gated characterization (`#10` in [`benchmark/README.md`](benchmark/README.md)). There `=~` (~5 µs/match) costs about 4× `match?` (~1.2 µs), because `=~` eagerly builds the `MatchData` and match globals — prefer `match?` for boolean tests.
|
|
326
|
+
|
|
325
327
|
```bash
|
|
326
328
|
bundle exec rake bench # six gated regression benchmarks (~5-8 min)
|
|
327
329
|
```
|
|
@@ -335,7 +337,7 @@ bin/setup # install dependencies
|
|
|
335
337
|
bundle exec rake # default: compile + test + rubocop + steep
|
|
336
338
|
```
|
|
337
339
|
|
|
338
|
-
Building from source requires a WASI-capable Rust toolchain in addition to the standard host toolchain; the first compile walks the full
|
|
340
|
+
Building from source requires a WASI-capable Rust toolchain in addition to the standard host toolchain; the first compile walks the full chain — the [beni](https://github.com/elct9620/beni) gem vendors wasi-sdk + mruby and builds `libmruby.a` (`rake beni:build`), then `rake wasm:build` produces the Guest Binary. See [`CLAUDE.md`](CLAUDE.md) for the rake task map and pipeline layout. `bin/console` opens an IRB session with the gem preloaded; `bundle exec rake install` installs the local checkout as a gem.
|
|
339
341
|
|
|
340
342
|
## Contributing
|
|
341
343
|
|
data/data/kobako.wasm
CHANGED
|
Binary file
|
data/ext/kobako/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "kobako"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Aotokitsuruya <contact@aotoki.me>"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -14,9 +14,7 @@ magnus = { version = "0.8.2" }
|
|
|
14
14
|
# wasmtime — host-side embedder for kobako.wasm. We disable default-features
|
|
15
15
|
# and opt back in only what kobako needs: a Cranelift-backed runtime that can
|
|
16
16
|
# compile a pre-built wasm32-wasip1 module on the host triple, plus the `wat`
|
|
17
|
-
# feature
|
|
18
|
-
# so test fixtures can be expressed as text. WASI integration is layered on
|
|
19
|
-
# later via `wasmtime-wasi` once stdin/stdout wiring is needed (item #16).
|
|
17
|
+
# feature so test fixtures can be expressed as text.
|
|
20
18
|
# `cache` / `parallel-compilation` / `pooling` / `component-model` / `async`
|
|
21
19
|
# are intentionally off — kobako runs short-lived synchronous sandboxes.
|
|
22
20
|
wasmtime = { version = "45.0.0", default-features = false, features = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Host-side dispatch for the `__kobako_dispatch` import.
|
|
2
2
|
//!
|
|
3
3
|
//! When the guest invokes the wasm import declared in
|
|
4
|
-
//! `wasm/kobako-
|
|
4
|
+
//! `wasm/kobako-core/src/abi.rs`, wasmtime calls back into the host
|
|
5
5
|
//! through the closure built in `super::Runtime::build`.
|
|
6
6
|
//! That closure delegates here. The dispatcher (docs/behavior.md B-12 / B-13):
|
|
7
7
|
//!
|
|
@@ -180,9 +180,9 @@ fn try_handle(
|
|
|
180
180
|
write_response(caller, &resp_bytes)
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
/// Invoke the Ruby-side dispatch
|
|
183
|
+
/// Invoke the Ruby-side dispatch `Proc` with the request bytes and return
|
|
184
184
|
/// the encoded Response bytes. The Proc is contracted to fold every
|
|
185
|
-
/// dispatch failure into a
|
|
185
|
+
/// dispatch failure into a `Response.err` envelope (see
|
|
186
186
|
/// `Kobako::Transport::Dispatcher.dispatch`), so reaching the error
|
|
187
187
|
/// branch is itself a wire-layer fault rather than a normal control path.
|
|
188
188
|
fn invoke_on_dispatch(
|
|
@@ -127,7 +127,7 @@ pub(super) fn guest_buffer_range(
|
|
|
127
127
|
|
|
128
128
|
/// Unpack the `(ptr, len)` u64 returned by `__kobako_take_outcome`:
|
|
129
129
|
/// high 32 bits = ptr, low 32 bits = len. Mirrors the guest-side
|
|
130
|
-
/// `
|
|
130
|
+
/// `unpack_u64` in `wasm/kobako-core/src/abi.rs`.
|
|
131
131
|
pub(super) fn unpack_outcome_packed(packed: u64) -> (usize, usize) {
|
|
132
132
|
let ptr = (packed >> 32) as u32 as usize;
|
|
133
133
|
let len = packed as u32 as usize;
|
|
@@ -82,7 +82,7 @@ impl Invocation {
|
|
|
82
82
|
self.stderr_pipe = Some(stderr);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
/// Register the Ruby-side dispatch
|
|
85
|
+
/// Register the Ruby-side dispatch `Proc`. From this point on, every
|
|
86
86
|
/// `__kobako_dispatch` host import invocation calls the Proc with the
|
|
87
87
|
/// request bytes and expects encoded Response bytes back.
|
|
88
88
|
pub(super) fn bind_on_dispatch(&mut self, proc_value: Opaque<Value>) {
|
|
@@ -325,9 +325,9 @@ pub(crate) struct MemoryLimitTrap {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
impl MemoryLimitTrap {
|
|
328
|
-
/// Construct a trap with the given
|
|
328
|
+
/// Construct a trap with the given `desired` / `limit` pair. Used
|
|
329
329
|
/// internally by `MemoryLimiter::memory_growing` in production and
|
|
330
|
-
/// by the sibling-module
|
|
330
|
+
/// by the sibling-module `classify_trap` unit tests to materialise
|
|
331
331
|
/// a representative error for downcast routing.
|
|
332
332
|
#[cfg(test)]
|
|
333
333
|
pub(super) fn new(desired: usize, limit: usize) -> Self {
|
data/ext/kobako/src/runtime.rs
CHANGED
|
@@ -71,9 +71,9 @@ use self::invocation::{Invocation, StoreCell};
|
|
|
71
71
|
/// the guest-side mirror is `kobako_core::abi::ABI_VERSION`.
|
|
72
72
|
const ABI_VERSION: u32 = 1;
|
|
73
73
|
|
|
74
|
-
/// Copy the bytes of
|
|
75
|
-
/// what would otherwise be an inline
|
|
76
|
-
/// .to_vec()
|
|
74
|
+
/// Copy the bytes of `s` into a fresh `Vec<u8>`. Single safe entry to
|
|
75
|
+
/// what would otherwise be an inline `unsafe { rstring.as_slice() }
|
|
76
|
+
/// .to_vec()` duplicated at every host-↔-guest boundary. The borrow
|
|
77
77
|
/// does not outlive this call, so no Ruby allocation can move the
|
|
78
78
|
/// underlying RString between the borrow and the copy — the safety
|
|
79
79
|
/// invariant the inline form relied on is established once here.
|
|
@@ -92,12 +92,12 @@ pub(crate) fn rstring_to_vec(s: RString) -> Vec<u8> {
|
|
|
92
92
|
// verb prefix and lets the subclass identity flow through unchanged.
|
|
93
93
|
// ---------------------------------------------------------------------------
|
|
94
94
|
|
|
95
|
-
/// Resolve `Kobako::<name>` as an
|
|
95
|
+
/// Resolve `Kobako::<name>` as an `ExceptionClass` — the shared body of
|
|
96
96
|
/// every error-class `Lazy` below, which differ only in the constant
|
|
97
97
|
/// name. The constants are guaranteed present by the time any of these
|
|
98
98
|
/// lazies first resolve (`lib/kobako/errors.rb` loads the hierarchy before
|
|
99
99
|
/// the ext raises into it), so a missing constant is a build / wiring bug
|
|
100
|
-
/// and the
|
|
100
|
+
/// and the `unwrap` is the correct fail-fast.
|
|
101
101
|
fn kobako_error_class(ruby: &Ruby, name: &str) -> ExceptionClass {
|
|
102
102
|
let kobako: RModule = ruby.class_object().const_get("Kobako").unwrap();
|
|
103
103
|
kobako.const_get(name).unwrap()
|
|
@@ -121,8 +121,8 @@ pub(crate) static MEMORY_LIMIT_ERROR: Lazy<ExceptionClass> =
|
|
|
121
121
|
pub(crate) static SANDBOX_ERROR: Lazy<ExceptionClass> =
|
|
122
122
|
Lazy::new(|ruby| kobako_error_class(ruby, "SandboxError"));
|
|
123
123
|
|
|
124
|
-
/// Build a
|
|
125
|
-
/// the named
|
|
124
|
+
/// Build a `MagnusError` in `class` carrying `msg` — the shared body of
|
|
125
|
+
/// the named `*_err` constructors below, which differ only in which
|
|
126
126
|
/// error-class `Lazy` they target.
|
|
127
127
|
fn error_in(ruby: &Ruby, class: &Lazy<ExceptionClass>, msg: impl Into<String>) -> MagnusError {
|
|
128
128
|
MagnusError::new(ruby.get_inner(class), msg.into())
|
|
@@ -414,9 +414,9 @@ impl Runtime {
|
|
|
414
414
|
Ok(())
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
-
/// Register the Ruby-side dispatch
|
|
418
|
-
/// Bound to Ruby as
|
|
419
|
-
/// every
|
|
417
|
+
/// Register the Ruby-side dispatch `Proc` on the active Invocation.
|
|
418
|
+
/// Bound to Ruby as `Kobako::Runtime#on_dispatch=`. From this point on,
|
|
419
|
+
/// every `__kobako_dispatch` host import invocation calls the Proc
|
|
420
420
|
/// with the request bytes and writes the returned Response bytes back
|
|
421
421
|
/// into guest memory (docs/behavior.md B-12).
|
|
422
422
|
pub(crate) fn set_on_dispatch(&self, proc_value: Value) -> Result<(), MagnusError> {
|
|
@@ -440,14 +440,14 @@ impl Runtime {
|
|
|
440
440
|
/// export with `args_bytes` as the yield-arguments payload, and
|
|
441
441
|
/// return the YieldResponse bytes the guest produced (B-24).
|
|
442
442
|
///
|
|
443
|
-
/// Bound to Ruby as
|
|
443
|
+
/// Bound to Ruby as `Kobako::Runtime#yield_to_active_invocation`.
|
|
444
444
|
/// Recovers the dispatcher's `&mut Caller` from the per-thread
|
|
445
445
|
/// Invocation slot (SPEC.md Single-Invocation Slot) — the host is
|
|
446
446
|
/// already inside a `__kobako_dispatch` callback, so the Caller
|
|
447
447
|
/// parked on the Rust stack is the same one the Sandbox-level
|
|
448
448
|
/// `#eval` / `#run` is driving. Invoked from the host-side yield
|
|
449
449
|
/// proxy that the dispatcher hands to Service methods (B-23 / B-24);
|
|
450
|
-
/// raises
|
|
450
|
+
/// raises `Kobako::TrapError` when called outside an active dispatch
|
|
451
451
|
/// frame, or when any of the underlying allocation / write / call /
|
|
452
452
|
/// read steps fails.
|
|
453
453
|
pub(crate) fn yield_to_active_invocation(
|
|
@@ -482,8 +482,8 @@ impl Runtime {
|
|
|
482
482
|
/// and return a `Snapshot` bundling every per-invocation observable.
|
|
483
483
|
///
|
|
484
484
|
/// Rebuilds the WASI context with fresh stdin / stdout / stderr pipes
|
|
485
|
-
/// (the three-frame stdin protocol carries
|
|
486
|
-
///
|
|
485
|
+
/// (the three-frame stdin protocol carries `preamble`, `source`, then
|
|
486
|
+
/// `snippets` — docs/wire-codec.md § Invocation channels), then
|
|
487
487
|
/// invokes `__kobako_eval`. Per-invocation caps (docs/behavior.md
|
|
488
488
|
/// B-01) are primed here: the wall-clock deadline is stamped into
|
|
489
489
|
/// `Invocation` and the epoch deadline is set to fire at the next
|
|
@@ -517,10 +517,10 @@ impl Runtime {
|
|
|
517
517
|
///
|
|
518
518
|
/// Rebuilds the WASI context with the two-frame stdin protocol
|
|
519
519
|
/// (preamble + snippets; no user source frame — docs/wire-codec.md
|
|
520
|
-
/// § Invocation channels), copies
|
|
520
|
+
/// § Invocation channels), copies `envelope` bytes into guest linear
|
|
521
521
|
/// memory via `__kobako_alloc`, and calls `__kobako_run(env_ptr,
|
|
522
522
|
/// env_len)`. Per-invocation cap semantics match `Runtime::eval`.
|
|
523
|
-
/// Raises
|
|
523
|
+
/// Raises `Kobako::TrapError` ("alloc returned 0") when guest
|
|
524
524
|
/// allocation fails (docs/behavior.md E-31).
|
|
525
525
|
pub(crate) fn run(
|
|
526
526
|
&self,
|
|
@@ -646,7 +646,7 @@ impl Runtime {
|
|
|
646
646
|
/// The mruby image's declared initial allocation and the high-water
|
|
647
647
|
/// mark left by prior invocations on the same Sandbox are folded
|
|
648
648
|
/// into the baseline rather than the budget — only `memory.grow`
|
|
649
|
-
/// past
|
|
649
|
+
/// past `baseline` counts against `memory_limit`.
|
|
650
650
|
///
|
|
651
651
|
/// Also stamps the wall-clock entry instant for the
|
|
652
652
|
/// docs/behavior.md B-35 `wall_time` measurement. The bracket
|
|
@@ -686,11 +686,11 @@ impl Runtime {
|
|
|
686
686
|
store_ref.data_mut().disarm_memory_cap();
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
-
/// Allocate a
|
|
690
|
-
/// `__kobako_alloc`, copy
|
|
691
|
-
/// as
|
|
692
|
-
/// Raises
|
|
693
|
-
/// itself traps, and
|
|
689
|
+
/// Allocate a `len`-byte buffer in guest linear memory via
|
|
690
|
+
/// `__kobako_alloc`, copy `envelope` into it, and return `(ptr, len)`
|
|
691
|
+
/// as `i32` values matching the `__kobako_run(env_ptr, env_len)` ABI.
|
|
692
|
+
/// Raises `Kobako::TrapError` when the allocation hook is missing or
|
|
693
|
+
/// itself traps, and `Kobako::SandboxError` when the hook runs but
|
|
694
694
|
/// cannot reserve the buffer (`__kobako_alloc` returns 0,
|
|
695
695
|
/// docs/behavior.md E-31) — an intact runtime, not an engine fault.
|
|
696
696
|
fn write_envelope(&self, ruby: &Ruby, envelope: RString) -> Result<(i32, i32), MagnusError> {
|
|
@@ -726,10 +726,10 @@ impl Runtime {
|
|
|
726
726
|
}
|
|
727
727
|
|
|
728
728
|
/// Rebuild the WASI context with fresh stdin (carrying every frame in
|
|
729
|
-
///
|
|
729
|
+
/// `frames`, each prefixed by its 4-byte big-endian u32 length —
|
|
730
730
|
/// docs/wire-codec.md § Invocation channels) plus fresh stdout / stderr
|
|
731
|
-
/// pipes. Called at the top of every guest invocation:
|
|
732
|
-
/// three frames (preamble, source, snippets),
|
|
731
|
+
/// pipes. Called at the top of every guest invocation: `#eval` passes
|
|
732
|
+
/// three frames (preamble, source, snippets), `#run` passes two
|
|
733
733
|
/// (preamble, snippets — the invocation envelope arrives via linear
|
|
734
734
|
/// memory instead). Each output pipe is sized at `cap + 1` so
|
|
735
735
|
/// `capture::clip_capture` can distinguish "wrote exactly cap
|
|
@@ -762,10 +762,10 @@ impl Runtime {
|
|
|
762
762
|
.install_wasi(wasi, stdout_pipe, stderr_pipe);
|
|
763
763
|
}
|
|
764
764
|
|
|
765
|
-
/// Invoke `__kobako_take_outcome`, decode the packed
|
|
765
|
+
/// Invoke `__kobako_take_outcome`, decode the packed `(ptr<<32)|len`
|
|
766
766
|
/// u64, and copy the OUTCOME_BUFFER slice out of guest memory. Raises
|
|
767
|
-
/// `Kobako::TrapError` when the export is missing,
|
|
768
|
-
/// 16 MiB single-dispatch cap, the
|
|
767
|
+
/// `Kobako::TrapError` when the export is missing, `len` exceeds the
|
|
768
|
+
/// 16 MiB single-dispatch cap, the `ptr`/`len` arithmetic overflows,
|
|
769
769
|
/// the slice falls outside live memory, or the `memory` export itself
|
|
770
770
|
/// is absent.
|
|
771
771
|
fn fetch_outcome_bytes(&self, ruby: &Ruby) -> Result<Vec<u8>, MagnusError> {
|
|
@@ -812,10 +812,10 @@ const SANDBOX_RUNTIME_MISSING_HOOKS: &str = "Sandbox runtime is missing required
|
|
|
812
812
|
const SANDBOX_RUNTIME_NOT_KOBAKO: &str =
|
|
813
813
|
"the loaded Wasm module is not a Kobako-compatible runtime";
|
|
814
814
|
|
|
815
|
-
/// Return the cached
|
|
816
|
-
///
|
|
817
|
-
/// methods (
|
|
818
|
-
/// drains
|
|
815
|
+
/// Return the cached `TypedFunc` for an ABI export, or raise
|
|
816
|
+
/// `Kobako::TrapError` when the option is `None`. Both run-path
|
|
817
|
+
/// methods (`#eval`, `#run`) plus the `build_snapshot` readout that
|
|
818
|
+
/// drains `OUTCOME_BUFFER` share the same "missing export → Ruby
|
|
819
819
|
/// error" boilerplate; this helper collapses those sites onto one
|
|
820
820
|
/// safe entry. The user-facing message is intentionally export-
|
|
821
821
|
/// agnostic (see `SANDBOX_RUNTIME_MISSING_HOOKS`) — the ABI symbol
|
|
@@ -28,7 +28,7 @@ module Kobako
|
|
|
28
28
|
# installs ({docs/behavior.md B-12}[link:../../../docs/behavior.md]).
|
|
29
29
|
# The registry holds an injected +Catalog::Handles+ reference so
|
|
30
30
|
# dispatch target resolution and host→guest auto-wrap share the same
|
|
31
|
-
# Sandbox-owned allocator (docs/behavior.md B-19).
|
|
31
|
+
# Sandbox-owned allocator ({docs/behavior.md B-19}[link:../../../docs/behavior.md]).
|
|
32
32
|
class Namespaces
|
|
33
33
|
# Build a fresh registry. +handler+ is an internal seam that injects
|
|
34
34
|
# a pre-configured +Catalog::Handles+; tests pass one whose +next_id+
|
|
@@ -40,7 +40,8 @@ module Kobako
|
|
|
40
40
|
@sealed = false
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
# Declare or retrieve the Namespace named +name+ (idempotent —
|
|
43
|
+
# Declare or retrieve the Namespace named +name+ (idempotent —
|
|
44
|
+
# {docs/behavior.md B-10}[link:../../../docs/behavior.md]).
|
|
44
45
|
# +name+ is a constant-form name as a +Symbol+ or +String+ (must satisfy
|
|
45
46
|
# +Namespace::NAME_PATTERN+). Returns the +Kobako::Namespace+ for that
|
|
46
47
|
# name, creating it if it does not exist. Raises +ArgumentError+ when
|
data/lib/kobako/codec/factory.rb
CHANGED
|
@@ -77,9 +77,7 @@ module Kobako
|
|
|
77
77
|
)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
# Symbol-to-name packer
|
|
81
|
-
# resolve the proc shape without tripping on +lambda(&:name)+'s
|
|
82
|
-
# +Symbol#to_proc+ inference path.
|
|
80
|
+
# Symbol-to-name packer for the ext-0x00 registration.
|
|
83
81
|
def pack_symbol(symbol)
|
|
84
82
|
symbol.name
|
|
85
83
|
end
|
|
@@ -148,8 +146,7 @@ module Kobako
|
|
|
148
146
|
# method when a nested ext 0x02 appears inside +details+. The recursion
|
|
149
147
|
# is bounded by msgpack nesting depth — identical to nested Array /
|
|
150
148
|
# Hash payloads — so no extra guard is needed. Do not switch back to
|
|
151
|
-
# +factory.load+ to "simplify": that path bypasses UTF-8 validation
|
|
152
|
-
# and re-opens the Decoder's special case for Fault (removed in M5).
|
|
149
|
+
# +factory.load+ to "simplify": that path bypasses UTF-8 validation.
|
|
153
150
|
def unpack_fault(payload)
|
|
154
151
|
Decoder.decode(payload) do |map|
|
|
155
152
|
raise InvalidType, "Fault payload must be a map" unless map.is_a?(Hash)
|
data/lib/kobako/codec/utils.rb
CHANGED
|
@@ -43,14 +43,12 @@ module Kobako
|
|
|
43
43
|
# stays {Kobako::Codec::Error} and never leaks +ArgumentError+ from
|
|
44
44
|
# the Ruby standard library.
|
|
45
45
|
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# outside the codec boundary — host-layer +ArgumentError+ values
|
|
53
|
-
# should propagate unchanged.
|
|
46
|
+
# Reach for this only where a value object is constructed outside a
|
|
47
|
+
# {Decoder.decode} block, whose rescue already performs the same
|
|
48
|
+
# mapping (worked example: {Factory#unpack_handle} building
|
|
49
|
+
# +Handle.restore+ from a raw fixext payload). Do not use it for
|
|
50
|
+
# general-purpose validation outside the codec boundary —
|
|
51
|
+
# host-layer +ArgumentError+ values should propagate unchanged.
|
|
54
52
|
def with_boundary
|
|
55
53
|
yield
|
|
56
54
|
rescue ::ArgumentError => e
|
|
@@ -143,8 +141,7 @@ module Kobako
|
|
|
143
141
|
end
|
|
144
142
|
end
|
|
145
143
|
|
|
146
|
-
#
|
|
147
|
-
# budget — the closed-set non-container branch. Returns +true+ for
|
|
144
|
+
# The non-container branch of {representable?}: returns +true+ for
|
|
148
145
|
# the scalar leaves and an existing Handle. Not part of the
|
|
149
146
|
# public surface; reach for {representable?} instead.
|
|
150
147
|
def primitive_type?(value)
|
|
@@ -155,11 +152,10 @@ module Kobako
|
|
|
155
152
|
end
|
|
156
153
|
end
|
|
157
154
|
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
# instead.
|
|
155
|
+
# The container branch of {representable?}: recurses into Array
|
|
156
|
+
# elements and Hash key+value pairs through the public
|
|
157
|
+
# {representable?}. Not part of the public surface; reach for
|
|
158
|
+
# {representable?} instead.
|
|
163
159
|
def container_representable?(value)
|
|
164
160
|
case value
|
|
165
161
|
when ::Array then value.all? { |element| Utils.representable?(element) }
|
data/lib/kobako/errors.rb
CHANGED
|
@@ -26,9 +26,9 @@ module Kobako
|
|
|
26
26
|
# * {SetupError} — construction layer. Raised by `Kobako::Sandbox.new`
|
|
27
27
|
# when the wasm runtime cannot be built from the
|
|
28
28
|
# configured +wasm_path+ before any invocation runs
|
|
29
|
-
# (docs/behavior.md E-40 / E-41).
|
|
30
|
-
# outcome, so it never passes
|
|
31
|
-
# attribution decision.
|
|
29
|
+
# ({docs/behavior.md E-40 / E-41}[link:../../docs/behavior.md]).
|
|
30
|
+
# Not an invocation outcome, so it never passes
|
|
31
|
+
# through the two-step attribution decision.
|
|
32
32
|
#
|
|
33
33
|
# Subclasses pinned by docs/behavior.md Error Classes:
|
|
34
34
|
#
|
data/lib/kobako/fault.rb
CHANGED
|
@@ -22,10 +22,8 @@ module Kobako
|
|
|
22
22
|
# exception class (RuntimeError, ArgumentError, Kobako::ServiceError, ...)
|
|
23
23
|
# is the responsibility of the dispatch layer, not the codec.
|
|
24
24
|
#
|
|
25
|
-
# Built on the +class X < Data.define(...)+ subclass form
|
|
26
|
-
#
|
|
27
|
-
# this as the Steep-friendly shape and the +Style/DataInheritance+
|
|
28
|
-
# cop is disabled on that basis (see +.rubocop.yml+).
|
|
25
|
+
# Built on the +class X < Data.define(...)+ subclass form (the
|
|
26
|
+
# Steep-friendly shape — see +lib/kobako/outcome/panic.rb+).
|
|
29
27
|
class Fault < Data.define(:type, :message, :details)
|
|
30
28
|
VALID_TYPES = %w[runtime argument undefined].freeze
|
|
31
29
|
|
data/lib/kobako/sandbox.rb
CHANGED
|
@@ -292,9 +292,9 @@ module Kobako
|
|
|
292
292
|
#
|
|
293
293
|
# The yielded block must return a +Kobako::Snapshot+ — i.e. the
|
|
294
294
|
# value of +Runtime#eval+ / +#run+ (SPEC.md Internal Concepts →
|
|
295
|
-
# Snapshot). The success path unpacks
|
|
296
|
-
#
|
|
297
|
-
#
|
|
295
|
+
# Snapshot). The success path unpacks +#stdout+ / +#stderr+ into
|
|
296
|
+
# +Capture+ and feeds +#return_bytes+ to +Outcome.decode+; usage is
|
|
297
|
+
# populated by the +ensure+ readout ({#read_usage!}) on every outcome.
|
|
298
298
|
# The rescue chain is the single trap-translation boundary —
|
|
299
299
|
# configured-cap paths
|
|
300
300
|
# ({docs/behavior.md E-19 / E-20}[link:../../docs/behavior.md])
|
|
@@ -117,7 +117,7 @@ module Kobako
|
|
|
117
117
|
end
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
-
# {docs/behavior.md B-16}[link:../../../docs/behavior.md] —
|
|
120
|
+
# {docs/behavior.md B-16}[link:../../../docs/behavior.md] — A Kobako::Handle arriving as a positional or keyword
|
|
121
121
|
# argument identifies a host-side object previously allocated by a prior
|
|
122
122
|
# transport call's Handle wrap (B-14). Resolve it back to the Ruby object before
|
|
123
123
|
# the dispatch reaches +public_send+.
|
data/lib/kobako/usage.rb
CHANGED
|
@@ -28,10 +28,8 @@ module Kobako
|
|
|
28
28
|
# consumed. Before the first invocation +Sandbox#usage+ returns the
|
|
29
29
|
# pre-invocation sentinel +Kobako::Usage::EMPTY+.
|
|
30
30
|
#
|
|
31
|
-
# Built on the +class X < Data.define(...)+ subclass form
|
|
32
|
-
#
|
|
33
|
-
# as the Steep-friendly shape and the +Style/DataInheritance+ cop is
|
|
34
|
-
# disabled on that basis (see +.rubocop.yml+).
|
|
31
|
+
# Built on the +class X < Data.define(...)+ subclass form (the
|
|
32
|
+
# Steep-friendly shape — see +lib/kobako/outcome/panic.rb+).
|
|
35
33
|
class Usage < Data.define(:wall_time, :memory_peak)
|
|
36
34
|
# Pre-invocation sentinel ({docs/behavior.md B-35}[link:../../docs/behavior.md]).
|
|
37
35
|
# Reused by +Sandbox+ before any invocation has run so callers do not
|
data/lib/kobako/version.rb
CHANGED
data/release-please-config.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
".": {
|
|
7
7
|
"component": "kobako",
|
|
8
8
|
"include-component-in-tag": false,
|
|
9
|
-
"release-type": "ruby"
|
|
9
|
+
"release-type": "ruby",
|
|
10
|
+
"exclude-paths": ["wasm"]
|
|
10
11
|
},
|
|
11
12
|
"wasm/kobako-core": {
|
|
12
13
|
"component": "kobako-core",
|
|
@@ -16,10 +17,71 @@
|
|
|
16
17
|
"type": "toml",
|
|
17
18
|
"path": "/wasm/Cargo.lock",
|
|
18
19
|
"jsonpath": "$.package[?(@.name=='kobako-core')].version"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "generic",
|
|
23
|
+
"path": "/wasm/kobako-core/README.md"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"wasm/kobako": {
|
|
28
|
+
"component": "kobako-rs",
|
|
29
|
+
"release-type": "rust",
|
|
30
|
+
"extra-files": [
|
|
31
|
+
{
|
|
32
|
+
"type": "toml",
|
|
33
|
+
"path": "/wasm/Cargo.lock",
|
|
34
|
+
"jsonpath": "$.package[?(@.name=='kobako')].version"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "toml",
|
|
38
|
+
"path": "/wasm/kobako/Cargo.toml",
|
|
39
|
+
"jsonpath": "$.dependencies['kobako-core'].version"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "generic",
|
|
43
|
+
"path": "/wasm/kobako/README.md"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"wasm/kobako-io": {
|
|
48
|
+
"component": "kobako-io",
|
|
49
|
+
"release-type": "rust",
|
|
50
|
+
"extra-files": [
|
|
51
|
+
{
|
|
52
|
+
"type": "toml",
|
|
53
|
+
"path": "/wasm/Cargo.lock",
|
|
54
|
+
"jsonpath": "$.package[?(@.name=='kobako-io')].version"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "generic",
|
|
58
|
+
"path": "/wasm/kobako-io/README.md"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"wasm/kobako-regexp": {
|
|
63
|
+
"component": "kobako-regexp",
|
|
64
|
+
"release-type": "rust",
|
|
65
|
+
"extra-files": [
|
|
66
|
+
{
|
|
67
|
+
"type": "toml",
|
|
68
|
+
"path": "/wasm/Cargo.lock",
|
|
69
|
+
"jsonpath": "$.package[?(@.name=='kobako-regexp')].version"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "generic",
|
|
73
|
+
"path": "/wasm/kobako-regexp/README.md"
|
|
19
74
|
}
|
|
20
75
|
]
|
|
21
76
|
}
|
|
22
77
|
},
|
|
78
|
+
"plugins": [
|
|
79
|
+
{
|
|
80
|
+
"type": "linked-versions",
|
|
81
|
+
"groupName": "kobako guest crates",
|
|
82
|
+
"components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-regexp"]
|
|
83
|
+
}
|
|
84
|
+
],
|
|
23
85
|
"extra-files": [
|
|
24
86
|
{
|
|
25
87
|
"type": "toml",
|
data/rust-toolchain.toml
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Pin the Rust toolchain so local builds and CI stay byte-identical.
|
|
2
2
|
# The wasm32-wasip1 crt1-command.o references __wasi_init_tp from 1.96 onward;
|
|
3
3
|
# vendored wasi-sdk 33's libc.a supplies that symbol, so the two move together.
|
|
4
|
-
# Bump this in lockstep with
|
|
4
|
+
# Bump this in lockstep with the wasi-sdk toolchain beni vendors
|
|
5
|
+
# (`rake beni:build`, wired in the Rakefile's Beni::Tasks block).
|
|
5
6
|
# This file is the single source of the channel; the CI workflows read it.
|
|
6
7
|
[toolchain]
|
|
7
8
|
channel = "1.96.0"
|
data/sig/kobako.rbs
CHANGED
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.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aotokitsuruya
|
|
@@ -110,6 +110,7 @@ files:
|
|
|
110
110
|
- sig/kobako/catalog/handles.rbs
|
|
111
111
|
- sig/kobako/catalog/namespaces.rbs
|
|
112
112
|
- sig/kobako/catalog/snippets.rbs
|
|
113
|
+
- sig/kobako/codec.rbs
|
|
113
114
|
- sig/kobako/codec/decoder.rbs
|
|
114
115
|
- sig/kobako/codec/encoder.rbs
|
|
115
116
|
- sig/kobako/codec/error.rbs
|
|
@@ -137,6 +138,7 @@ files:
|
|
|
137
138
|
- sig/kobako/transport/yield.rbs
|
|
138
139
|
- sig/kobako/transport/yielder.rbs
|
|
139
140
|
- sig/kobako/usage.rbs
|
|
141
|
+
- sig/kobako/version.rbs
|
|
140
142
|
homepage: https://github.com/elct9620/kobako
|
|
141
143
|
licenses:
|
|
142
144
|
- Apache-2.0
|