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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +103 -0
- data/Cargo.lock +117 -140
- data/README.md +156 -118
- data/ROADMAP.md +23 -22
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +38 -0
- data/crates/kobako-runtime/Cargo.toml +2 -2
- data/crates/kobako-runtime/README.md +10 -12
- data/crates/kobako-runtime/src/error.rs +8 -9
- data/crates/kobako-runtime/src/lib.rs +1 -1
- data/crates/kobako-runtime/src/profile.rs +1 -1
- data/crates/kobako-runtime/src/runtime.rs +31 -17
- data/crates/kobako-runtime/src/snapshot.rs +13 -10
- data/crates/kobako-runtime/src/yielder.rs +9 -6
- data/crates/kobako-transport/CHANGELOG.md +35 -0
- data/crates/kobako-transport/Cargo.toml +1 -1
- data/crates/kobako-transport/README.md +43 -27
- data/crates/kobako-transport/src/abi.rs +11 -14
- data/crates/kobako-transport/src/envelope/bytes.rs +18 -6
- data/crates/kobako-transport/src/envelope/compile_reply.rs +139 -0
- data/crates/kobako-transport/src/envelope/outcome.rs +17 -16
- data/crates/kobako-transport/src/envelope/preamble.rs +513 -0
- data/crates/kobako-transport/src/envelope/reply.rs +77 -53
- data/crates/kobako-transport/src/envelope/request.rs +295 -0
- data/crates/kobako-transport/src/envelope/run.rs +1 -2
- data/crates/kobako-transport/src/envelope/tag.rs +49 -0
- data/crates/kobako-transport/src/envelope/unsupported.rs +105 -0
- data/crates/kobako-transport/src/envelope.rs +19 -12
- data/crates/kobako-transport/src/lib.rs +0 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +51 -0
- data/crates/kobako-wasmtime/Cargo.toml +7 -5
- data/crates/kobako-wasmtime/README.md +8 -10
- data/crates/kobako-wasmtime/src/abi.rs +5 -8
- data/crates/kobako-wasmtime/src/ambient.rs +2 -2
- data/crates/kobako-wasmtime/src/cache.rs +25 -59
- data/crates/kobako-wasmtime/src/capture.rs +4 -11
- data/crates/kobako-wasmtime/src/config.rs +11 -14
- data/crates/kobako-wasmtime/src/dispatch.rs +70 -80
- data/crates/kobako-wasmtime/src/driver.rs +133 -86
- data/crates/kobako-wasmtime/src/exports.rs +88 -34
- data/crates/kobako-wasmtime/src/guest_mem.rs +145 -138
- data/crates/kobako-wasmtime/src/instance_pre.rs +11 -22
- data/crates/kobako-wasmtime/src/invocation.rs +55 -372
- data/crates/kobako-wasmtime/src/io.rs +286 -0
- data/crates/kobako-wasmtime/src/lib.rs +3 -24
- data/crates/kobako-wasmtime/src/limiter.rs +253 -0
- data/crates/kobako-wasmtime/src/trap.rs +32 -45
- data/crates/kobako-wasmtime/tests/preamble_flags.rs +69 -0
- data/crates/kobako-wasmtime/tests/snippet_compile.rs +94 -0
- data/crates/kobako-wasmtime/tests/support/mod.rs +34 -0
- data/crates/kobako-wasmtime/tests/unsupported_call.rs +50 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +2 -2
- data/ext/kobako/src/runtime/bridge.rs +38 -47
- data/ext/kobako/src/runtime/errors.rs +26 -32
- data/ext/kobako/src/runtime/gvl.rs +4 -12
- data/ext/kobako/src/runtime.rs +169 -212
- data/lib/kobako/capture.rb +1 -4
- data/lib/kobako/catalog/extensions.rb +21 -59
- data/lib/kobako/catalog/flags.rb +41 -0
- data/lib/kobako/catalog/handles.rb +7 -54
- data/lib/kobako/catalog/services.rb +7 -61
- data/lib/kobako/catalog/snippets.rb +42 -66
- data/lib/kobako/catalog.rb +2 -10
- data/lib/kobako/codec/decoder.rb +9 -28
- data/lib/kobako/codec/encoder.rb +3 -22
- data/lib/kobako/codec/error.rb +6 -27
- data/lib/kobako/codec/ext_types.rb +72 -94
- data/lib/kobako/codec/handle_walk.rb +65 -130
- data/lib/kobako/codec/nesting.rb +3 -8
- data/lib/kobako/codec/state.rb +4 -10
- data/lib/kobako/codec/utils.rb +6 -34
- data/lib/kobako/codec.rb +3 -22
- data/lib/kobako/constant_name.rb +8 -0
- data/lib/kobako/context.rb +51 -112
- data/lib/kobako/errors.rb +51 -127
- data/lib/kobako/execution.rb +16 -21
- data/lib/kobako/extension.rb +23 -40
- data/lib/kobako/handle.rb +7 -12
- data/lib/kobako/outcome.rb +51 -80
- data/lib/kobako/payload/arguments.rb +1 -16
- data/lib/kobako/payload.rb +4 -12
- data/lib/kobako/pool.rb +23 -53
- data/lib/kobako/runtime.rb +7 -21
- data/lib/kobako/sandbox.rb +96 -133
- data/lib/kobako/sandbox_options.rb +38 -84
- data/lib/kobako/snippet/binary.rb +1 -16
- data/lib/kobako/snippet/source.rb +1 -16
- data/lib/kobako/snippet.rb +1 -9
- data/lib/kobako/transport/call.rb +1 -14
- data/lib/kobako/transport/dispatcher.rb +112 -195
- data/lib/kobako/transport/error.rb +6 -20
- data/lib/kobako/transport/exposure.rb +5 -10
- data/lib/kobako/transport/reflection.rb +39 -50
- data/lib/kobako/transport/run.rb +17 -71
- data/lib/kobako/transport/yielder.rb +21 -73
- data/lib/kobako/transport.rb +3 -15
- data/lib/kobako/unresolved.rb +4 -11
- data/lib/kobako/usage.rb +2 -6
- data/lib/kobako/version.rb +1 -1
- data/sig/kobako/catalog/extensions.rbs +2 -2
- data/sig/kobako/catalog/flags.rbs +17 -0
- data/sig/kobako/catalog/services.rbs +0 -6
- data/sig/kobako/catalog/snippets.rbs +4 -4
- data/sig/kobako/codec/ext_types.rbs +10 -8
- data/sig/kobako/codec/handle_walk.rbs +9 -7
- data/sig/kobako/codec/utils.rbs +2 -2
- data/sig/kobako/codec.rbs +2 -2
- data/sig/kobako/constant_name.rbs +3 -0
- data/sig/kobako/context.rbs +2 -3
- data/sig/kobako/errors.rbs +6 -0
- data/sig/kobako/outcome.rbs +6 -8
- data/sig/kobako/runtime.rbs +12 -8
- data/sig/kobako/sandbox.rbs +4 -0
- data/sig/kobako/sandbox_options.rbs +2 -6
- data/sig/kobako/transport/dispatcher.rbs +14 -18
- data/sig/kobako/transport/reflection.rbs +5 -3
- data/sig/kobako/transport/run.rbs +1 -3
- data/sig/kobako/transport/yielder.rbs +3 -3
- data/sig/kobako/transport.rbs +7 -6
- metadata +16 -4
- data/crates/kobako-transport/src/envelope/invocation_frames.rs +0 -241
- data/crates/kobako-wasmtime/src/frames.rs +0 -273
- data/rust-toolchain.toml +0 -11
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//! The answer to a compile downcall.
|
|
2
|
+
//!
|
|
3
|
+
//! Only the guest can compile its own source, so the host learns the
|
|
4
|
+
//! bytecode, or why there is none, from this answer alone.
|
|
5
|
+
|
|
6
|
+
use super::bytes::{Reader, Writer};
|
|
7
|
+
use super::{tag, DecodeError, ErrorRecord, Unsupported};
|
|
8
|
+
|
|
9
|
+
/// How compiling the Preamble's source Snippets ended.
|
|
10
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
11
|
+
pub enum CompileReply {
|
|
12
|
+
/// One bytecode per source Snippet, in the Preamble's order.
|
|
13
|
+
Ok(Vec<Vec<u8>>),
|
|
14
|
+
/// A Snippet did not compile, or a Flag could not be applied first.
|
|
15
|
+
Error(ErrorRecord),
|
|
16
|
+
/// The guest does not compile.
|
|
17
|
+
Unsupported(Unsupported),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
impl CompileReply {
|
|
21
|
+
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
22
|
+
let mut reader = Reader::new(bytes);
|
|
23
|
+
match reader.u8()? {
|
|
24
|
+
tag::OK => {
|
|
25
|
+
let bytecodes = reader.bytes_list()?;
|
|
26
|
+
reader.finish()?;
|
|
27
|
+
Ok(CompileReply::Ok(bytecodes))
|
|
28
|
+
}
|
|
29
|
+
tag::ERROR => {
|
|
30
|
+
let record = ErrorRecord::read(&mut reader)?;
|
|
31
|
+
reader.finish()?;
|
|
32
|
+
Ok(CompileReply::Error(record))
|
|
33
|
+
}
|
|
34
|
+
tag::UNSUPPORTED => Ok(CompileReply::Unsupported(Unsupported::read(&mut reader)?)),
|
|
35
|
+
_ => Err(DecodeError::new(
|
|
36
|
+
"Compile Reply tag must be 0x10 (ok), 0x13 (error), or 0x15 (unsupported)",
|
|
37
|
+
)),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn encode(&self) -> Vec<u8> {
|
|
42
|
+
let mut writer = Writer::new();
|
|
43
|
+
match self {
|
|
44
|
+
CompileReply::Ok(bytecodes) => {
|
|
45
|
+
writer.u8(tag::OK).list(bytecodes);
|
|
46
|
+
}
|
|
47
|
+
CompileReply::Error(record) => {
|
|
48
|
+
writer.u8(tag::ERROR);
|
|
49
|
+
record.write(&mut writer);
|
|
50
|
+
}
|
|
51
|
+
CompileReply::Unsupported(refusal) => return refusal.encode(),
|
|
52
|
+
}
|
|
53
|
+
writer.into_bytes()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[cfg(test)]
|
|
58
|
+
mod tests {
|
|
59
|
+
use super::*;
|
|
60
|
+
|
|
61
|
+
// @behavior WE-085
|
|
62
|
+
#[test]
|
|
63
|
+
fn every_compile_reply_arm_round_trips() {
|
|
64
|
+
for reply in [
|
|
65
|
+
CompileReply::Ok(vec![vec![0x52, 0x49], Vec::new()]),
|
|
66
|
+
CompileReply::Ok(Vec::new()),
|
|
67
|
+
CompileReply::Error(ErrorRecord {
|
|
68
|
+
name: "SyntaxError".into(),
|
|
69
|
+
message: "unexpected end".into(),
|
|
70
|
+
backtrace: vec!["(snippet:A):1".into()],
|
|
71
|
+
}),
|
|
72
|
+
CompileReply::Unsupported(Unsupported::new("not compiled here")),
|
|
73
|
+
] {
|
|
74
|
+
assert_eq!(
|
|
75
|
+
CompileReply::decode(&reply.encode()),
|
|
76
|
+
Ok(reply),
|
|
77
|
+
"a Compile Reply through encode and decode must arrive as it was given on \
|
|
78
|
+
every arm"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// @behavior WE-086
|
|
84
|
+
#[test]
|
|
85
|
+
fn golden_layout_pins_the_compile_reply_tags() {
|
|
86
|
+
assert_eq!(
|
|
87
|
+
CompileReply::Ok(vec![vec![0xaa], vec![0xbb, 0xcc]]).encode(),
|
|
88
|
+
vec![
|
|
89
|
+
0x10, // tag: ok
|
|
90
|
+
0, 0, 0, 2, // bytecode count
|
|
91
|
+
0, 0, 0, 1, 0xaa, // first bytecode
|
|
92
|
+
0, 0, 0, 2, 0xbb, 0xcc, // second bytecode
|
|
93
|
+
],
|
|
94
|
+
"a compiled answer must encode as tag byte 0x10 followed by a counted list of \
|
|
95
|
+
length-prefixed bytecodes"
|
|
96
|
+
);
|
|
97
|
+
assert_eq!(
|
|
98
|
+
CompileReply::Error(ErrorRecord {
|
|
99
|
+
name: "E".into(),
|
|
100
|
+
message: "m".into(),
|
|
101
|
+
backtrace: Vec::new(),
|
|
102
|
+
})
|
|
103
|
+
.encode(),
|
|
104
|
+
vec![
|
|
105
|
+
0x13, // tag: error
|
|
106
|
+
0, 0, 0, 1, b'E', // name
|
|
107
|
+
0, 0, 0, 1, b'm', // message
|
|
108
|
+
0, 0, 0, 0, // backtrace count
|
|
109
|
+
],
|
|
110
|
+
"a failed compile must encode as tag byte 0x13 followed by an Error Record"
|
|
111
|
+
);
|
|
112
|
+
assert_eq!(
|
|
113
|
+
CompileReply::Unsupported(Unsupported::new("x")).encode(),
|
|
114
|
+
vec![0x15, 0, 0, 0, 0, 1, b'x'],
|
|
115
|
+
"a guest that does not compile must answer on tag byte 0x15"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// @behavior WE-087
|
|
120
|
+
#[test]
|
|
121
|
+
fn a_compile_reply_tag_outside_its_arms_is_refused() {
|
|
122
|
+
for bytes in [&[0x11, 0, 0, 0, 0][..], &[0x14][..], &[][..]] {
|
|
123
|
+
assert!(
|
|
124
|
+
CompileReply::decode(bytes).is_err(),
|
|
125
|
+
"a Compile Reply {bytes:02x?} through CompileReply::decode must be refused"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// @behavior WE-088
|
|
131
|
+
#[test]
|
|
132
|
+
fn a_compiled_answer_with_trailing_bytes_is_refused() {
|
|
133
|
+
assert!(
|
|
134
|
+
CompileReply::decode(&[0x10, 0, 0, 0, 0, 0xff]).is_err(),
|
|
135
|
+
"a compiled answer with bytes after its list through CompileReply::decode must be \
|
|
136
|
+
refused as a framing desync"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -5,10 +5,7 @@
|
|
|
5
5
|
//! byte. Only the ok arm carries codec-encoded bytes.
|
|
6
6
|
|
|
7
7
|
use super::bytes::{Reader, Writer};
|
|
8
|
-
use super::{DecodeError, ErrorRecord};
|
|
9
|
-
|
|
10
|
-
const TAG_OK: u8 = 0x01;
|
|
11
|
-
const TAG_PANIC: u8 = 0x02;
|
|
8
|
+
use super::{tag, DecodeError, ErrorRecord, Unsupported};
|
|
12
9
|
|
|
13
10
|
/// Who a failed invocation attributes to.
|
|
14
11
|
///
|
|
@@ -52,6 +49,8 @@ pub enum Outcome {
|
|
|
52
49
|
Ok(Vec<u8>),
|
|
53
50
|
/// The invocation terminated with an uncaught exception.
|
|
54
51
|
Panic(Panic),
|
|
52
|
+
/// The guest does not serve the invocation it was sent.
|
|
53
|
+
Unsupported(Unsupported),
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/// An uncaught top-level failure, plus what attribution and correction need.
|
|
@@ -68,8 +67,8 @@ impl Outcome {
|
|
|
68
67
|
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
69
68
|
let mut reader = Reader::new(bytes);
|
|
70
69
|
match reader.u8()? {
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
tag::OK => Ok(Outcome::Ok(reader.remaining().to_vec())),
|
|
71
|
+
tag::PANIC => {
|
|
73
72
|
let origin = Origin::from_name(reader.text()?);
|
|
74
73
|
let error = ErrorRecord::read(&mut reader)?;
|
|
75
74
|
let available = reader.text_list()?;
|
|
@@ -80,8 +79,9 @@ impl Outcome {
|
|
|
80
79
|
available,
|
|
81
80
|
}))
|
|
82
81
|
}
|
|
82
|
+
tag::UNSUPPORTED => Ok(Outcome::Unsupported(Unsupported::read(&mut reader)?)),
|
|
83
83
|
_ => Err(DecodeError::new(
|
|
84
|
-
"Outcome tag must be
|
|
84
|
+
"Outcome tag must be 0x10 (ok), 0x14 (panic), or 0x15 (unsupported)",
|
|
85
85
|
)),
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -90,13 +90,14 @@ impl Outcome {
|
|
|
90
90
|
let mut writer = Writer::new();
|
|
91
91
|
match self {
|
|
92
92
|
Outcome::Ok(value) => {
|
|
93
|
-
writer.u8(
|
|
93
|
+
writer.u8(tag::OK).remainder(value);
|
|
94
94
|
}
|
|
95
95
|
Outcome::Panic(panic) => {
|
|
96
|
-
writer.u8(
|
|
96
|
+
writer.u8(tag::PANIC).bytes(panic.origin.name().as_bytes());
|
|
97
97
|
panic.error.write(&mut writer);
|
|
98
98
|
writer.list(&panic.available);
|
|
99
99
|
}
|
|
100
|
+
Outcome::Unsupported(refusal) => return refusal.encode(),
|
|
100
101
|
}
|
|
101
102
|
writer.into_bytes()
|
|
102
103
|
}
|
|
@@ -203,7 +204,7 @@ mod tests {
|
|
|
203
204
|
// this is the shape a third-party guest can legally produce and the
|
|
204
205
|
// one the fallback rule exists for.
|
|
205
206
|
let mut writer = Writer::new();
|
|
206
|
-
writer.u8(
|
|
207
|
+
writer.u8(tag::PANIC).bytes(b"something-else");
|
|
207
208
|
panic_sample().error.write(&mut writer);
|
|
208
209
|
writer.list::<&[u8]>(&[]);
|
|
209
210
|
assert_eq!(
|
|
@@ -219,8 +220,8 @@ mod tests {
|
|
|
219
220
|
fn golden_layout_pins_the_ok_tag() {
|
|
220
221
|
assert_eq!(
|
|
221
222
|
Outcome::Ok(vec![0x2a]).encode(),
|
|
222
|
-
vec![
|
|
223
|
-
"an ok Outcome must encode as tag byte
|
|
223
|
+
vec![0x10, 0x2a],
|
|
224
|
+
"an ok Outcome must encode as tag byte 0x10 followed by the value alone"
|
|
224
225
|
);
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -239,7 +240,7 @@ mod tests {
|
|
|
239
240
|
assert_eq!(
|
|
240
241
|
Outcome::Panic(panic).encode(),
|
|
241
242
|
vec![
|
|
242
|
-
|
|
243
|
+
0x14, // tag: panic
|
|
243
244
|
0, 0, 0, 7, b's', b'e', b'r', b'v', b'i', b'c', b'e', // origin
|
|
244
245
|
0, 0, 0, 1, b'E', // name
|
|
245
246
|
0, 0, 0, 1, b'm', // message
|
|
@@ -257,7 +258,7 @@ mod tests {
|
|
|
257
258
|
fn a_zero_length_outcome_is_refused() {
|
|
258
259
|
assert!(
|
|
259
260
|
Outcome::decode(&[]).is_err(),
|
|
260
|
-
"a zero-length
|
|
261
|
+
"a zero-length Outcome must be rejected as a wire violation"
|
|
261
262
|
);
|
|
262
263
|
}
|
|
263
264
|
|
|
@@ -265,8 +266,8 @@ mod tests {
|
|
|
265
266
|
#[test]
|
|
266
267
|
fn an_unknown_outcome_tag_is_refused() {
|
|
267
268
|
assert!(
|
|
268
|
-
Outcome::decode(&[
|
|
269
|
-
"an Outcome tag
|
|
269
|
+
Outcome::decode(&[0x11, 0x00]).is_err(),
|
|
270
|
+
"an Outcome tag outside its arms must be rejected as a wire violation"
|
|
270
271
|
);
|
|
271
272
|
}
|
|
272
273
|
}
|