Module: Kobako::Sandbox::OutcomeDecoder

Defined in:
lib/kobako/sandbox/outcome_decoder.rb

Overview

Pure-function decoder for the OUTCOME_BUFFER bytes returned by __kobako_run. Maps a tagged msgpack envelope to either the unwrapped mruby return value or a raised three-layer (SPEC.md “Error Scenarios”) exception.

* tag 0x01, decode OK                 → return Result.value
* tag 0x01, decode fails              → SandboxError (E-09)
* tag 0x02, origin="service"          → ServiceError (E-13)
* tag 0x02, origin="sandbox"/missing  → SandboxError (E-04..E-07)
* tag 0x02, decode fails              → SandboxError (E-08)
* unknown tag                         → TrapError    (E-03)

Class Method Summary collapse

Class Method Details

.build_panic_error(panic) ⇒ Object

Map a decoded Panic envelope into the corresponding three-layer Ruby exception. origin == “service” → ServiceError (with the ::Disconnected subclass selected when the panic carries the disconnected sentinel —SPEC “Error Classes”); everything else → SandboxError.



71
72
73
74
75
76
77
78
79
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 71

def build_panic_error(panic)
  panic_target_class(panic).new(
    panic.message,
    origin: panic.origin,
    klass: panic.klass,
    backtrace_lines: panic.backtrace,
    details: panic.details
  )
end

.decode(bytes) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 19

def decode(bytes)
  tag, body = split_outcome_tag(bytes)
  case tag
  when Kobako::Wire::Envelope::OUTCOME_TAG_RESULT
    decode_result(body)
  when Kobako::Wire::Envelope::OUTCOME_TAG_PANIC
    decode_panic(body)
  else
    raise trap_for_tag(tag)
  end
end

.decode_panic(body) ⇒ Object

Decode failure on a known Panic tag is a SandboxError (E-08). Either path raises — on success the decoded Panic is mapped to its three- layer exception via build_panic_error and raised; on wire-decode failure the rescue path raises the wire-violation SandboxError. Symmetric with decode_result — both have signature “decode body and return value, or raise”.



59
60
61
62
63
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 59

def decode_panic(body)
  raise build_panic_error(Kobako::Wire::Envelope.decode_panic(body))
rescue Kobako::Wire::Codec::Error => e
  raise wire_violation_error("panic envelope decode failed: #{e.message}")
end

.decode_result(body) ⇒ Object

Decode failure on a known Result tag is a SandboxError (E-09): the framing was fine, but the wrapped value is unrepresentable.



47
48
49
50
51
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 47

def decode_result(body)
  Kobako::Wire::Envelope.decode_result(body).value
rescue Kobako::Wire::Codec::Error => e
  raise wire_violation_error("result envelope decode failed: #{e.message}")
end

.panic_target_class(panic) ⇒ Object

SPEC “Error Classes”: when origin=“service” and the panic class field names ServiceError::Disconnected, surface that subclass so callers can rescue the disconnected path specifically (E-14).



85
86
87
88
89
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 85

def panic_target_class(panic)
  return SandboxError unless panic.origin == Kobako::Wire::Envelope::Panic::ORIGIN_SERVICE

  panic.klass == "Kobako::ServiceError::Disconnected" ? ServiceError::Disconnected : ServiceError
end

.split_outcome_tag(bytes) ⇒ Object



40
41
42
43
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 40

def split_outcome_tag(bytes)
  bytes = bytes.b
  [bytes.getbyte(0), bytes.byteslice(1, bytes.bytesize - 1)]
end

.trap_for_tag(tag) ⇒ Object

TrapError for unknown or absent tag (SPEC.md ABI Signatures: len=0 and unknown-tag both walk the trap path).



34
35
36
37
38
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 34

def trap_for_tag(tag)
  return TrapError.new("guest exited without writing an outcome (len=0)") if tag.nil?

  TrapError.new(format("unknown outcome tag 0x%<tag>02x", tag: tag))
end

.wire_violation_error(message) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/kobako/sandbox/outcome_decoder.rb', line 91

def wire_violation_error(message)
  SandboxError.new(
    message,
    origin: Kobako::Wire::Envelope::Panic::ORIGIN_SANDBOX,
    klass: "Kobako::WireError"
  )
end