kobako 0.28.0 → 0.30.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 +377 -0
- data/Cargo.lock +181 -154
- data/README.md +99 -459
- data/ROADMAP.md +7 -1
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +354 -0
- data/crates/kobako-runtime/Cargo.toml +38 -7
- data/crates/kobako-runtime/README.md +17 -8
- data/crates/kobako-runtime/src/dispatch.rs +9 -1
- data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
- data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
- data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
- data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
- data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
- data/crates/kobako-runtime/src/endpoint.rs +166 -0
- data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
- data/crates/kobako-runtime/src/filesystem/bind/lookup.rs +236 -0
- data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
- data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
- data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
- data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
- data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +301 -0
- data/crates/kobako-runtime/src/filesystem/overlay.rs +3469 -0
- data/crates/kobako-runtime/src/filesystem/path.rs +21 -0
- data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
- data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
- data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
- data/crates/kobako-runtime/src/filesystem.rs +26 -0
- data/crates/kobako-runtime/src/grant.rs +20 -0
- data/crates/kobako-runtime/src/hashtable.rs +44 -0
- data/crates/kobako-runtime/src/lib.rs +16 -1
- data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
- data/crates/kobako-runtime/src/locking.rs +218 -0
- data/crates/kobako-runtime/src/mount.rs +105 -0
- data/crates/kobako-runtime/src/profile.rs +140 -4
- data/crates/kobako-runtime/src/runtime.rs +12 -0
- data/crates/kobako-runtime/src/sched.rs +210 -0
- data/crates/kobako-runtime/src/security.rs +696 -0
- data/crates/kobako-runtime/src/snapshot.rs +20 -5
- data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
- data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
- data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
- data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
- data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
- data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
- data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
- data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
- data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
- data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
- data/crates/kobako-runtime/src/vfs/path.rs +64 -0
- data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
- data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
- data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
- data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
- data/crates/kobako-runtime/src/vfs.rs +60 -0
- data/crates/kobako-runtime/src/yielder.rs +11 -6
- data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
- data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
- data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
- data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
- data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
- data/crates/kobako-transport/CHANGELOG.md +23 -0
- data/crates/kobako-transport/Cargo.toml +3 -3
- data/crates/kobako-transport/README.md +6 -5
- data/crates/kobako-transport/src/abi.rs +1 -1
- data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
- data/crates/kobako-transport/src/envelope/call.rs +5 -5
- data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
- data/crates/kobako-transport/src/envelope/fault.rs +4 -3
- data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
- data/crates/kobako-transport/src/envelope/preamble.rs +24 -24
- data/crates/kobako-transport/src/envelope/reply.rs +34 -16
- data/crates/kobako-transport/src/envelope/request.rs +19 -9
- data/crates/kobako-transport/src/envelope/run.rs +1 -1
- data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
- data/crates/kobako-transport/src/envelope.rs +2 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +265 -0
- data/crates/kobako-wasmtime/Cargo.toml +25 -19
- data/crates/kobako-wasmtime/README.md +13 -7
- data/crates/kobako-wasmtime/src/abi.rs +75 -49
- data/crates/kobako-wasmtime/src/ambient.rs +64 -6
- data/crates/kobako-wasmtime/src/cache.rs +188 -77
- data/crates/kobako-wasmtime/src/config.rs +77 -5
- data/crates/kobako-wasmtime/src/descriptors.rs +200 -0
- data/crates/kobako-wasmtime/src/driver.rs +70 -135
- data/crates/kobako-wasmtime/src/exports.rs +5 -16
- data/crates/kobako-wasmtime/src/guest.rs +38 -0
- data/crates/kobako-wasmtime/src/imports.rs +52 -0
- data/crates/kobako-wasmtime/src/instance_pre.rs +18 -46
- data/crates/kobako-wasmtime/src/invocation.rs +43 -74
- data/crates/kobako-wasmtime/src/lib.rs +5 -4
- data/crates/kobako-wasmtime/src/limiter.rs +6 -230
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +632 -0
- data/crates/kobako-wasmtime/src/preview1/differential.rs +486 -0
- data/crates/kobako-wasmtime/src/preview1/errno.rs +135 -0
- data/crates/kobako-wasmtime/src/preview1/fd.rs +1420 -0
- data/crates/kobako-wasmtime/src/preview1/memory.rs +172 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +3766 -0
- data/crates/kobako-wasmtime/src/preview1/path.rs +302 -0
- data/crates/kobako-wasmtime/src/preview1/poll.rs +381 -0
- data/crates/kobako-wasmtime/src/preview1/sock.rs +13 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
- data/crates/kobako-wasmtime/src/preview1.rs +512 -0
- data/crates/kobako-wasmtime/src/trap.rs +45 -25
- data/crates/kobako-wasmtime/src/wasi.rs +333 -0
- data/crates/kobako-wasmtime/tests/integration/compile_posture.rs +43 -0
- data/crates/kobako-wasmtime/tests/integration/guest_file.rs +1085 -0
- data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
- data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +4 -3
- data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +5 -4
- data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +9 -13
- data/crates/kobako-wasmtime/tests/integration/unanswered_call.rs +86 -0
- data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +2 -1
- data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
- data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +4 -2
- data/ext/kobako/src/runtime/bridge.rs +154 -81
- data/ext/kobako/src/runtime/errors.rs +1 -1
- data/ext/kobako/src/runtime/escape.rs +67 -0
- data/ext/kobako/src/runtime/filesystem/grants.rs +107 -0
- data/ext/kobako/src/runtime/filesystem/ruby.rs +708 -0
- data/ext/kobako/src/runtime/filesystem.rs +208 -0
- data/ext/kobako/src/runtime/gvl.rs +49 -8
- data/ext/kobako/src/runtime/pinned_value.rs +31 -0
- data/ext/kobako/src/runtime/snapshot.rs +127 -0
- data/ext/kobako/src/runtime.rs +125 -199
- data/lib/kobako/catalog/extensions.rb +5 -5
- data/lib/kobako/catalog/flags.rb +2 -14
- data/lib/kobako/catalog/grants.rb +30 -0
- data/lib/kobako/catalog/handles.rb +6 -6
- data/lib/kobako/catalog/mount.rb +19 -0
- data/lib/kobako/catalog/services.rb +13 -2
- data/lib/kobako/catalog/snippets.rb +11 -15
- data/lib/kobako/catalog.rb +11 -0
- data/lib/kobako/codec/decoder.rb +10 -9
- data/lib/kobako/codec/ext_types.rb +4 -6
- data/lib/kobako/codec/handle_walk.rb +4 -6
- data/lib/kobako/codec/nesting.rb +7 -7
- data/lib/kobako/codec/state.rb +19 -3
- data/lib/kobako/codec/utils.rb +1 -1
- data/lib/kobako/codec/value_budget.rb +86 -0
- data/lib/kobako/codec.rb +7 -0
- data/lib/kobako/context.rb +40 -13
- data/lib/kobako/errors.rb +15 -14
- data/lib/kobako/filesystem.rb +69 -0
- data/lib/kobako/payload/arguments.rb +2 -2
- data/lib/kobako/pool.rb +6 -6
- data/lib/kobako/sandbox.rb +55 -27
- data/lib/kobako/sandbox_options.rb +39 -13
- data/lib/kobako/snippet/{binary.rb → bytecode.rb} +2 -2
- data/lib/kobako/snippet/source.rb +1 -1
- data/lib/kobako/snippet.rb +1 -1
- data/lib/kobako/transport/block_failure.rb +35 -0
- data/lib/kobako/transport/dispatcher.rb +33 -29
- data/lib/kobako/transport/exposure.rb +7 -9
- data/lib/kobako/transport/reflection.rb +79 -23
- data/lib/kobako/transport/run.rb +5 -5
- data/lib/kobako/transport/yielder.rb +9 -31
- data/lib/kobako/transport.rb +1 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +35 -0
- data/release-please-config.json +1 -0
- data/sig/kobako/catalog/extensions.rbs +3 -3
- data/sig/kobako/catalog/flags.rbs +0 -4
- data/sig/kobako/catalog/grants.rbs +13 -0
- data/sig/kobako/catalog/handles.rbs +3 -3
- data/sig/kobako/catalog/mount.rbs +24 -0
- data/sig/kobako/catalog/services.rbs +5 -1
- data/sig/kobako/catalog/snippets.rbs +4 -4
- data/sig/kobako/catalog.rbs +1 -0
- data/sig/kobako/codec/decoder.rbs +1 -1
- data/sig/kobako/codec/handle_walk.rbs +1 -1
- data/sig/kobako/codec/nesting.rbs +2 -2
- data/sig/kobako/codec/state.rbs +7 -1
- data/sig/kobako/codec/utils.rbs +1 -1
- data/sig/kobako/codec/value_budget.rbs +23 -0
- data/sig/kobako/codec.rbs +3 -2
- data/sig/kobako/context.rbs +14 -6
- data/sig/kobako/errors.rbs +4 -4
- data/sig/kobako/filesystem.rbs +93 -0
- data/sig/kobako/guest_yielder.rbs +9 -0
- data/sig/kobako/payload/arguments.rbs +1 -1
- data/sig/kobako/pool.rbs +3 -3
- data/sig/kobako/runtime.rbs +21 -49
- data/sig/kobako/sandbox.rbs +9 -5
- data/sig/kobako/sandbox_options.rbs +25 -3
- data/sig/kobako/snapshot.rbs +20 -0
- data/sig/kobako/snippet/bytecode.rbs +12 -0
- data/sig/kobako/transport/block_failure.rbs +13 -0
- data/sig/kobako/transport/dispatcher.rbs +4 -4
- data/sig/kobako/transport/exposure.rbs +2 -2
- data/sig/kobako/transport/reflection.rbs +14 -1
- data/sig/kobako/transport/run.rbs +3 -3
- data/sig/kobako/transport/yielder.rbs +3 -7
- data/sig/kobako/transport.rbs +3 -3
- metadata +116 -15
- data/crates/kobako-wasmtime/src/capture.rs +0 -96
- data/crates/kobako-wasmtime/src/dispatch.rs +0 -107
- data/crates/kobako-wasmtime/src/guest_mem.rs +0 -265
- data/crates/kobako-wasmtime/src/io.rs +0 -286
- data/sig/kobako/snippet/binary.rbs +0 -12
|
@@ -66,7 +66,7 @@ impl Preamble {
|
|
|
66
66
|
/// appear at most once; an absent one reads as empty.
|
|
67
67
|
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
68
68
|
let mut reader = Reader::new(bytes);
|
|
69
|
-
let count = reader.
|
|
69
|
+
let count = reader.read_u32()? as usize;
|
|
70
70
|
// Each record costs at least six bytes, its two leading bytes and a
|
|
71
71
|
// body length, so a count past the bytes left can never be
|
|
72
72
|
// satisfied; refusing it bounds the work.
|
|
@@ -79,13 +79,13 @@ impl Preamble {
|
|
|
79
79
|
let mut snippets = None;
|
|
80
80
|
let mut flags = Vec::new();
|
|
81
81
|
for _ in 0..count {
|
|
82
|
-
let critical = match reader.
|
|
82
|
+
let critical = match reader.read_u8()? {
|
|
83
83
|
ADVISORY => false,
|
|
84
84
|
CRITICAL => true,
|
|
85
85
|
_ => return Err(DecodeError::new("Preamble criticality must be 0 or 1")),
|
|
86
86
|
};
|
|
87
|
-
let kind = reader.
|
|
88
|
-
let body = reader.
|
|
87
|
+
let kind = reader.read_u8()?;
|
|
88
|
+
let body = reader.read_bytes()?;
|
|
89
89
|
match kind {
|
|
90
90
|
tag::BINDINGS => once(&mut bindings, decode_bindings(body)?)?,
|
|
91
91
|
tag::SNIPPETS => once(&mut snippets, decode_snippets(body)?)?,
|
|
@@ -147,7 +147,7 @@ fn once<T>(slot: &mut Option<T>, value: T) -> Result<(), DecodeError> {
|
|
|
147
147
|
|
|
148
148
|
fn decode_bindings(bytes: &[u8]) -> Result<Bindings, DecodeError> {
|
|
149
149
|
let mut reader = Reader::new(bytes);
|
|
150
|
-
let paths = reader.
|
|
150
|
+
let paths = reader.read_text_list()?;
|
|
151
151
|
reader.finish()?;
|
|
152
152
|
Ok(Bindings { paths })
|
|
153
153
|
}
|
|
@@ -160,7 +160,7 @@ fn encode_bindings(bindings: &Bindings) -> Vec<u8> {
|
|
|
160
160
|
|
|
161
161
|
fn decode_snippets(bytes: &[u8]) -> Result<Snippets, DecodeError> {
|
|
162
162
|
let mut reader = Reader::new(bytes);
|
|
163
|
-
let count = reader.
|
|
163
|
+
let count = reader.read_u32()? as usize;
|
|
164
164
|
// Each entry costs at least a kind byte and a `u32` length, so a count
|
|
165
165
|
// past what the bytes left can hold is refused before it sizes the
|
|
166
166
|
// allocation.
|
|
@@ -171,13 +171,13 @@ fn decode_snippets(bytes: &[u8]) -> Result<Snippets, DecodeError> {
|
|
|
171
171
|
}
|
|
172
172
|
let mut entries = Vec::with_capacity(count);
|
|
173
173
|
for _ in 0..count {
|
|
174
|
-
entries.push(match reader.
|
|
174
|
+
entries.push(match reader.read_u8()? {
|
|
175
175
|
SNIPPET_SOURCE => Snippet::Source {
|
|
176
|
-
name: reader.
|
|
177
|
-
body: reader.
|
|
176
|
+
name: reader.read_text()?.to_owned(),
|
|
177
|
+
body: reader.read_text()?.to_owned(),
|
|
178
178
|
},
|
|
179
179
|
SNIPPET_BYTECODE => Snippet::Bytecode {
|
|
180
|
-
body: reader.
|
|
180
|
+
body: reader.read_bytes()?.to_vec(),
|
|
181
181
|
},
|
|
182
182
|
_ => return Err(DecodeError::new("Preamble snippet kind must be 0 or 1")),
|
|
183
183
|
});
|
|
@@ -207,8 +207,8 @@ fn encode_snippets(snippets: &Snippets) -> Vec<u8> {
|
|
|
207
207
|
|
|
208
208
|
fn decode_flag(bytes: &[u8], critical: bool) -> Result<Flag, DecodeError> {
|
|
209
209
|
let mut reader = Reader::new(bytes);
|
|
210
|
-
let name = reader.
|
|
211
|
-
let value = reader.
|
|
210
|
+
let name = reader.read_text()?.to_owned();
|
|
211
|
+
let value = reader.read_text()?.to_owned();
|
|
212
212
|
reader.finish()?;
|
|
213
213
|
Ok(Flag {
|
|
214
214
|
name,
|
|
@@ -243,7 +243,7 @@ mod tests {
|
|
|
243
243
|
writer.into_bytes()
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
fn
|
|
246
|
+
fn preamble_with(records: &[Vec<u8>]) -> Vec<u8> {
|
|
247
247
|
let mut bytes = (records.len() as u32).to_be_bytes().to_vec();
|
|
248
248
|
for record in records {
|
|
249
249
|
bytes.extend_from_slice(record);
|
|
@@ -307,7 +307,7 @@ mod tests {
|
|
|
307
307
|
// @behavior WE-053
|
|
308
308
|
#[test]
|
|
309
309
|
fn an_absent_record_reads_as_empty() {
|
|
310
|
-
let bytes =
|
|
310
|
+
let bytes = preamble_with(&[record(0, 0x07, &encode_flag(&flag("f", "", false)))]);
|
|
311
311
|
let preamble = Preamble::decode(&bytes).expect("a flag-only Preamble must decode");
|
|
312
312
|
assert_eq!(
|
|
313
313
|
(preamble.bindings, preamble.snippets),
|
|
@@ -375,7 +375,7 @@ mod tests {
|
|
|
375
375
|
// @behavior WE-056
|
|
376
376
|
#[test]
|
|
377
377
|
fn an_unknown_snippet_kind_is_refused() {
|
|
378
|
-
let bytes =
|
|
378
|
+
let bytes = preamble_with(&[record(1, 0x06, &[0, 0, 0, 1, 9])]);
|
|
379
379
|
assert!(
|
|
380
380
|
Preamble::decode(&bytes).is_err(),
|
|
381
381
|
"a snippet kind that is neither source nor bytecode through Preamble::decode must \
|
|
@@ -387,7 +387,7 @@ mod tests {
|
|
|
387
387
|
#[test]
|
|
388
388
|
fn a_count_the_preamble_cannot_satisfy_is_refused() {
|
|
389
389
|
let records = [0xff, 0xff, 0xff, 0xff];
|
|
390
|
-
let snippets =
|
|
390
|
+
let snippets = preamble_with(&[record(1, 0x06, &[0xff, 0xff, 0xff, 0xff])]);
|
|
391
391
|
assert!(
|
|
392
392
|
Preamble::decode(&records).is_err() && Preamble::decode(&snippets).is_err(),
|
|
393
393
|
"a record or snippet count larger than what follows through Preamble::decode must \
|
|
@@ -400,7 +400,7 @@ mod tests {
|
|
|
400
400
|
fn trailing_bytes_are_refused() {
|
|
401
401
|
let mut past_the_list = Preamble::default().encode();
|
|
402
402
|
past_the_list.push(0);
|
|
403
|
-
let past_a_body =
|
|
403
|
+
let past_a_body = preamble_with(&[record(1, 0x05, &[0, 0, 0, 0, 0])]);
|
|
404
404
|
assert!(
|
|
405
405
|
Preamble::decode(&past_the_list).is_err() && Preamble::decode(&past_a_body).is_err(),
|
|
406
406
|
"bytes past the last record or past a record body's last field through \
|
|
@@ -411,7 +411,7 @@ mod tests {
|
|
|
411
411
|
// @behavior WE-077
|
|
412
412
|
#[test]
|
|
413
413
|
fn an_advisory_record_of_an_unknown_kind_is_skipped() {
|
|
414
|
-
let bytes =
|
|
414
|
+
let bytes = preamble_with(&[
|
|
415
415
|
record(0, 0x7e, b"anything"),
|
|
416
416
|
record(
|
|
417
417
|
1,
|
|
@@ -432,7 +432,7 @@ mod tests {
|
|
|
432
432
|
// @behavior WE-078
|
|
433
433
|
#[test]
|
|
434
434
|
fn a_critical_record_of_an_unknown_kind_is_refused() {
|
|
435
|
-
let bytes =
|
|
435
|
+
let bytes = preamble_with(&[record(1, 0x7e, b"anything")]);
|
|
436
436
|
assert!(
|
|
437
437
|
Preamble::decode(&bytes).is_err(),
|
|
438
438
|
"a critical record of a kind the registry has not assigned through \
|
|
@@ -444,7 +444,7 @@ mod tests {
|
|
|
444
444
|
#[test]
|
|
445
445
|
fn a_record_kind_assigned_to_another_message_is_refused() {
|
|
446
446
|
for kind in [0x00, 0x01, 0x10] {
|
|
447
|
-
let bytes =
|
|
447
|
+
let bytes = preamble_with(&[record(0, kind, b"")]);
|
|
448
448
|
assert!(
|
|
449
449
|
Preamble::decode(&bytes).is_err(),
|
|
450
450
|
"an advisory record of kind {kind:#04x}, which the registry assigns elsewhere, \
|
|
@@ -459,7 +459,7 @@ mod tests {
|
|
|
459
459
|
let bindings = encode_bindings(&Bindings::default());
|
|
460
460
|
let snippets = encode_snippets(&Snippets::default());
|
|
461
461
|
for (kind, body) in [(0x05, bindings), (0x06, snippets)] {
|
|
462
|
-
let bytes =
|
|
462
|
+
let bytes = preamble_with(&[record(1, kind, &body), record(1, kind, &body)]);
|
|
463
463
|
assert!(
|
|
464
464
|
Preamble::decode(&bytes).is_err(),
|
|
465
465
|
"a second record of kind {kind:#04x} through Preamble::decode must be refused"
|
|
@@ -485,7 +485,7 @@ mod tests {
|
|
|
485
485
|
// @behavior WE-082
|
|
486
486
|
#[test]
|
|
487
487
|
fn a_criticality_that_is_neither_value_is_refused() {
|
|
488
|
-
let bytes =
|
|
488
|
+
let bytes = preamble_with(&[record(2, 0x07, &encode_flag(&flag("f", "", false)))]);
|
|
489
489
|
assert!(
|
|
490
490
|
Preamble::decode(&bytes).is_err(),
|
|
491
491
|
"a criticality byte other than 0 or 1 through Preamble::decode must be refused"
|
|
@@ -504,8 +504,8 @@ mod tests {
|
|
|
504
504
|
);
|
|
505
505
|
let flag = record(0, 0x07, &encode_flag(&flag("f", "", false)));
|
|
506
506
|
assert_eq!(
|
|
507
|
-
Preamble::decode(&
|
|
508
|
-
Preamble::decode(&
|
|
507
|
+
Preamble::decode(&preamble_with(&[flag.clone(), bindings.clone()])),
|
|
508
|
+
Preamble::decode(&preamble_with(&[bindings, flag])),
|
|
509
509
|
"the same records in another order through Preamble::decode must read as the same \
|
|
510
510
|
Preamble"
|
|
511
511
|
);
|
|
@@ -17,6 +17,9 @@ pub enum Reply {
|
|
|
17
17
|
/// every field of a Fault is kobako's, so a guest reads a refusal
|
|
18
18
|
/// whatever schema it speaks.
|
|
19
19
|
Fault(Fault),
|
|
20
|
+
/// A block exit unwound the method; the guest continues that exit,
|
|
21
|
+
/// which it kept, so nothing of it crosses.
|
|
22
|
+
Break,
|
|
20
23
|
/// The receiver does not serve the request it was sent.
|
|
21
24
|
Unsupported(Unsupported),
|
|
22
25
|
}
|
|
@@ -24,14 +27,18 @@ pub enum Reply {
|
|
|
24
27
|
impl Reply {
|
|
25
28
|
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
26
29
|
let mut reader = Reader::new(bytes);
|
|
27
|
-
match reader.
|
|
30
|
+
match reader.read_u8()? {
|
|
28
31
|
tag::OK => Ok(Reply::Ok(reader.remaining().to_vec())),
|
|
29
32
|
// No `finish` here: a Fault owns everything after the tag,
|
|
30
33
|
// including the fields a later contract version appends.
|
|
31
34
|
tag::FAULT => Ok(Reply::Fault(Fault::read(&mut reader)?)),
|
|
35
|
+
tag::BREAK => {
|
|
36
|
+
reader.finish()?;
|
|
37
|
+
Ok(Reply::Break)
|
|
38
|
+
}
|
|
32
39
|
tag::UNSUPPORTED => Ok(Reply::Unsupported(Unsupported::read(&mut reader)?)),
|
|
33
40
|
_ => Err(DecodeError::new(
|
|
34
|
-
"Reply tag must be 0x10 (ok), 0x11 (fault), or 0x15 (unsupported)",
|
|
41
|
+
"Reply tag must be 0x10 (ok), 0x11 (fault), 0x12 (break), or 0x15 (unsupported)",
|
|
35
42
|
)),
|
|
36
43
|
}
|
|
37
44
|
}
|
|
@@ -46,21 +53,23 @@ impl Reply {
|
|
|
46
53
|
writer.u8(tag::FAULT);
|
|
47
54
|
fault.write(&mut writer);
|
|
48
55
|
}
|
|
56
|
+
Reply::Break => {
|
|
57
|
+
writer.u8(tag::BREAK);
|
|
58
|
+
}
|
|
49
59
|
Reply::Unsupported(refusal) => return refusal.encode(),
|
|
50
60
|
}
|
|
51
61
|
writer.into_bytes()
|
|
52
62
|
}
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
/// The answer to one Yield Call.
|
|
56
|
-
/// Reply has no counterpart for, because a block can end its caller.
|
|
65
|
+
/// The answer to one Yield Call.
|
|
57
66
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
58
67
|
pub enum YieldReply {
|
|
59
68
|
/// The block completed; the bytes are its value, codec-encoded.
|
|
60
69
|
Ok(Vec<u8>),
|
|
61
|
-
/// The block ran `break
|
|
62
|
-
///
|
|
63
|
-
Break
|
|
70
|
+
/// The block ran `break` or `return` to a frame past the Service.
|
|
71
|
+
/// The guest keeps the exit, so the Service is only unwound.
|
|
72
|
+
Break,
|
|
64
73
|
/// The block raised, or failed in a way the host re-raises at the
|
|
65
74
|
/// yield site.
|
|
66
75
|
Error(ErrorRecord),
|
|
@@ -71,9 +80,12 @@ pub enum YieldReply {
|
|
|
71
80
|
impl YieldReply {
|
|
72
81
|
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
73
82
|
let mut reader = Reader::new(bytes);
|
|
74
|
-
match reader.
|
|
83
|
+
match reader.read_u8()? {
|
|
75
84
|
tag::OK => Ok(YieldReply::Ok(reader.remaining().to_vec())),
|
|
76
|
-
tag::BREAK =>
|
|
85
|
+
tag::BREAK => {
|
|
86
|
+
reader.finish()?;
|
|
87
|
+
Ok(YieldReply::Break)
|
|
88
|
+
}
|
|
77
89
|
tag::ERROR => {
|
|
78
90
|
let record = ErrorRecord::read(&mut reader)?;
|
|
79
91
|
reader.finish()?;
|
|
@@ -90,8 +102,8 @@ impl YieldReply {
|
|
|
90
102
|
YieldReply::Ok(body) => {
|
|
91
103
|
writer.u8(tag::OK).remainder(body);
|
|
92
104
|
}
|
|
93
|
-
YieldReply::Break
|
|
94
|
-
writer.u8(tag::BREAK)
|
|
105
|
+
YieldReply::Break => {
|
|
106
|
+
writer.u8(tag::BREAK);
|
|
95
107
|
}
|
|
96
108
|
YieldReply::Error(record) => {
|
|
97
109
|
writer.u8(tag::ERROR);
|
|
@@ -116,6 +128,7 @@ mod tests {
|
|
|
116
128
|
super::super::FaultKind::Undefined,
|
|
117
129
|
"no such method",
|
|
118
130
|
)),
|
|
131
|
+
Reply::Break,
|
|
119
132
|
Reply::Unsupported(Unsupported::new("no such operation")),
|
|
120
133
|
] {
|
|
121
134
|
let encoded = reply.encode();
|
|
@@ -142,7 +155,7 @@ mod tests {
|
|
|
142
155
|
#[test]
|
|
143
156
|
fn an_unknown_reply_tag_is_refused() {
|
|
144
157
|
assert!(
|
|
145
|
-
Reply::decode(&[
|
|
158
|
+
Reply::decode(&[0x13, 0]).is_err(),
|
|
146
159
|
"a Reply tag outside its arms must be rejected as a wire violation"
|
|
147
160
|
);
|
|
148
161
|
}
|
|
@@ -161,7 +174,7 @@ mod tests {
|
|
|
161
174
|
fn every_live_yield_arm_round_trips() {
|
|
162
175
|
let arms = [
|
|
163
176
|
YieldReply::Ok(vec![0xa4]),
|
|
164
|
-
YieldReply::Break
|
|
177
|
+
YieldReply::Break,
|
|
165
178
|
YieldReply::Error(ErrorRecord {
|
|
166
179
|
name: "LocalJumpError".into(),
|
|
167
180
|
message: "no block".into(),
|
|
@@ -214,6 +227,11 @@ mod tests {
|
|
|
214
227
|
"an unsupported Reply must encode as tag byte 0x15 followed by kind 0 and the \
|
|
215
228
|
message"
|
|
216
229
|
);
|
|
230
|
+
assert_eq!(
|
|
231
|
+
Reply::Break.encode(),
|
|
232
|
+
vec![0x12],
|
|
233
|
+
"a break Reply must encode as tag byte 0x12 alone, since the guest keeps the exit"
|
|
234
|
+
);
|
|
217
235
|
}
|
|
218
236
|
|
|
219
237
|
// @behavior WE-068
|
|
@@ -242,9 +260,9 @@ mod tests {
|
|
|
242
260
|
"a Yield Reply carrying the block's value must encode as tag byte 0x10"
|
|
243
261
|
);
|
|
244
262
|
assert_eq!(
|
|
245
|
-
YieldReply::Break
|
|
246
|
-
vec![0x12
|
|
247
|
-
"a Yield Reply
|
|
263
|
+
YieldReply::Break.encode(),
|
|
264
|
+
vec![0x12],
|
|
265
|
+
"a Yield Reply for a block exit must encode as tag byte 0x12 alone"
|
|
248
266
|
);
|
|
249
267
|
assert_eq!(
|
|
250
268
|
YieldReply::Unsupported(Unsupported::new("x")).encode(),
|
|
@@ -18,9 +18,9 @@ pub enum Downcall {
|
|
|
18
18
|
Eval(Vec<u8>),
|
|
19
19
|
/// Call the entrypoint the Run names.
|
|
20
20
|
Run(Run),
|
|
21
|
-
/// Re-enter the block
|
|
22
|
-
/// its arguments, codec-encoded.
|
|
23
|
-
Yield
|
|
21
|
+
/// Re-enter the block of the open Call `frame` counts to, from `0`
|
|
22
|
+
/// at the outermost; `args` are its arguments, codec-encoded.
|
|
23
|
+
Yield { frame: u32, args: Vec<u8> },
|
|
24
24
|
/// Compile the Preamble's source Snippets without running them. It
|
|
25
25
|
/// carries no body: what it compiles is the Preamble an invocation
|
|
26
26
|
/// would read.
|
|
@@ -37,7 +37,10 @@ impl Downcall {
|
|
|
37
37
|
Ok(match tag {
|
|
38
38
|
tag::EVAL => Downcall::Eval(body.to_vec()),
|
|
39
39
|
tag::RUN => Downcall::Run(Run::decode(body)?),
|
|
40
|
-
tag::YIELD => Downcall::Yield
|
|
40
|
+
tag::YIELD => Downcall::Yield {
|
|
41
|
+
frame: reader.read_u32()?,
|
|
42
|
+
args: reader.remaining().to_vec(),
|
|
43
|
+
},
|
|
41
44
|
tag::COMPILE if body.is_empty() => Downcall::Compile,
|
|
42
45
|
tag::COMPILE => return Err(DecodeError::new("a compile request carries no body")),
|
|
43
46
|
tag => Downcall::Unknown {
|
|
@@ -52,7 +55,7 @@ impl Downcall {
|
|
|
52
55
|
match self {
|
|
53
56
|
Downcall::Eval(source) => writer.u8(tag::EVAL).remainder(source),
|
|
54
57
|
Downcall::Run(run) => writer.u8(tag::RUN).remainder(&run.encode()),
|
|
55
|
-
Downcall::Yield
|
|
58
|
+
Downcall::Yield { frame, args } => writer.u8(tag::YIELD).u32(*frame).remainder(args),
|
|
56
59
|
Downcall::Compile => writer.u8(tag::COMPILE),
|
|
57
60
|
Downcall::Unknown { tag, body } => writer.u8(tag.get()).remainder(body),
|
|
58
61
|
};
|
|
@@ -112,7 +115,7 @@ impl UnknownTag {
|
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
fn read_tag(reader: &mut Reader<'_>) -> Result<u8, DecodeError> {
|
|
115
|
-
match reader.
|
|
118
|
+
match reader.read_u8()? {
|
|
116
119
|
tag::INVALID => Err(DecodeError::new("request tag 0x00 is invalid")),
|
|
117
120
|
tag => Ok(tag),
|
|
118
121
|
}
|
|
@@ -146,14 +149,18 @@ mod tests {
|
|
|
146
149
|
(
|
|
147
150
|
Downcall::Eval(b"1".to_vec()).encode(),
|
|
148
151
|
Downcall::Run(run).encode(),
|
|
149
|
-
Downcall::Yield
|
|
152
|
+
Downcall::Yield {
|
|
153
|
+
frame: 2,
|
|
154
|
+
args: vec![0x90],
|
|
155
|
+
}
|
|
156
|
+
.encode(),
|
|
150
157
|
Downcall::Compile.encode(),
|
|
151
158
|
Upcall::Call(call).encode(),
|
|
152
159
|
),
|
|
153
160
|
(
|
|
154
161
|
vec![0x01, b'1'],
|
|
155
162
|
vec![0x02, 0, 0, 0, 1, b'M', 0x90],
|
|
156
|
-
vec![0x03, 0x90],
|
|
163
|
+
vec![0x03, 0, 0, 0, 2, 0x90],
|
|
157
164
|
vec![0x08],
|
|
158
165
|
vec![
|
|
159
166
|
0x04, // tag: call
|
|
@@ -177,7 +184,10 @@ mod tests {
|
|
|
177
184
|
entrypoint: "Main".into(),
|
|
178
185
|
payload: vec![0x90],
|
|
179
186
|
}),
|
|
180
|
-
Downcall::Yield
|
|
187
|
+
Downcall::Yield {
|
|
188
|
+
frame: 0,
|
|
189
|
+
args: vec![0x91, 0x01],
|
|
190
|
+
},
|
|
181
191
|
Downcall::Compile,
|
|
182
192
|
Downcall::Unknown {
|
|
183
193
|
tag: UnknownTag::new(0xf0).unwrap(),
|
|
@@ -18,7 +18,7 @@ pub struct Run {
|
|
|
18
18
|
impl Run {
|
|
19
19
|
pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
|
|
20
20
|
let mut reader = Reader::new(bytes);
|
|
21
|
-
let entrypoint = reader.
|
|
21
|
+
let entrypoint = reader.read_text()?.to_owned();
|
|
22
22
|
Ok(Run {
|
|
23
23
|
entrypoint,
|
|
24
24
|
payload: reader.remaining().to_vec(),
|
|
@@ -38,8 +38,8 @@ impl Unsupported {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
pub(crate) fn read(reader: &mut Reader<'_>) -> Result<Self, DecodeError> {
|
|
41
|
-
reader.
|
|
42
|
-
let message = reader.
|
|
41
|
+
reader.read_u8()?;
|
|
42
|
+
let message = reader.read_text()?.to_owned();
|
|
43
43
|
// The last thing its answer carries, so whatever follows belongs
|
|
44
44
|
// to a later version of these fields, as for a Fault.
|
|
45
45
|
reader.skip_unknown_fields();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! The
|
|
1
|
+
//! The Envelope — the fixed-layout outer frame of every host↔guest
|
|
2
2
|
//! message.
|
|
3
3
|
//!
|
|
4
4
|
//! Routing and outcome attribution live here; everything a resolved method
|
|
@@ -50,7 +50,7 @@ pub use unsupported::Unsupported;
|
|
|
50
50
|
|
|
51
51
|
use std::fmt;
|
|
52
52
|
|
|
53
|
-
/// A message that does not conform to the
|
|
53
|
+
/// A message that does not conform to the Envelope layout. Only
|
|
54
54
|
/// decoding produces one — every `encode` is infallible — so the name says
|
|
55
55
|
/// which direction it comes from.
|
|
56
56
|
///
|