File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -713,12 +713,21 @@ private void EnterScriptCallable(
713713 throw new InvalidOperationException ( "callable parameter layout does not match its arity" ) ;
714714 }
715715
716+ // Managed callbacks start after the root frame has halted, so there is
717+ // no active caller frame from which to inherit callable locals. The
718+ // root locals remain the lexical environment for the program and may
719+ // contain closure values referenced by the callback body (for example,
720+ // an event wrapper calling another RSS function). Preserve those
721+ // callable slots just as we do for a nested RSS call.
716722 var inheritedCallables = GetActiveFrameOrDefault ( ) is { } caller
717723 ? _locals . Skip ( caller . LocalBase ) . Take ( caller . LocalCount )
718724 . Select ( ( value , slot ) => ( value , slot ) )
719725 . Where ( item => item . value . Kind == PdVmValueKind . Callable )
720726 . ToArray ( )
721- : [ ] ;
727+ : _locals . Take ( _rootLocalCount )
728+ . Select ( ( value , slot ) => ( value , slot ) )
729+ . Where ( item => item . value . Kind == PdVmValueKind . Callable )
730+ . ToArray ( ) ;
722731 var localBase = _locals . Count ;
723732 _locals . AddRange ( Enumerable . Repeat ( PdVmValue . Null ( ) , prototype . FrameLocalCount ) ) ;
724733 InitializeRootCallableBindings ( localBase , prototype . FrameLocalCount ) ;
Original file line number Diff line number Diff line change @@ -148,6 +148,33 @@ public async Task CallbackQueuePreservesFifoOrderAndBorrowedCaptureState()
148148 Assert . Throws < ObjectDisposedException > ( ( ) => callback . Post ( PdVmValue . FromInt ( 4 ) ) ) ;
149149 }
150150
151+ [ Fact ]
152+ public async Task ManagedCallbackCanCallAClosureKeptInRootLocals ( )
153+ {
154+ using var fixture = new SourceFixture (
155+ "let mut calls: int = 0;\n " +
156+ "fn inner() -> int { calls = calls + 1; calls }\n " +
157+ "pub fn wrapper() -> int { inner() }\n " ) ;
158+ var output = PdVmDotNetSourceCompiler . CompileFile ( fixture . SourcePath , fixture . OutputPath ) ;
159+ var program = Assert . IsAssignableFrom < IPdVmCallableProgram > (
160+ PdVmAssemblyLoader . CreateProgram ( Assembly . Load ( File . ReadAllBytes ( output ) ) ) ) ;
161+ var host = PdVmDefaultHost . CreateConsoleHost ( ) ;
162+ _ = PdVmExecution . Run ( program , host ) ;
163+ var adapter = PdVmCallbackAdapters . Create < PdVmUnit , PdVmValue > (
164+ _ => Array . Empty < PdVmValue > ( ) ,
165+ value => value ,
166+ Array . Empty < PdVmValueType > ( ) ,
167+ PdVmValueType . Int ) ;
168+ using var callback = program . CreateCallback (
169+ "wrapper" ,
170+ adapter ,
171+ host ) ;
172+
173+ var result = await callback . InvokeAsync ( PdVmUnit . Value ) ;
174+
175+ Assert . Equal ( 1 , result . AsInt ( ) ) ;
176+ }
177+
151178 [ Fact ]
152179 public async Task CallbackAdapterSchemaIsValidatedBeforeScriptExecution ( )
153180 {
You can’t perform that action at this time.
0 commit comments