@@ -2,6 +2,8 @@ use crate::error::Error;
22use crate :: error:: Result ;
33use crate :: model:: { Object , ObjectView , ScalarCow , Value , ValueCow , ValueView } ;
44
5+ use super :: Registers ;
6+
57/// Layer variables on top of the existing runtime
68pub struct StackFrame < P , O > {
79 parent : P ,
@@ -89,12 +91,14 @@ impl<P: super::Runtime, O: ObjectView> super::Runtime for StackFrame<P, O> {
8991 }
9092}
9193
92- pub ( crate ) struct GlobalFrame < P > {
94+ /// A stack frame that only provides a sandboxed set of globals
95+ pub struct GlobalFrame < P > {
9396 parent : P ,
9497 data : std:: cell:: RefCell < Object > ,
9598}
9699
97100impl < P : super :: Runtime > GlobalFrame < P > {
101+ /// Override globals for `parent`
98102 pub fn new ( parent : P ) -> Self {
99103 Self {
100104 parent,
@@ -238,3 +242,176 @@ impl<P: super::Runtime> super::Runtime for IndexFrame<P> {
238242 self . parent . registers ( )
239243 }
240244}
245+
246+ /// A [`StackFrame`] where variables are not recursively searched for,
247+ /// However, you can still access the parent's partials.
248+ pub struct SandboxedStackFrame < P , O > {
249+ parent : P ,
250+ name : Option < crate :: model:: KString > ,
251+ data : O ,
252+ registers : Registers ,
253+ }
254+
255+ impl < P : super :: Runtime , O : ObjectView > SandboxedStackFrame < P , O > {
256+ /// Create a new [`SandboxedStackFrame`] from a parent and some data
257+ pub fn new ( parent : P , data : O ) -> Self {
258+ Self {
259+ parent,
260+ name : None ,
261+ data,
262+ registers : Default :: default ( ) ,
263+ }
264+ }
265+
266+ /// Name the current context
267+ pub fn with_name < S : Into < crate :: model:: KString > > ( mut self , name : S ) -> Self {
268+ self . name = Some ( name. into ( ) ) ;
269+ self
270+ }
271+ }
272+
273+ impl < P : super :: Runtime , O : ObjectView > super :: Runtime for SandboxedStackFrame < P , O > {
274+ fn partials ( & self ) -> & dyn super :: PartialStore {
275+ self . parent . partials ( )
276+ }
277+
278+ fn name ( & self ) -> Option < crate :: model:: KStringRef < ' _ > > {
279+ self . name
280+ . as_ref ( )
281+ . map ( |n| n. as_ref ( ) )
282+ . or_else ( || self . parent . name ( ) )
283+ }
284+
285+ fn roots ( & self ) -> std:: collections:: BTreeSet < crate :: model:: KStringCow < ' _ > > {
286+ let mut roots = std:: collections:: BTreeSet :: new ( ) ;
287+ roots. extend ( self . data . keys ( ) ) ;
288+ roots
289+ }
290+
291+ fn try_get ( & self , path : & [ ScalarCow < ' _ > ] ) -> Option < ValueCow < ' _ > > {
292+ let key = path. first ( ) ?;
293+ let key = key. to_kstr ( ) ;
294+ let data = & self . data ;
295+ data. get ( key. as_str ( ) )
296+ . and_then ( |_| crate :: model:: try_find ( data. as_value ( ) , path) )
297+ }
298+
299+ fn get ( & self , path : & [ ScalarCow < ' _ > ] ) -> Result < ValueCow < ' _ > > {
300+ let key = path. first ( ) . ok_or_else ( || {
301+ Error :: with_msg ( "Unknown variable" ) . context ( "requested variable" , "nil" )
302+ } ) ?;
303+ let key = key. to_kstr ( ) ;
304+ let data = & self . data ;
305+ data. get ( key. as_str ( ) )
306+ . and_then ( |_| crate :: model:: try_find ( data. as_value ( ) , path) )
307+ . map ( |v| v. into_owned ( ) . into ( ) )
308+ . ok_or_else ( || Error :: with_msg ( "Unknown variable" ) . context ( "requested variable" , key) )
309+ }
310+
311+ fn set_global (
312+ & self ,
313+ name : crate :: model:: KString ,
314+ val : crate :: model:: Value ,
315+ ) -> Option < crate :: model:: Value > {
316+ self . parent . set_global ( name, val)
317+ }
318+
319+ fn set_index ( & self , name : crate :: model:: KString , val : Value ) -> Option < Value > {
320+ self . parent . set_index ( name, val)
321+ }
322+
323+ fn get_index < ' a > ( & ' a self , name : & str ) -> Option < ValueCow < ' a > > {
324+ self . parent . get_index ( name)
325+ }
326+
327+ fn registers ( & self ) -> & super :: Registers {
328+ & self . registers
329+ }
330+ }
331+
332+ #[ cfg( test) ]
333+ mod tests {
334+ use crate :: { runtime:: RuntimeBuilder , Runtime } ;
335+
336+ use super :: * ;
337+
338+ #[ test]
339+ fn test_opaque_stack_frame_try_get ( ) {
340+ let globals = {
341+ let mut o = Object :: new ( ) ;
342+ o. insert ( "a" . into ( ) , Value :: Scalar ( 1i64 . into ( ) ) ) ;
343+ o
344+ } ;
345+ let runtime = RuntimeBuilder :: new ( ) . set_globals ( & globals) . build ( ) ;
346+ let opaque_stack_frame = SandboxedStackFrame :: new ( & runtime, {
347+ let mut o = Object :: new ( ) ;
348+ o. insert ( "b" . into ( ) , Value :: Scalar ( 2i64 . into ( ) ) ) ;
349+ o
350+ } ) ;
351+
352+ // Testing that you can access variables in the current frame, but not the parent
353+ assert ! ( opaque_stack_frame. try_get( & [ "a" . into( ) ] ) . is_none( ) ) ;
354+ assert ! ( opaque_stack_frame. try_get( & [ "b" . into( ) ] ) . is_some( ) ) ;
355+
356+ let stack_frame = StackFrame :: new ( opaque_stack_frame, {
357+ let mut o = Object :: new ( ) ;
358+ o. insert ( "c" . into ( ) , Value :: Scalar ( 1i64 . into ( ) ) ) ;
359+ o
360+ } ) ;
361+
362+ // Testing that a child of a OpaqueStackFrame can access access OpaqueStackFrame's variables but not the parent
363+ assert ! ( stack_frame. try_get( & [ "a" . into( ) ] ) . is_none( ) ) ;
364+ assert ! ( stack_frame. try_get( & [ "b" . into( ) ] ) . is_some( ) ) ;
365+ assert ! ( stack_frame. try_get( & [ "c" . into( ) ] ) . is_some( ) ) ;
366+ }
367+
368+ #[ test]
369+ fn test_opaque_stack_frame_get ( ) {
370+ let globals = {
371+ let mut o = Object :: new ( ) ;
372+ o. insert ( "a" . into ( ) , Value :: Scalar ( 1i64 . into ( ) ) ) ;
373+ o
374+ } ;
375+ let runtime = RuntimeBuilder :: new ( ) . set_globals ( & globals) . build ( ) ;
376+ let opaque_stack_frame = SandboxedStackFrame :: new ( & runtime, {
377+ let mut o = Object :: new ( ) ;
378+ o. insert ( "b" . into ( ) , Value :: Scalar ( 2i64 . into ( ) ) ) ;
379+ o
380+ } ) ;
381+
382+ // Testing that you can access variables in the current frame, but not the parent
383+ assert ! ( opaque_stack_frame. get( & [ "a" . into( ) ] ) . is_err( ) ) ;
384+ assert ! ( opaque_stack_frame. get( & [ "b" . into( ) ] ) . is_ok( ) ) ;
385+
386+ let stack_frame = StackFrame :: new ( opaque_stack_frame, {
387+ let mut o = Object :: new ( ) ;
388+ o. insert ( "c" . into ( ) , Value :: Scalar ( 1i64 . into ( ) ) ) ;
389+ o
390+ } ) ;
391+
392+ // Testing that a child of a OpaqueStackFrame can access access OpaqueStackFrame's variables but not the parent
393+ assert ! ( stack_frame. get( & [ "a" . into( ) ] ) . is_err( ) ) ;
394+ assert ! ( stack_frame. get( & [ "b" . into( ) ] ) . is_ok( ) ) ;
395+ assert ! ( stack_frame. get( & [ "c" . into( ) ] ) . is_ok( ) ) ;
396+ }
397+
398+ #[ test]
399+ fn test_opaque_stack_frame_roots ( ) {
400+ let globals = {
401+ let mut o = Object :: new ( ) ;
402+ o. insert ( "a" . into ( ) , Value :: Scalar ( 1i64 . into ( ) ) ) ;
403+ o
404+ } ;
405+ let runtime = RuntimeBuilder :: new ( ) . set_globals ( & globals) . build ( ) ;
406+ let opaque_stack_frame = SandboxedStackFrame :: new ( & runtime, {
407+ let mut o = Object :: new ( ) ;
408+ o. insert ( "b" . into ( ) , Value :: Scalar ( 2i64 . into ( ) ) ) ;
409+ o
410+ } ) ;
411+ let roots = opaque_stack_frame. roots ( ) ;
412+
413+ // Testing that the roots are not copied from the parent
414+ assert ! ( !roots. contains( "a" ) ) ;
415+ assert ! ( roots. contains( "b" ) ) ;
416+ }
417+ }
0 commit comments