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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +103 -0
  4. data/Cargo.lock +117 -140
  5. data/README.md +156 -118
  6. data/ROADMAP.md +23 -22
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +38 -0
  9. data/crates/kobako-runtime/Cargo.toml +2 -2
  10. data/crates/kobako-runtime/README.md +10 -12
  11. data/crates/kobako-runtime/src/error.rs +8 -9
  12. data/crates/kobako-runtime/src/lib.rs +1 -1
  13. data/crates/kobako-runtime/src/profile.rs +1 -1
  14. data/crates/kobako-runtime/src/runtime.rs +31 -17
  15. data/crates/kobako-runtime/src/snapshot.rs +13 -10
  16. data/crates/kobako-runtime/src/yielder.rs +9 -6
  17. data/crates/kobako-transport/CHANGELOG.md +35 -0
  18. data/crates/kobako-transport/Cargo.toml +1 -1
  19. data/crates/kobako-transport/README.md +43 -27
  20. data/crates/kobako-transport/src/abi.rs +11 -14
  21. data/crates/kobako-transport/src/envelope/bytes.rs +18 -6
  22. data/crates/kobako-transport/src/envelope/compile_reply.rs +139 -0
  23. data/crates/kobako-transport/src/envelope/outcome.rs +17 -16
  24. data/crates/kobako-transport/src/envelope/preamble.rs +513 -0
  25. data/crates/kobako-transport/src/envelope/reply.rs +77 -53
  26. data/crates/kobako-transport/src/envelope/request.rs +295 -0
  27. data/crates/kobako-transport/src/envelope/run.rs +1 -2
  28. data/crates/kobako-transport/src/envelope/tag.rs +49 -0
  29. data/crates/kobako-transport/src/envelope/unsupported.rs +105 -0
  30. data/crates/kobako-transport/src/envelope.rs +19 -12
  31. data/crates/kobako-transport/src/lib.rs +0 -2
  32. data/crates/kobako-wasmtime/CHANGELOG.md +51 -0
  33. data/crates/kobako-wasmtime/Cargo.toml +7 -5
  34. data/crates/kobako-wasmtime/README.md +8 -10
  35. data/crates/kobako-wasmtime/src/abi.rs +5 -8
  36. data/crates/kobako-wasmtime/src/ambient.rs +2 -2
  37. data/crates/kobako-wasmtime/src/cache.rs +25 -59
  38. data/crates/kobako-wasmtime/src/capture.rs +4 -11
  39. data/crates/kobako-wasmtime/src/config.rs +11 -14
  40. data/crates/kobako-wasmtime/src/dispatch.rs +70 -80
  41. data/crates/kobako-wasmtime/src/driver.rs +133 -86
  42. data/crates/kobako-wasmtime/src/exports.rs +88 -34
  43. data/crates/kobako-wasmtime/src/guest_mem.rs +145 -138
  44. data/crates/kobako-wasmtime/src/instance_pre.rs +11 -22
  45. data/crates/kobako-wasmtime/src/invocation.rs +55 -372
  46. data/crates/kobako-wasmtime/src/io.rs +286 -0
  47. data/crates/kobako-wasmtime/src/lib.rs +3 -24
  48. data/crates/kobako-wasmtime/src/limiter.rs +253 -0
  49. data/crates/kobako-wasmtime/src/trap.rs +32 -45
  50. data/crates/kobako-wasmtime/tests/preamble_flags.rs +69 -0
  51. data/crates/kobako-wasmtime/tests/snippet_compile.rs +94 -0
  52. data/crates/kobako-wasmtime/tests/support/mod.rs +34 -0
  53. data/crates/kobako-wasmtime/tests/unsupported_call.rs +50 -0
  54. data/data/kobako.wasm +0 -0
  55. data/ext/kobako/Cargo.toml +2 -2
  56. data/ext/kobako/src/runtime/bridge.rs +38 -47
  57. data/ext/kobako/src/runtime/errors.rs +26 -32
  58. data/ext/kobako/src/runtime/gvl.rs +4 -12
  59. data/ext/kobako/src/runtime.rs +169 -212
  60. data/lib/kobako/capture.rb +1 -4
  61. data/lib/kobako/catalog/extensions.rb +21 -59
  62. data/lib/kobako/catalog/flags.rb +41 -0
  63. data/lib/kobako/catalog/handles.rb +7 -54
  64. data/lib/kobako/catalog/services.rb +7 -61
  65. data/lib/kobako/catalog/snippets.rb +42 -66
  66. data/lib/kobako/catalog.rb +2 -10
  67. data/lib/kobako/codec/decoder.rb +9 -28
  68. data/lib/kobako/codec/encoder.rb +3 -22
  69. data/lib/kobako/codec/error.rb +6 -27
  70. data/lib/kobako/codec/ext_types.rb +72 -94
  71. data/lib/kobako/codec/handle_walk.rb +65 -130
  72. data/lib/kobako/codec/nesting.rb +3 -8
  73. data/lib/kobako/codec/state.rb +4 -10
  74. data/lib/kobako/codec/utils.rb +6 -34
  75. data/lib/kobako/codec.rb +3 -22
  76. data/lib/kobako/constant_name.rb +8 -0
  77. data/lib/kobako/context.rb +51 -112
  78. data/lib/kobako/errors.rb +51 -127
  79. data/lib/kobako/execution.rb +16 -21
  80. data/lib/kobako/extension.rb +23 -40
  81. data/lib/kobako/handle.rb +7 -12
  82. data/lib/kobako/outcome.rb +51 -80
  83. data/lib/kobako/payload/arguments.rb +1 -16
  84. data/lib/kobako/payload.rb +4 -12
  85. data/lib/kobako/pool.rb +23 -53
  86. data/lib/kobako/runtime.rb +7 -21
  87. data/lib/kobako/sandbox.rb +96 -133
  88. data/lib/kobako/sandbox_options.rb +38 -84
  89. data/lib/kobako/snippet/binary.rb +1 -16
  90. data/lib/kobako/snippet/source.rb +1 -16
  91. data/lib/kobako/snippet.rb +1 -9
  92. data/lib/kobako/transport/call.rb +1 -14
  93. data/lib/kobako/transport/dispatcher.rb +112 -195
  94. data/lib/kobako/transport/error.rb +6 -20
  95. data/lib/kobako/transport/exposure.rb +5 -10
  96. data/lib/kobako/transport/reflection.rb +39 -50
  97. data/lib/kobako/transport/run.rb +17 -71
  98. data/lib/kobako/transport/yielder.rb +21 -73
  99. data/lib/kobako/transport.rb +3 -15
  100. data/lib/kobako/unresolved.rb +4 -11
  101. data/lib/kobako/usage.rb +2 -6
  102. data/lib/kobako/version.rb +1 -1
  103. data/sig/kobako/catalog/extensions.rbs +2 -2
  104. data/sig/kobako/catalog/flags.rbs +17 -0
  105. data/sig/kobako/catalog/services.rbs +0 -6
  106. data/sig/kobako/catalog/snippets.rbs +4 -4
  107. data/sig/kobako/codec/ext_types.rbs +10 -8
  108. data/sig/kobako/codec/handle_walk.rbs +9 -7
  109. data/sig/kobako/codec/utils.rbs +2 -2
  110. data/sig/kobako/codec.rbs +2 -2
  111. data/sig/kobako/constant_name.rbs +3 -0
  112. data/sig/kobako/context.rbs +2 -3
  113. data/sig/kobako/errors.rbs +6 -0
  114. data/sig/kobako/outcome.rbs +6 -8
  115. data/sig/kobako/runtime.rbs +12 -8
  116. data/sig/kobako/sandbox.rbs +4 -0
  117. data/sig/kobako/sandbox_options.rbs +2 -6
  118. data/sig/kobako/transport/dispatcher.rbs +14 -18
  119. data/sig/kobako/transport/reflection.rbs +5 -3
  120. data/sig/kobako/transport/run.rbs +1 -3
  121. data/sig/kobako/transport/yielder.rbs +3 -3
  122. data/sig/kobako/transport.rbs +7 -6
  123. metadata +16 -4
  124. data/crates/kobako-transport/src/envelope/invocation_frames.rs +0 -241
  125. data/crates/kobako-wasmtime/src/frames.rs +0 -273
  126. 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
- TAG_OK => Ok(Outcome::Ok(reader.remaining().to_vec())),
72
- TAG_PANIC => {
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 0x01 (ok) or 0x02 (panic)",
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(TAG_OK).remainder(value);
93
+ writer.u8(tag::OK).remainder(value);
94
94
  }
95
95
  Outcome::Panic(panic) => {
96
- writer.u8(TAG_PANIC).bytes(panic.origin.name().as_bytes());
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(TAG_PANIC).bytes(b"something-else");
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![0x01, 0x2a],
223
- "an ok Outcome must encode as tag byte 0x01 followed by the value alone"
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
- 0x02, // tag: panic
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 outcome buffer must be rejected as a wire violation"
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(&[0x03, 0x00]).is_err(),
269
- "an Outcome tag that is neither ok nor panic must be rejected as a wire violation"
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
  }