1use std::sync::Arc;
2
3use sim_kernel::{
4 CapabilityName, CapabilitySet, Cx, DefaultFactory, Env, Error, Expr, Factory, Registry, Result,
5 Symbol, Value,
6};
7
8use crate::{EvalSite, ServerAddress, ServerFrame};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub enum ShareMode {
14 Share,
16 Child,
18 Isolate,
20 Import(Symbol),
22}
23
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct IsolationPolicy {
28 pub env: ShareMode,
30 pub libs: ShareMode,
32 pub factory: ShareMode,
34 pub registry: ShareMode,
36 pub capabilities: ShareMode,
38}
39
40impl Default for IsolationPolicy {
41 fn default() -> Self {
42 Self {
43 env: ShareMode::Share,
44 libs: ShareMode::Share,
45 factory: ShareMode::Share,
46 registry: ShareMode::Share,
47 capabilities: ShareMode::Share,
48 }
49 }
50}
51
52impl IsolationPolicy {
53 pub fn from_expr(expr: &Expr) -> Result<Self> {
58 match expr {
59 Expr::Nil => Ok(Self::default()),
60 Expr::Symbol(symbol) if symbol.name.as_ref() == "all-shared" => Ok(Self::default()),
61 Expr::List(items) | Expr::Vector(items) => {
62 let mut policy = Self::default();
63 if !items.len().is_multiple_of(2) {
64 return Err(Error::Eval(
65 "isolation policy must use key/value pairs".to_owned(),
66 ));
67 }
68 for pair in items.chunks(2) {
69 let Expr::Symbol(symbol) = &pair[0] else {
70 return Err(Error::TypeMismatch {
71 expected: "keyword symbol",
72 found: "non-symbol",
73 });
74 };
75 let key = symbol
76 .name
77 .strip_prefix(':')
78 .unwrap_or(symbol.name.as_ref());
79 let mode = parse_share_mode(&pair[1])?;
80 match key {
81 "env" => policy.env = mode,
82 "libs" => policy.libs = mode,
83 "factory" => policy.factory = mode,
84 "registry" => policy.registry = mode,
85 "capabilities" => policy.capabilities = mode,
86 other => {
87 return Err(Error::Eval(format!(
88 "unknown isolation policy axis :{other}"
89 )));
90 }
91 }
92 }
93 Ok(policy)
94 }
95 _ => Err(Error::TypeMismatch {
96 expected: "isolation policy expression",
97 found: "non-policy",
98 }),
99 }
100 }
101
102 pub fn as_value(&self, cx: &mut Cx) -> Result<Value> {
104 let env = share_mode_value(cx, &self.env)?;
105 let libs = share_mode_value(cx, &self.libs)?;
106 let factory = share_mode_value(cx, &self.factory)?;
107 let registry = share_mode_value(cx, &self.registry)?;
108 let capabilities = share_mode_value(cx, &self.capabilities)?;
109 cx.factory().table(vec![
110 (Symbol::new("env"), env),
111 (Symbol::new("libs"), libs),
112 (Symbol::new("factory"), factory),
113 (Symbol::new("registry"), registry),
114 (Symbol::new("capabilities"), capabilities),
115 ])
116 }
117
118 pub fn apply<T>(&self, cx: &mut Cx, f: impl FnOnce(&mut Cx) -> Result<T>) -> Result<T> {
123 let env = derive_env(cx, &self.env)?;
124 let capabilities = derive_capabilities(cx, &self.capabilities)?;
125 let factory = derive_factory(cx, &self.factory);
126 let registry = derive_registry(cx, &self.registry, &self.libs)?;
127 cx.with_registry(registry, |cx| {
128 cx.with_factory(factory, |cx| {
129 cx.with_capabilities(capabilities, |cx| cx.with_env(env, f))
130 })
131 })
132 }
133}
134
135fn parse_share_mode(expr: &Expr) -> Result<ShareMode> {
136 match expr {
137 Expr::Symbol(symbol) => match symbol.name.as_ref() {
138 "share" => Ok(ShareMode::Share),
139 "child" => Ok(ShareMode::Child),
140 "isolate" => Ok(ShareMode::Isolate),
141 other => Err(Error::Eval(format!("unsupported share mode {other}"))),
142 },
143 Expr::List(items) | Expr::Vector(items) => match items.as_slice() {
144 [Expr::Symbol(head), value] if head.name.as_ref() == "import" => match value {
145 Expr::Symbol(symbol) => Ok(ShareMode::Import(symbol.clone())),
146 _ => Err(Error::TypeMismatch {
147 expected: "import symbol",
148 found: "non-symbol",
149 }),
150 },
151 _ => Err(Error::Eval("unsupported share mode form".to_owned())),
152 },
153 _ => Err(Error::TypeMismatch {
154 expected: "share mode",
155 found: "non-share-mode",
156 }),
157 }
158}
159
160fn share_mode_value(cx: &mut Cx, mode: &ShareMode) -> Result<Value> {
161 match mode {
162 ShareMode::Share => cx.factory().symbol(Symbol::new("share")),
163 ShareMode::Child => cx.factory().symbol(Symbol::new("child")),
164 ShareMode::Isolate => cx.factory().symbol(Symbol::new("isolate")),
165 ShareMode::Import(symbol) => cx.factory().table(vec![
166 (
167 Symbol::new("kind"),
168 cx.factory().symbol(Symbol::new("import"))?,
169 ),
170 (Symbol::new("value"), cx.factory().symbol(symbol.clone())?),
171 ]),
172 }
173}
174
175fn derive_env(cx: &mut Cx, mode: &ShareMode) -> Result<Env> {
176 match mode {
177 ShareMode::Share => Ok(cx.env().clone()),
178 ShareMode::Child => Ok(Env::child(Arc::new(cx.env().clone()))),
179 ShareMode::Isolate => Ok(Env::default()),
180 ShareMode::Import(symbol) => env_from_snapshot_expr(&snapshot_expr(cx, symbol)?, cx),
181 }
182}
183
184fn derive_capabilities(cx: &mut Cx, mode: &ShareMode) -> Result<CapabilitySet> {
185 match mode {
186 ShareMode::Share | ShareMode::Child => Ok(cx.capabilities().clone()),
187 ShareMode::Isolate => Ok(CapabilitySet::default()),
188 ShareMode::Import(symbol) => capabilities_from_snapshot_expr(&snapshot_expr(cx, symbol)?),
189 }
190}
191
192fn derive_factory(cx: &Cx, mode: &ShareMode) -> Arc<dyn Factory> {
193 match mode {
194 ShareMode::Share | ShareMode::Child => cx.factory_ref(),
195 ShareMode::Isolate | ShareMode::Import(_) => Arc::new(DefaultFactory),
196 }
197}
198
199fn derive_registry(
200 cx: &mut Cx,
201 registry_mode: &ShareMode,
202 libs_mode: &ShareMode,
203) -> Result<Registry> {
204 let base = match registry_mode {
205 ShareMode::Share | ShareMode::Child => cx.registry().clone(),
206 ShareMode::Isolate => Registry::default(),
207 ShareMode::Import(symbol) => registry_from_snapshot_expr(&snapshot_expr(cx, symbol)?, cx)?,
208 };
209 apply_libs_mode(cx, base, libs_mode)
210}
211
212fn apply_libs_mode(cx: &mut Cx, registry: Registry, mode: &ShareMode) -> Result<Registry> {
213 match mode {
214 ShareMode::Share | ShareMode::Child => Ok(registry),
215 ShareMode::Isolate => Ok(Registry::default()),
216 ShareMode::Import(symbol) => registry_from_snapshot_expr(&snapshot_expr(cx, symbol)?, cx),
217 }
218}
219
220fn snapshot_expr(cx: &mut Cx, symbol: &Symbol) -> Result<Expr> {
221 let value = cx
222 .registry()
223 .value_by_symbol(symbol)
224 .cloned()
225 .ok_or_else(|| Error::UnknownSymbol {
226 symbol: symbol.clone(),
227 })?;
228 value.object().as_expr(cx)
229}
230
231fn env_from_snapshot_expr(expr: &Expr, cx: &mut Cx) -> Result<Env> {
232 let Expr::Map(entries) = expr else {
233 return Err(Error::TypeMismatch {
234 expected: "env snapshot table",
235 found: "non-table",
236 });
237 };
238 let mut env = Env::default();
239 for (key, value) in entries {
240 let Expr::Symbol(symbol) = key else {
241 return Err(Error::TypeMismatch {
242 expected: "symbol table key",
243 found: "non-symbol",
244 });
245 };
246 env.define(symbol.clone(), expr_to_value(cx, value)?);
247 }
248 Ok(env)
249}
250
251fn capabilities_from_snapshot_expr(expr: &Expr) -> Result<CapabilitySet> {
252 let mut capabilities = CapabilitySet::new();
253 match expr {
254 Expr::Nil => {}
255 Expr::Symbol(symbol) => {
256 capabilities.insert(CapabilityName::new(symbol.to_string()));
257 }
258 Expr::String(text) => {
259 capabilities.insert(CapabilityName::new(text.clone()));
260 }
261 Expr::List(items) | Expr::Vector(items) => {
262 for item in items {
263 match item {
264 Expr::Symbol(symbol) => {
265 capabilities.insert(CapabilityName::new(symbol.to_string()));
266 }
267 Expr::String(text) => {
268 capabilities.insert(CapabilityName::new(text.clone()));
269 }
270 _ => {
271 return Err(Error::TypeMismatch {
272 expected: "capability symbol or string",
273 found: "non-capability",
274 });
275 }
276 }
277 }
278 }
279 _ => {
280 return Err(Error::TypeMismatch {
281 expected: "capability list",
282 found: "non-list",
283 });
284 }
285 }
286 Ok(capabilities)
287}
288
289fn registry_from_snapshot_expr(expr: &Expr, cx: &mut Cx) -> Result<Registry> {
290 let libs = match expr {
291 Expr::Nil => Vec::new(),
292 Expr::Symbol(symbol) => vec![symbol.clone()],
293 Expr::List(items) | Expr::Vector(items) => items
294 .iter()
295 .map(|item| match item {
296 Expr::Symbol(symbol) => Ok(symbol.clone()),
297 _ => Err(Error::TypeMismatch {
298 expected: "library symbol",
299 found: "non-symbol",
300 }),
301 })
302 .collect::<Result<Vec<_>>>()?,
303 _ => {
304 return Err(Error::TypeMismatch {
305 expected: "registry snapshot symbol list",
306 found: "non-list",
307 });
308 }
309 };
310 Ok(cx.registry().subset_for_libs(&libs))
311}
312
313fn expr_to_value(cx: &mut Cx, expr: &Expr) -> Result<Value> {
314 match expr {
315 Expr::Nil => cx.factory().nil(),
316 Expr::Bool(value) => cx.factory().bool(*value),
317 Expr::Number(number) => cx
318 .factory()
319 .number_literal(number.domain.clone(), number.canonical.clone()),
320 Expr::Symbol(symbol) => cx.factory().symbol(symbol.clone()),
321 Expr::String(text) => cx.factory().string(text.clone()),
322 Expr::Bytes(bytes) => cx.factory().bytes(bytes.clone()),
323 Expr::List(items) | Expr::Vector(items) => {
324 let values = items
325 .iter()
326 .map(|item| expr_to_value(cx, item))
327 .collect::<Result<Vec<_>>>()?;
328 cx.factory().list(values)
329 }
330 Expr::Map(entries) => {
331 let values = entries
332 .iter()
333 .map(|(key, value)| {
334 let Expr::Symbol(symbol) = key else {
335 return Err(Error::TypeMismatch {
336 expected: "symbol table key",
337 found: "non-symbol",
338 });
339 };
340 Ok((symbol.clone(), expr_to_value(cx, value)?))
341 })
342 .collect::<Result<Vec<_>>>()?;
343 cx.factory().table(values)
344 }
345 _ => cx.factory().expr(expr.clone()),
346 }
347}
348
349#[derive(Clone)]
350pub(crate) struct IsolatedEvalSite {
351 inner: Arc<dyn EvalSite>,
352 policy: IsolationPolicy,
353}
354
355impl IsolatedEvalSite {
356 pub(crate) fn wrap(inner: Arc<dyn EvalSite>, policy: IsolationPolicy) -> Arc<dyn EvalSite> {
357 Arc::new(Self { inner, policy })
358 }
359
360 pub(crate) fn inner(&self) -> &Arc<dyn EvalSite> {
361 &self.inner
362 }
363}
364
365impl EvalSite for IsolatedEvalSite {
366 fn site_kind(&self) -> &'static str {
367 self.inner.site_kind()
368 }
369
370 fn address(&self) -> &ServerAddress {
371 self.inner.address()
372 }
373
374 fn codecs(&self) -> &[Symbol] {
375 self.inner.codecs()
376 }
377
378 fn answer(&self, cx: &mut Cx, frame: ServerFrame) -> Result<ServerFrame> {
379 self.policy.apply(cx, |cx| self.inner.answer(cx, frame))
380 }
381
382 fn stream(
383 &self,
384 cx: &mut Cx,
385 frame: ServerFrame,
386 sink: &mut dyn crate::StreamSink,
387 ) -> Result<()> {
388 self.policy
389 .apply(cx, |cx| self.inner.stream(cx, frame, sink))
390 }
391
392 fn as_eval_fabric(&self) -> Option<&dyn sim_kernel::EvalFabric> {
393 self.inner.as_eval_fabric()
394 }
395
396 fn as_any(&self) -> &dyn std::any::Any {
397 self
398 }
399}