kobako 0.3.0 → 0.5.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 -0
- data/CHANGELOG.md +29 -0
- data/Cargo.lock +1 -1
- data/README.md +85 -6
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/lib.rs +4 -2
- data/ext/kobako/src/{wasm → runtime}/cache.rs +22 -18
- data/ext/kobako/src/runtime/capture.rs +91 -0
- data/ext/kobako/src/runtime/config.rs +26 -0
- data/ext/kobako/src/runtime/dispatch.rs +211 -0
- data/ext/kobako/src/runtime/exports.rs +51 -0
- data/ext/kobako/src/runtime/guest_mem.rs +228 -0
- data/ext/kobako/src/{wasm/host_state.rs → runtime/invocation.rs} +195 -81
- data/ext/kobako/src/runtime/trap.rs +134 -0
- data/ext/kobako/src/runtime.rs +782 -0
- data/ext/kobako/src/snapshot.rs +110 -0
- data/lib/kobako/capture.rb +11 -16
- data/lib/kobako/catalog/handles.rb +107 -0
- data/lib/kobako/catalog/namespaces.rb +99 -0
- data/lib/kobako/{snippet/table.rb → catalog/snippets.rb} +37 -62
- data/lib/kobako/catalog.rb +18 -0
- data/lib/kobako/codec/decoder.rb +13 -7
- data/lib/kobako/codec/factory.rb +21 -18
- data/lib/kobako/codec/utils.rb +118 -29
- data/lib/kobako/codec.rb +6 -3
- data/lib/kobako/errors.rb +45 -28
- data/lib/kobako/fault.rb +40 -0
- data/lib/kobako/handle.rb +60 -0
- data/lib/kobako/namespace.rb +67 -0
- data/lib/kobako/outcome.rb +55 -29
- data/lib/kobako/runtime.rb +30 -0
- data/lib/kobako/sandbox.rb +131 -67
- data/lib/kobako/sandbox_options.rb +6 -9
- data/lib/kobako/snapshot.rb +40 -0
- data/lib/kobako/snippet/binary.rb +6 -7
- data/lib/kobako/snippet/source.rb +8 -8
- data/lib/kobako/snippet.rb +7 -9
- data/lib/kobako/transport/dispatcher.rb +195 -0
- data/lib/kobako/transport/error.rb +24 -0
- data/lib/kobako/transport/request.rb +78 -0
- data/lib/kobako/transport/response.rb +69 -0
- data/lib/kobako/transport/run.rb +141 -0
- data/lib/kobako/transport/yield.rb +91 -0
- data/lib/kobako/transport/yielder.rb +89 -0
- data/lib/kobako/transport.rb +24 -0
- data/lib/kobako/usage.rb +41 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +4 -3
- data/release-please-config.json +24 -0
- data/sig/kobako/capture.rbs +0 -2
- data/sig/kobako/{rpc/handle_table.rbs → catalog/handles.rbs} +3 -9
- data/sig/kobako/catalog/namespaces.rbs +17 -0
- data/sig/kobako/{snippet/table.rbs → catalog/snippets.rbs} +2 -11
- data/sig/kobako/{rpc.rbs → catalog.rbs} +1 -1
- data/sig/kobako/codec/decoder.rbs +2 -1
- data/sig/kobako/codec/factory.rbs +3 -3
- data/sig/kobako/codec/utils.rbs +11 -1
- data/sig/kobako/errors.rbs +7 -7
- data/sig/kobako/fault.rbs +19 -0
- data/sig/kobako/handle.rbs +18 -0
- data/sig/kobako/namespace.rbs +19 -0
- data/sig/kobako/outcome.rbs +2 -2
- data/sig/kobako/runtime.rbs +23 -0
- data/sig/kobako/sandbox.rbs +10 -7
- data/sig/kobako/snapshot.rbs +15 -0
- data/sig/kobako/transport/dispatcher.rbs +34 -0
- data/sig/kobako/transport/error.rbs +6 -0
- data/sig/kobako/transport/request.rbs +32 -0
- data/sig/kobako/transport/response.rbs +30 -0
- data/sig/kobako/transport/run.rbs +27 -0
- data/sig/kobako/transport/yield.rbs +34 -0
- data/sig/kobako/transport/yielder.rbs +21 -0
- data/sig/kobako/transport.rbs +4 -0
- data/sig/kobako/usage.rbs +11 -0
- metadata +52 -30
- data/ext/kobako/src/wasm/dispatch.rs +0 -161
- data/ext/kobako/src/wasm/instance.rs +0 -771
- data/ext/kobako/src/wasm.rs +0 -125
- data/lib/kobako/invocation.rb +0 -112
- data/lib/kobako/rpc/dispatcher.rb +0 -169
- data/lib/kobako/rpc/envelope.rb +0 -118
- data/lib/kobako/rpc/fault.rb +0 -41
- data/lib/kobako/rpc/handle.rb +0 -39
- data/lib/kobako/rpc/handle_table.rb +0 -107
- data/lib/kobako/rpc/namespace.rb +0 -74
- data/lib/kobako/rpc/server.rb +0 -158
- data/lib/kobako/rpc.rb +0 -11
- data/lib/kobako/wasm.rb +0 -25
- data/sig/kobako/invocation.rbs +0 -23
- data/sig/kobako/rpc/dispatcher.rbs +0 -33
- data/sig/kobako/rpc/envelope.rbs +0 -51
- data/sig/kobako/rpc/fault.rbs +0 -20
- data/sig/kobako/rpc/handle.rbs +0 -19
- data/sig/kobako/rpc/namespace.rbs +0 -24
- data/sig/kobako/rpc/server.rbs +0 -37
- data/sig/kobako/wasm.rbs +0 -39
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//! Trap classification for the run path.
|
|
2
|
+
//!
|
|
3
|
+
//! Maps a `wasmtime` run error to the right top-level `Kobako::*` Ruby
|
|
4
|
+
//! exception (`TimeoutError` / `MemoryLimitError` / `TrapError`), and
|
|
5
|
+
//! hosts the epoch-deadline callback that raises the wall-clock
|
|
6
|
+
//! `TimeoutTrap`. The classification is a pure function over the error's
|
|
7
|
+
//! downcast chain so it can be exercised from `cargo test` without the
|
|
8
|
+
//! magnus surface; the trap marker types themselves live in
|
|
9
|
+
//! `super::invocation` (where the limiter / callback construct them).
|
|
10
|
+
|
|
11
|
+
use std::time::Instant;
|
|
12
|
+
|
|
13
|
+
use magnus::{Error as MagnusError, Ruby};
|
|
14
|
+
use wasmtime::{StoreContextMut, UpdateDeadline};
|
|
15
|
+
|
|
16
|
+
use super::invocation::{Invocation, MemoryLimitTrap, TimeoutTrap};
|
|
17
|
+
use super::{memory_limit_err, setup_err, timeout_err, trap_err};
|
|
18
|
+
|
|
19
|
+
/// Epoch-deadline callback installed on every Store. Read the per-run
|
|
20
|
+
/// wall-clock deadline from `Invocation` (docs/behavior.md B-01) and trap with
|
|
21
|
+
/// `TimeoutTrap` once the deadline has passed; otherwise extend the
|
|
22
|
+
/// next check by one tick of the process-wide epoch ticker. When the
|
|
23
|
+
/// deadline is `None` the callback should not fire under normal
|
|
24
|
+
/// `Runtime::eval` / `Runtime::run` flow because
|
|
25
|
+
/// `set_epoch_deadline(u64::MAX)` is used; returning a long extension
|
|
26
|
+
/// keeps the callback inert as a defence in depth.
|
|
27
|
+
pub(super) fn epoch_deadline_callback(
|
|
28
|
+
ctx: StoreContextMut<'_, Invocation>,
|
|
29
|
+
) -> wasmtime::Result<UpdateDeadline> {
|
|
30
|
+
match ctx.data().deadline() {
|
|
31
|
+
Some(deadline) if Instant::now() >= deadline => Err(wasmtime::Error::new(TimeoutTrap)),
|
|
32
|
+
Some(_) => Ok(UpdateDeadline::Continue(1)),
|
|
33
|
+
None => Ok(UpdateDeadline::Continue(u64::MAX / 2)),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Configured-cap path classification for a wasmtime error. The
|
|
38
|
+
/// downcast logic stays in a pure helper so the
|
|
39
|
+
/// `Kobako::TimeoutError` / `MemoryLimitError` /
|
|
40
|
+
/// `Kobako::TrapError` mapping can be exercised from `cargo test`
|
|
41
|
+
/// without the magnus surface.
|
|
42
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
43
|
+
enum TrapClass {
|
|
44
|
+
/// docs/behavior.md E-19 wall-clock cap path.
|
|
45
|
+
Timeout,
|
|
46
|
+
/// docs/behavior.md E-20 linear-memory cap path.
|
|
47
|
+
MemoryLimit,
|
|
48
|
+
/// Any other wasmtime error — surfaces as the base
|
|
49
|
+
/// `Kobako::TrapError`.
|
|
50
|
+
Other,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Inspect a wasmtime error to decide which top-level `Kobako::*` trap
|
|
54
|
+
/// class it should map to. Pure function — operates on the error's
|
|
55
|
+
/// downcast chain only, no magnus / Ruby state required.
|
|
56
|
+
fn classify_trap(err: &wasmtime::Error) -> TrapClass {
|
|
57
|
+
if err.downcast_ref::<TimeoutTrap>().is_some() {
|
|
58
|
+
TrapClass::Timeout
|
|
59
|
+
} else if err.downcast_ref::<MemoryLimitTrap>().is_some() {
|
|
60
|
+
TrapClass::MemoryLimit
|
|
61
|
+
} else {
|
|
62
|
+
TrapClass::Other
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Map a wasmtime call error to the right top-level `Kobako::*` Ruby
|
|
67
|
+
/// exception class. The ABI export symbol (`__kobako_eval` /
|
|
68
|
+
/// `__kobako_run`) is deliberately omitted from the message — the
|
|
69
|
+
/// Sandbox layer attaches the user-facing verb (`Sandbox#eval` /
|
|
70
|
+
/// `Sandbox#run`) so the message reads in caller vocabulary rather
|
|
71
|
+
/// than ABI vocabulary.
|
|
72
|
+
///
|
|
73
|
+
/// For the configured-cap paths (`TrapClass::Timeout` /
|
|
74
|
+
/// `TrapClass::MemoryLimit`) the trap's own `std::fmt::Display`
|
|
75
|
+
/// carries the user-facing reason (`"wall-clock deadline exceeded"`,
|
|
76
|
+
/// `"linear memory growth exceeded memory_limit: ..."`). The wasmtime
|
|
77
|
+
/// outer wrapper at `format!("{}", err)` would otherwise surface only
|
|
78
|
+
/// the `"error while executing at wasm backtrace: ..."` framing, which
|
|
79
|
+
/// is operator noise on a cap trap. For `TrapClass::Other` the
|
|
80
|
+
/// wasmtime wrapper IS the diagnostic (real script trap) so it stays.
|
|
81
|
+
pub(super) fn call_err(ruby: &Ruby, err: wasmtime::Error) -> MagnusError {
|
|
82
|
+
match classify_trap(&err) {
|
|
83
|
+
TrapClass::Timeout => {
|
|
84
|
+
let msg = err
|
|
85
|
+
.downcast_ref::<TimeoutTrap>()
|
|
86
|
+
.map(|t| t.to_string())
|
|
87
|
+
.unwrap_or_else(|| format!("{}", err));
|
|
88
|
+
timeout_err(ruby, msg)
|
|
89
|
+
}
|
|
90
|
+
TrapClass::MemoryLimit => {
|
|
91
|
+
let msg = err
|
|
92
|
+
.downcast_ref::<MemoryLimitTrap>()
|
|
93
|
+
.map(|t| t.to_string())
|
|
94
|
+
.unwrap_or_else(|| format!("{}", err));
|
|
95
|
+
memory_limit_err(ruby, msg)
|
|
96
|
+
}
|
|
97
|
+
TrapClass::Other => trap_err(ruby, format!("{}", err)),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Map an instantiation error to `Kobako::SetupError`. Instantiation runs
|
|
102
|
+
/// during `from_path` construction, before any invocation — docs/behavior.md
|
|
103
|
+
/// E-41 classifies every such failure as a construction setup fault, not a
|
|
104
|
+
/// per-invocation cap outcome. The memory cap is dormant during
|
|
105
|
+
/// instantiation (see `Invocation::arm_memory_cap` /
|
|
106
|
+
/// `Invocation::disarm_memory_cap`) and the epoch deadline is not yet
|
|
107
|
+
/// armed, so the `call_err` trap-class split does not apply here.
|
|
108
|
+
pub(super) fn instantiate_err(ruby: &Ruby, err: wasmtime::Error) -> MagnusError {
|
|
109
|
+
setup_err(ruby, format!("instantiate: {}", err))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#[cfg(test)]
|
|
113
|
+
mod tests {
|
|
114
|
+
use super::{classify_trap, TrapClass};
|
|
115
|
+
use crate::runtime::invocation::{MemoryLimitTrap, TimeoutTrap};
|
|
116
|
+
|
|
117
|
+
#[test]
|
|
118
|
+
fn classify_trap_routes_timeout_trap_to_timeout() {
|
|
119
|
+
let err = wasmtime::Error::new(TimeoutTrap);
|
|
120
|
+
assert_eq!(classify_trap(&err), TrapClass::Timeout);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#[test]
|
|
124
|
+
fn classify_trap_routes_memory_limit_trap_to_memory_limit() {
|
|
125
|
+
let err = wasmtime::Error::new(MemoryLimitTrap::new(1 << 20, 1 << 19));
|
|
126
|
+
assert_eq!(classify_trap(&err), TrapClass::MemoryLimit);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#[test]
|
|
130
|
+
fn classify_trap_falls_back_to_other_for_unknown_errors() {
|
|
131
|
+
let err = wasmtime::Error::msg("some other wasmtime fault");
|
|
132
|
+
assert_eq!(classify_trap(&err), TrapClass::Other);
|
|
133
|
+
}
|
|
134
|
+
}
|