kobako 0.14.0 → 0.16.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 +67 -0
- data/Cargo.lock +3 -3
- data/README.md +104 -29
- data/ROADMAP.md +2 -3
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +14 -0
- data/crates/kobako-runtime/Cargo.toml +1 -1
- data/crates/kobako-runtime/README.md +1 -1
- data/crates/kobako-runtime/src/snapshot.rs +7 -3
- data/crates/kobako-wasmtime/CHANGELOG.md +14 -0
- data/crates/kobako-wasmtime/Cargo.toml +2 -2
- data/crates/kobako-wasmtime/README.md +1 -1
- data/crates/kobako-wasmtime/src/capture.rs +17 -13
- data/crates/kobako-wasmtime/src/driver.rs +6 -7
- data/crates/kobako-wasmtime/src/frames.rs +2 -9
- data/crates/kobako-wasmtime/src/guest_mem.rs +8 -1
- data/crates/kobako-wasmtime/src/trap.rs +30 -57
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/runtime/errors.rs +9 -39
- data/ext/kobako/src/runtime.rs +5 -20
- data/lib/kobako/catalog/extensions.rb +114 -0
- data/lib/kobako/catalog/handles.rb +1 -1
- data/lib/kobako/catalog/services.rb +135 -0
- data/lib/kobako/catalog/snippets.rb +1 -1
- data/lib/kobako/catalog.rb +7 -5
- data/lib/kobako/codec/decoder.rb +3 -3
- data/lib/kobako/codec/encoder.rb +4 -4
- data/lib/kobako/codec/ext_types.rb +169 -0
- data/lib/kobako/codec/state.rb +98 -0
- data/lib/kobako/codec/utils.rb +2 -2
- data/lib/kobako/codec.rb +23 -6
- data/lib/kobako/extension.rb +47 -0
- data/lib/kobako/fault.rb +1 -1
- data/lib/kobako/handle.rb +1 -1
- data/lib/kobako/outcome.rb +13 -7
- data/lib/kobako/pool.rb +1 -1
- data/lib/kobako/sandbox.rb +47 -27
- data/lib/kobako/transport/dispatcher.rb +25 -18
- data/lib/kobako/transport/request.rb +14 -9
- data/lib/kobako/transport/response.rb +9 -2
- data/lib/kobako/transport/yield.rb +4 -1
- data/lib/kobako/transport/yielder.rb +14 -6
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/sig/kobako/catalog/extensions.rbs +25 -0
- data/sig/kobako/catalog/services.rbs +27 -0
- data/sig/kobako/codec/ext_types.rbs +31 -0
- data/sig/kobako/codec/state.rbs +20 -0
- data/sig/kobako/codec.rbs +3 -0
- data/sig/kobako/extension.rbs +20 -0
- data/sig/kobako/sandbox.rbs +3 -1
- data/sig/kobako/transport/dispatcher.rbs +4 -4
- data/sig/kobako/transport/yielder.rbs +1 -1
- data/sig/kobako/transport.rbs +2 -2
- metadata +11 -7
- data/lib/kobako/catalog/namespaces.rb +0 -115
- data/lib/kobako/codec/factory.rb +0 -187
- data/lib/kobako/namespace.rb +0 -78
- data/sig/kobako/catalog/namespaces.rbs +0 -17
- data/sig/kobako/codec/factory.rbs +0 -34
- data/sig/kobako/namespace.rbs +0 -21
data/lib/kobako/codec/factory.rb
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "singleton"
|
|
4
|
-
require "forwardable"
|
|
5
|
-
require "msgpack"
|
|
6
|
-
|
|
7
|
-
require_relative "error"
|
|
8
|
-
require_relative "utils"
|
|
9
|
-
require_relative "../handle"
|
|
10
|
-
require_relative "../fault"
|
|
11
|
-
|
|
12
|
-
module Kobako
|
|
13
|
-
module Codec
|
|
14
|
-
# Cached +MessagePack::Factory+ that owns the kobako wire ext-type
|
|
15
|
-
# registration ({docs/wire-codec.md}[link:../../../docs/wire-codec.md]
|
|
16
|
-
# § Ext Types).
|
|
17
|
-
#
|
|
18
|
-
# The factory is the single place in the host gem that touches the
|
|
19
|
-
# msgpack API — both Encoder and Decoder delegate through it, so
|
|
20
|
-
# the three kobako ext codes (0x00 Symbol, 0x01 Capability Handle,
|
|
21
|
-
# 0x02 Exception envelope) are configured exactly once at first use.
|
|
22
|
-
#
|
|
23
|
-
# Lifecycle is owned by +Singleton+ from the Ruby standard library:
|
|
24
|
-
# +Factory.instance+ is lazy, thread-safe, and process-wide. Class-level
|
|
25
|
-
# +Factory.dump+ / +Factory.load+ shortcuts are exposed via
|
|
26
|
-
# +SingleForwardable+ so callers do not have to spell the +.instance+
|
|
27
|
-
# hop at every call site; the instance-level +#dump+ / +#load+ are in
|
|
28
|
-
# turn delegated to the wrapped +MessagePack::Factory+ via +Forwardable+.
|
|
29
|
-
class Factory
|
|
30
|
-
include Singleton
|
|
31
|
-
extend Forwardable
|
|
32
|
-
extend SingleForwardable
|
|
33
|
-
|
|
34
|
-
# MessagePack ext type code reserved for Symbol
|
|
35
|
-
# ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
|
|
36
|
-
# → ext 0x00). Class-private — mirrors +codec::EXT_SYMBOL+ on the
|
|
37
|
-
# Rust side.
|
|
38
|
-
EXT_SYMBOL = 0x00
|
|
39
|
-
# MessagePack ext type code reserved for Capability Handle
|
|
40
|
-
# ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
|
|
41
|
-
# → ext 0x01). Class-private — mirrors +codec::EXT_HANDLE+ on the
|
|
42
|
-
# Rust side.
|
|
43
|
-
EXT_HANDLE = 0x01
|
|
44
|
-
# MessagePack ext type code reserved for Exception envelope
|
|
45
|
-
# ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
|
|
46
|
-
# → ext 0x02). Class-private — mirrors +codec::EXT_ERRENV+ on the
|
|
47
|
-
# Rust side.
|
|
48
|
-
EXT_ERRENV = 0x02
|
|
49
|
-
private_constant :EXT_SYMBOL, :EXT_HANDLE, :EXT_ERRENV
|
|
50
|
-
|
|
51
|
-
# An ext 0x02 (Fault) envelope nests through its +details+ field, and
|
|
52
|
-
# each level re-enters the codec with a fresh +MessagePack+ unpacker
|
|
53
|
-
# whose built-in stack guard resets — so ext-envelope depth is tracked
|
|
54
|
-
# here instead. The cap matches the wire's overall nesting bound and
|
|
55
|
-
# keeps a nested chain from exhausting the native stack: an over-deep
|
|
56
|
-
# chain fails as a clean wire error, never a stack-level trap.
|
|
57
|
-
MAX_EXT_DEPTH = 128
|
|
58
|
-
EXT_DEPTH_KEY = :__kobako_codec_ext_depth__
|
|
59
|
-
private_constant :MAX_EXT_DEPTH, :EXT_DEPTH_KEY
|
|
60
|
-
|
|
61
|
-
# Instance-level pass-through onto the wrapped +MessagePack::Factory+.
|
|
62
|
-
# Spelled +def_instance_delegators+ rather than +def_delegators+ because
|
|
63
|
-
# the class also extends +SingleForwardable+ (see the +extend+ block
|
|
64
|
-
# above), which defines its own +def_delegators+ that shadows
|
|
65
|
-
# +Forwardable+'s — the unambiguous forms keep both delegation tiers
|
|
66
|
-
# wired to the right scope.
|
|
67
|
-
def_instance_delegators :@factory, :dump, :load
|
|
68
|
-
|
|
69
|
-
# Class-level shortcuts so callers can write +Factory.dump(v)+ instead
|
|
70
|
-
# of +Factory.instance.dump(v)+; both resolve to the same singleton.
|
|
71
|
-
def_single_delegators :instance, :dump, :load
|
|
72
|
-
|
|
73
|
-
def initialize
|
|
74
|
-
@factory = MessagePack::Factory.new
|
|
75
|
-
register_symbol
|
|
76
|
-
register_handle
|
|
77
|
-
register_fault
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
private
|
|
81
|
-
|
|
82
|
-
def register_symbol
|
|
83
|
-
@factory.register_type(
|
|
84
|
-
EXT_SYMBOL, Symbol,
|
|
85
|
-
packer: method(:pack_symbol),
|
|
86
|
-
unpacker: method(:unpack_symbol)
|
|
87
|
-
)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Symbol-to-name packer for the ext-0x00 registration.
|
|
91
|
-
def pack_symbol(symbol)
|
|
92
|
-
symbol.name
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Validate the ext-0x00 payload as UTF-8 and intern. Raises
|
|
96
|
-
# InvalidEncoding on invalid bytes — SPEC forbids the
|
|
97
|
-
# binary-encoding fallback that msgpack-gem's default unpacker
|
|
98
|
-
# would otherwise apply. The re-tag step lives here because the
|
|
99
|
-
# msgpack ext-type unpacker hands us binary bytes; the assertion
|
|
100
|
-
# itself is shared with Decoder via Utils.assert_utf8!. The
|
|
101
|
-
# +"Symbol"+ label keeps the error message in Ruby vocabulary
|
|
102
|
-
# rather than wire-ext-code vocabulary.
|
|
103
|
-
def unpack_symbol(payload)
|
|
104
|
-
name = payload.b.force_encoding(Encoding::UTF_8)
|
|
105
|
-
Utils.assert_utf8!(name, "Symbol payload")
|
|
106
|
-
name.to_sym
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def register_handle
|
|
110
|
-
@factory.register_type(
|
|
111
|
-
EXT_HANDLE, Kobako::Handle,
|
|
112
|
-
packer: ->(handle) { [handle.id].pack("N") },
|
|
113
|
-
unpacker: ->(payload) { unpack_handle(payload) }
|
|
114
|
-
)
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def register_fault
|
|
118
|
-
@factory.register_type(
|
|
119
|
-
EXT_ERRENV, Kobako::Fault,
|
|
120
|
-
packer: ->(fault) { pack_fault(fault) },
|
|
121
|
-
unpacker: ->(payload) { unpack_fault(payload) }
|
|
122
|
-
)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Peel off the fixext-4 frame, hand the bytes to the
|
|
126
|
-
# Host-Gem-internal +Kobako::Handle.restore+ factory, and
|
|
127
|
-
# translate the +ArgumentError+ raised by Handle's invariants
|
|
128
|
-
# into a wire-layer +InvalidType+ via Codec::Utils.with_boundary.
|
|
129
|
-
# The Value Object owns the id-range contract; this method only
|
|
130
|
-
# owns the frame shape.
|
|
131
|
-
def unpack_handle(payload)
|
|
132
|
-
bytes = payload.b
|
|
133
|
-
raise InvalidType, "Handle payload must be 4 bytes, got #{bytes.bytesize}" unless bytes.bytesize == 4
|
|
134
|
-
|
|
135
|
-
id = bytes.unpack1("N") # : Integer
|
|
136
|
-
Codec::Utils.with_boundary { Kobako::Handle.restore(id) }
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Encode the inner ext-0x02 map via Encoder (not +factory.dump+) so
|
|
140
|
-
# the embedded payload flows through the same boundary as a top-level
|
|
141
|
-
# encode — nested kobako values (Handle, nested Fault) reach the
|
|
142
|
-
# registered ext-type packers via the cached singleton. A +details+
|
|
143
|
-
# chain nested past MAX_EXT_DEPTH has no wire representation and
|
|
144
|
-
# surfaces as +UnsupportedType+.
|
|
145
|
-
def pack_fault(fault)
|
|
146
|
-
within_ext_frame(UnsupportedType) do
|
|
147
|
-
Encoder.encode("type" => fault.type, "message" => fault.message, "details" => fault.details)
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Peel the embedded msgpack map and hand it to +Kobako::Fault.new+
|
|
152
|
-
# inside Decoder.decode's block form, so the value-object's
|
|
153
|
-
# +ArgumentError+ invariants surface as +InvalidType+ through the
|
|
154
|
-
# decoder boundary. Inner decode goes through Decoder (not
|
|
155
|
-
# +factory.load+) so the embedded +str+ payloads flow through the
|
|
156
|
-
# same UTF-8 validation as a top-level decode. A nested ext 0x02 in
|
|
157
|
-
# +details+ re-enters this method, so #within_ext_frame bounds the
|
|
158
|
-
# chain depth to keep it from exhausting the native stack.
|
|
159
|
-
def unpack_fault(payload)
|
|
160
|
-
within_ext_frame(InvalidType) do
|
|
161
|
-
Decoder.decode(payload) do |map|
|
|
162
|
-
raise InvalidType, "Fault payload must be a map" unless map.is_a?(Hash)
|
|
163
|
-
|
|
164
|
-
Kobako::Fault.new(type: map["type"], message: map["message"], details: map["details"])
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Track ext-envelope re-entry depth and refuse a chain past
|
|
170
|
-
# MAX_EXT_DEPTH, raising +over_limit+ so the failure lands in the
|
|
171
|
-
# caller's existing wire-error class. The counter is thread-scoped and
|
|
172
|
-
# balanced by the +ensure+, so a raise mid-chain still unwinds it to
|
|
173
|
-
# its entry value.
|
|
174
|
-
def within_ext_frame(over_limit)
|
|
175
|
-
depth = (Thread.current[EXT_DEPTH_KEY] || 0) + 1
|
|
176
|
-
raise over_limit, "ext envelope nesting exceeds #{MAX_EXT_DEPTH} levels" if depth > MAX_EXT_DEPTH
|
|
177
|
-
|
|
178
|
-
Thread.current[EXT_DEPTH_KEY] = depth
|
|
179
|
-
begin
|
|
180
|
-
yield
|
|
181
|
-
ensure
|
|
182
|
-
Thread.current[EXT_DEPTH_KEY] = depth - 1
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
data/lib/kobako/namespace.rb
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kobako
|
|
4
|
-
# A named grouping of Members for one Sandbox.
|
|
5
|
-
# Returned by +Sandbox#define+. Each instance owns a flat name→object
|
|
6
|
-
# table of Members; member binding is validated against NAME_PATTERN.
|
|
7
|
-
class Namespace
|
|
8
|
-
# Ruby constant-name pattern shared by Namespace and Member names.
|
|
9
|
-
NAME_PATTERN = /\A[A-Z]\w*\z/
|
|
10
|
-
|
|
11
|
-
attr_reader :name
|
|
12
|
-
|
|
13
|
-
# Build a new Namespace. +name+ is an already-validated Namespace
|
|
14
|
-
# name (must satisfy NAME_PATTERN; validation is the caller's
|
|
15
|
-
# responsibility).
|
|
16
|
-
def initialize(name)
|
|
17
|
-
@name = name
|
|
18
|
-
@members = {} # : Hash[String, untyped]
|
|
19
|
-
@sealed = false
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Bind +object+ under +member+ inside this Namespace. +member+ is a
|
|
23
|
-
# constant-form name as a +Symbol+ or +String+. +object+ is any Ruby
|
|
24
|
-
# object that responds to the methods guest code will invoke. Returns
|
|
25
|
-
# +self+ for chaining. Raises +ArgumentError+ when +member+ does not
|
|
26
|
-
# match the constant pattern, when a Member of the same name is
|
|
27
|
-
# already bound, or when the owning Sandbox's first invocation has
|
|
28
|
-
# sealed Service registration.
|
|
29
|
-
def bind(member, object)
|
|
30
|
-
raise ArgumentError, "cannot bind after first Sandbox invocation" if @sealed
|
|
31
|
-
|
|
32
|
-
member_str = validate_member_name!(member)
|
|
33
|
-
raise ArgumentError, "Member #{@name}::#{member_str} is already bound" if @members.key?(member_str)
|
|
34
|
-
|
|
35
|
-
@members[member_str] = object
|
|
36
|
-
self
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Mark this Namespace as sealed. Called by
|
|
40
|
-
# +Kobako::Catalog::Namespaces#seal!+ on the owning Sandbox's first
|
|
41
|
-
# invocation; afterwards #bind raises +ArgumentError+. Idempotent;
|
|
42
|
-
# returns +self+.
|
|
43
|
-
def seal!
|
|
44
|
-
@sealed = true
|
|
45
|
-
self
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Member lookup; raises +KeyError+ when no Member is registered
|
|
49
|
-
# under +member+.
|
|
50
|
-
def fetch(member)
|
|
51
|
-
member_str = member.to_s
|
|
52
|
-
unless @members.key?(member_str)
|
|
53
|
-
raise KeyError,
|
|
54
|
-
"no member named #{member_str.inspect} in namespace #{@name.inspect}"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
@members[member_str]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Structured description for the guest preamble (Frame 1). Returns a
|
|
61
|
-
# two-element array +[name, member_keys]+ suitable for msgpack encoding.
|
|
62
|
-
def to_preamble
|
|
63
|
-
[@name, @members.keys]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
private
|
|
67
|
-
|
|
68
|
-
def validate_member_name!(member)
|
|
69
|
-
member_str = member.to_s
|
|
70
|
-
unless NAME_PATTERN.match?(member_str)
|
|
71
|
-
raise ArgumentError,
|
|
72
|
-
"MemberName must match #{NAME_PATTERN.inspect} (got #{member.inspect})"
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
member_str
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module Catalog
|
|
3
|
-
class Namespaces
|
|
4
|
-
def initialize: (?handler: Kobako::Catalog::Handles) -> void
|
|
5
|
-
|
|
6
|
-
def define: (Symbol | String name) -> Kobako::Namespace
|
|
7
|
-
|
|
8
|
-
def lookup: (String target) -> untyped
|
|
9
|
-
|
|
10
|
-
def encode: () -> String
|
|
11
|
-
|
|
12
|
-
def seal!: () -> self
|
|
13
|
-
|
|
14
|
-
def sealed?: () -> bool
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module Codec
|
|
3
|
-
class Factory
|
|
4
|
-
include Singleton
|
|
5
|
-
extend Forwardable
|
|
6
|
-
extend SingleForwardable
|
|
7
|
-
|
|
8
|
-
EXT_SYMBOL: Integer
|
|
9
|
-
EXT_HANDLE: Integer
|
|
10
|
-
EXT_ERRENV: Integer
|
|
11
|
-
MAX_EXT_DEPTH: Integer
|
|
12
|
-
EXT_DEPTH_KEY: Symbol
|
|
13
|
-
|
|
14
|
-
def dump: (untyped value) -> String
|
|
15
|
-
def load: (String bytes) -> untyped
|
|
16
|
-
|
|
17
|
-
def self.dump: (untyped value) -> String
|
|
18
|
-
def self.load: (String bytes) -> untyped
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def initialize: () -> void
|
|
23
|
-
def register_symbol: () -> void
|
|
24
|
-
def pack_symbol: (Symbol symbol) -> String
|
|
25
|
-
def unpack_symbol: (String payload) -> Symbol
|
|
26
|
-
def register_handle: () -> void
|
|
27
|
-
def register_fault: () -> void
|
|
28
|
-
def unpack_handle: (String payload) -> Kobako::Handle
|
|
29
|
-
def pack_fault: (Kobako::Fault fault) -> String
|
|
30
|
-
def unpack_fault: (String payload) -> Kobako::Fault
|
|
31
|
-
def within_ext_frame: [T] (singleton(Kobako::Codec::Error) over_limit) { () -> T } -> T
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
data/sig/kobako/namespace.rbs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
class Namespace
|
|
3
|
-
NAME_PATTERN: Regexp
|
|
4
|
-
|
|
5
|
-
attr_reader name: String
|
|
6
|
-
|
|
7
|
-
def initialize: (String name) -> void
|
|
8
|
-
|
|
9
|
-
def bind: (Symbol | String member, untyped object) -> self
|
|
10
|
-
|
|
11
|
-
def seal!: () -> self
|
|
12
|
-
|
|
13
|
-
def fetch: (Symbol | String member) -> untyped
|
|
14
|
-
|
|
15
|
-
def to_preamble: () -> [String, Array[String]]
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
def validate_member_name!: (Symbol | String member) -> String
|
|
20
|
-
end
|
|
21
|
-
end
|