@@ -37,6 +37,7 @@ pub enum VMError {
3737 MissingFunctionName ,
3838 MissingContext ,
3939 InvalidInstruction ( u8 ) ,
40+ MissingStackFrame ,
4041}
4142
4243impl std:: error:: Error for VMError { }
@@ -65,6 +66,7 @@ impl std::fmt::Display for VMError {
6566 VMError :: InvalidInstruction ( instruction) => {
6667 write ! ( f, "Invalid instruction: {}" , instruction)
6768 }
69+ VMError :: MissingStackFrame => write ! ( f, "Missing stack frame" ) ,
6870 }
6971 }
7072}
@@ -114,7 +116,7 @@ const LENGTH_OFFSET: usize = std::mem::size_of::<usize>();
114116
115117pub struct VM {
116118 code : Vec < u8 > ,
117- stack : Vec < StackValue > ,
119+ stack : Vec < Vec < StackValue > > ,
118120 vars : HashMap < String , StackValue > ,
119121 label_jump_map : HashMap < String , usize > ,
120122 label_index_map : HashMap < usize , String > ,
@@ -164,7 +166,7 @@ impl VM {
164166 code,
165167 label_jump_map,
166168 label_index_map,
167- stack : Vec :: new ( ) ,
169+ stack : vec ! [ Vec :: new( ) ] ,
168170 vars : HashMap :: new ( ) ,
169171 ip : 0 ,
170172 print_tx,
@@ -238,6 +240,7 @@ impl VM {
238240
239241 async fn handle_local_call ( & mut self , label : String ) -> Result < ( ) , VMError > {
240242 self . return_addresses . push ( self . ip ) ;
243+ self . stack . push ( Vec :: new ( ) ) ;
241244 self . ip = * self
242245 . label_jump_map
243246 . get ( & label)
@@ -254,31 +257,38 @@ impl VM {
254257 ( start, end, length)
255258 }
256259
260+ fn current_stackframe ( & mut self ) -> Result < & mut Vec < StackValue > , VMError > {
261+ self . stack . last_mut ( ) . ok_or ( VMError :: MissingStackFrame )
262+ }
263+
257264 async fn execute_instruction ( & mut self ) -> Result < ( ) , VMError > {
258265 let instruction = self . code [ self . ip ] ;
259266 match instruction {
260267 PUSH_STRING_CODE => {
261268 let ( _start, end, str_len) = self . extract_length ( ) ;
262269 let str = & self . code [ end..end + str_len] ;
263270 let str = String :: from_utf8 ( str. to_vec ( ) ) . unwrap ( ) ;
264- self . stack . push ( StackValue :: String ( str) ) ;
271+ self . current_stackframe ( ) ? . push ( StackValue :: String ( str) ) ;
265272 self . ip = end + str_len;
266273 }
267274 PUSH_INT_CODE => {
268275 let ( _start, end, int_len) = self . extract_length ( ) ;
269276 let int = & self . code [ end..end + int_len] ;
270277 let int = u64:: from_le_bytes ( int. try_into ( ) . unwrap ( ) ) ;
271- self . stack . push ( StackValue :: Int ( int) ) ;
278+ self . current_stackframe ( ) ? . push ( StackValue :: Int ( int) ) ;
272279 self . ip = end + int_len;
273280 }
274281 POP_CODE => {
275282 self . stack . pop ( ) ;
276283 self . ip += 1 ;
277284 }
278285 DEC_CODE => {
279- let top = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
286+ let top = self
287+ . current_stackframe ( ) ?
288+ . pop ( )
289+ . ok_or ( VMError :: StackUnderflow ) ?;
280290 match top {
281- StackValue :: Int ( n) => self . stack . push ( StackValue :: Int ( n - 1 ) ) ,
291+ StackValue :: Int ( n) => self . current_stackframe ( ) ? . push ( StackValue :: Int ( n - 1 ) ) ,
282292 _ => return Err ( VMError :: InvalidStackValue ) ,
283293 }
284294 self . ip += 1 ;
@@ -287,7 +297,10 @@ impl VM {
287297 let ( _start, end, jump_to_label_len) = self . extract_length ( ) ;
288298 let jump_to_label_bytes = & self . code [ end..end + jump_to_label_len] ;
289299 let jump_to_label = String :: from_utf8 ( jump_to_label_bytes. to_vec ( ) ) . unwrap ( ) ;
290- let top = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
300+ let top = self
301+ . current_stackframe ( ) ?
302+ . pop ( )
303+ . ok_or ( VMError :: StackUnderflow ) ?;
291304 match top {
292305 StackValue :: Int ( n) => {
293306 if n == 0 {
@@ -307,7 +320,10 @@ impl VM {
307320 self . ip = end + label_len;
308321 }
309322 STDOUT_CODE => {
310- let str = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
323+ let str = self
324+ . current_stackframe ( ) ?
325+ . pop ( )
326+ . ok_or ( VMError :: StackUnderflow ) ?;
311327 match str {
312328 StackValue :: String ( s) => self
313329 . print_tx
@@ -323,7 +339,10 @@ impl VM {
323339 self . ip += 1 ;
324340 }
325341 STDERR_CODE => {
326- let top = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
342+ let top = self
343+ . current_stackframe ( ) ?
344+ . pop ( )
345+ . ok_or ( VMError :: StackUnderflow ) ?;
327346 match top {
328347 StackValue :: String ( s) => {
329348 self . print_tx
@@ -364,13 +383,18 @@ impl VM {
364383 let value = self
365384 . vars
366385 . get ( & key)
367- . ok_or ( VMError :: MissingVar ( key. clone ( ) ) ) ?;
368- self . stack . push ( value. clone ( ) ) ;
386+ . ok_or ( VMError :: MissingVar ( key. clone ( ) ) ) ?
387+ . clone ( ) ;
388+ self . current_stackframe ( ) ?. push ( value) ;
369389 self . ip = end + key_len;
370390 }
371391 DUP_CODE => {
372- let top = self . stack . last ( ) . ok_or ( VMError :: StackUnderflow ) ?;
373- self . stack . push ( top. clone ( ) ) ;
392+ let top = self
393+ . current_stackframe ( ) ?
394+ . last ( )
395+ . ok_or ( VMError :: StackUnderflow ) ?
396+ . clone ( ) ;
397+ self . current_stackframe ( ) ?. push ( top) ;
374398 self . ip += 1 ;
375399 }
376400 JUMP_CODE => {
@@ -384,8 +408,14 @@ impl VM {
384408 . to_owned ( ) ;
385409 }
386410 PRINTF_CODE => {
387- let var = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
388- let template = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
411+ let var = self
412+ . current_stackframe ( ) ?
413+ . pop ( )
414+ . ok_or ( VMError :: StackUnderflow ) ?;
415+ let template = self
416+ . current_stackframe ( ) ?
417+ . pop ( )
418+ . ok_or ( VMError :: StackUnderflow ) ?;
389419 let template = match template {
390420 StackValue :: String ( s) => s,
391421 _ => return Err ( VMError :: InvalidStackValue ) ,
@@ -397,70 +427,79 @@ impl VM {
397427 _ => return Err ( VMError :: InvalidStackValue ) ,
398428 } ;
399429 let formatted = template. replace ( "%s" , & var) ;
400- self . stack . push ( StackValue :: String ( formatted) ) ;
430+ self . current_stackframe ( ) ?
431+ . push ( StackValue :: String ( formatted) ) ;
401432 } else if template. contains ( "%d" ) {
402433 let var = match var {
403434 StackValue :: Int ( i) => i,
404435 _ => return Err ( VMError :: InvalidStackValue ) ,
405436 } ;
406437 let formatted = template. replace ( "%d" , & var. to_string ( ) ) ;
407- self . stack . push ( StackValue :: String ( formatted) ) ;
438+ self . current_stackframe ( ) ?
439+ . push ( StackValue :: String ( formatted) ) ;
408440 } else {
409441 return Err ( VMError :: InvalidTemplate ( template. clone ( ) ) ) ;
410442 }
411443 self . ip += 1 ;
412444 }
413445 REMOTE_CALL_CODE => {
414- if let Some ( remote_call_tx) = self . remote_call_tx . as_ref ( ) {
415- let remote_method = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
416- let remote_service = self . stack . pop ( ) . ok_or ( VMError :: StackUnderflow ) ?;
417- let local_function_name = self
418- . find_current_function_name ( )
419- . ok_or ( VMError :: MissingFunctionName ) ?;
420- let mut cx = None ;
421-
422- if let Some ( tracer_provider) = self . tracer . as_ref ( ) {
423- if let Some ( otel_cx) = self . otel_context . as_ref ( ) {
424- let tracer = tracer_provider. tracer ( self . service_name . clone ( ) ) ;
425-
426- let span = tracer
427- . span_builder ( format ! (
428- "{}/{}" ,
429- self . service_name, local_function_name
430- ) )
431- . with_kind ( SpanKind :: Client )
432- . with_attributes ( vec ! [ KeyValue :: new(
433- SERVICE_NAME ,
434- self . service_name. clone( ) ,
435- ) ] )
436- . start ( & tracer) ;
437-
438- cx = Some ( otel_cx. with_span ( span) ) ;
439- let mut metadata = HashMap :: new ( ) ;
440- let propagator = TraceContextPropagator :: new ( ) ;
441- propagator. inject_context ( & cx. clone ( ) . unwrap ( ) , & mut metadata) ;
442- } else {
443- return Err ( VMError :: MissingContext ) ;
444- }
445- }
446-
447- remote_call_tx
448- . send ( ServiceMessage :: Call {
449- to : remote_service. to_string ( ) ,
450- function : remote_method. to_string ( ) ,
451- context : cx. clone ( ) . unwrap_or ( opentelemetry:: Context :: current ( ) ) ,
452- } )
453- . await
454- . map_err ( |e| VMError :: RemoteCallError ( e. to_string ( ) ) ) ?;
446+ let remote_call_tx = self
447+ . remote_call_tx
448+ . as_ref ( )
449+ . ok_or ( VMError :: RemoteCallError (
450+ "Remote call tx not set" . to_string ( ) ,
451+ ) ) ?
452+ . clone ( ) ;
453+
454+ let remote_method = self
455+ . current_stackframe ( ) ?
456+ . pop ( )
457+ . ok_or ( VMError :: StackUnderflow ) ?
458+ . clone ( ) ;
459+ let remote_service = self
460+ . current_stackframe ( ) ?
461+ . pop ( )
462+ . ok_or ( VMError :: StackUnderflow ) ?
463+ . clone ( ) ;
464+ let local_function_name = self
465+ . find_current_function_name ( )
466+ . ok_or ( VMError :: MissingFunctionName ) ?;
467+ let mut cx = None ;
455468
456- if let Some ( cx) = cx {
457- cx. span ( )
458- . set_attributes ( vec ! [ KeyValue :: new( "response" , "OK" ) ] ) ;
469+ if let Some ( tracer_provider) = self . tracer . as_ref ( ) {
470+ if let Some ( otel_cx) = self . otel_context . as_ref ( ) {
471+ let tracer = tracer_provider. tracer ( self . service_name . clone ( ) ) ;
472+
473+ let span = tracer
474+ . span_builder ( format ! ( "{}/{}" , self . service_name, local_function_name) )
475+ . with_kind ( SpanKind :: Client )
476+ . with_attributes ( vec ! [ KeyValue :: new(
477+ SERVICE_NAME ,
478+ self . service_name. clone( ) ,
479+ ) ] )
480+ . start ( & tracer) ;
481+
482+ cx = Some ( otel_cx. with_span ( span) ) ;
483+ let mut metadata = HashMap :: new ( ) ;
484+ let propagator = TraceContextPropagator :: new ( ) ;
485+ propagator. inject_context ( & cx. clone ( ) . unwrap ( ) , & mut metadata) ;
486+ } else {
487+ return Err ( VMError :: MissingContext ) ;
459488 }
460- } else {
461- return Err ( VMError :: RemoteCallError (
462- "Remote call tx not set" . to_string ( ) ,
463- ) ) ;
489+ }
490+
491+ remote_call_tx
492+ . send ( ServiceMessage :: Call {
493+ to : remote_service. to_string ( ) ,
494+ function : remote_method. to_string ( ) ,
495+ context : cx. clone ( ) . unwrap_or ( opentelemetry:: Context :: current ( ) ) ,
496+ } )
497+ . await
498+ . map_err ( |e| VMError :: RemoteCallError ( e. to_string ( ) ) ) ?;
499+
500+ if let Some ( cx) = cx {
501+ cx. span ( )
502+ . set_attributes ( vec ! [ KeyValue :: new( "response" , "OK" ) ] ) ;
464503 }
465504 self . ip += 1 ;
466505 }
@@ -502,6 +541,7 @@ impl VM {
502541 }
503542 RET_CODE => {
504543 self . ip = self . return_addresses . pop ( ) . unwrap ( ) ;
544+ self . stack . pop ( ) ;
505545 }
506546 _ => {
507547 return Err ( VMError :: InvalidInstruction ( instruction) ) ;
@@ -735,7 +775,7 @@ mod tests {
735775 Instruction :: Stdout ,
736776 Instruction :: Stdout ,
737777 ] ;
738- let ( print_tx, mut print_rx) = mpsc:: channel ( 10 ) ;
778+ let ( print_tx, print_rx) = mpsc:: channel ( 10 ) ;
739779 let mut vm = VM :: new ( code. clone ( ) , "test" , print_tx) . with_max_execution_counter ( 5 ) ;
740780 match vm. run ( ) . await {
741781 Ok ( _) => {
@@ -827,7 +867,7 @@ mod tests {
827867 Instruction :: Printf ,
828868 Instruction :: Stdout ,
829869 ] ;
830- let ( print_tx, print_rx ) = mpsc:: channel ( 10 ) ;
870+ let ( print_tx, _print_rx ) = mpsc:: channel ( 10 ) ;
831871 let mut vm = VM :: new ( code. clone ( ) , "test" , print_tx) . with_max_execution_counter ( 4 ) ;
832872 match vm. run ( ) . await {
833873 Ok ( _) => {
@@ -1054,4 +1094,32 @@ mod tests {
10541094 }
10551095 }
10561096 }
1097+
1098+ #[ tokio:: test]
1099+ async fn test_vm_creates_new_stackframe_on_call ( ) {
1100+ let code = vec ! [
1101+ Instruction :: Jump ( "main" . to_string( ) ) ,
1102+ Instruction :: Label ( "start_function" . to_string( ) ) ,
1103+ Instruction :: Stdout ,
1104+ Instruction :: Ret ,
1105+ Instruction :: Label ( "end_function" . to_string( ) ) ,
1106+ Instruction :: Label ( "main" . to_string( ) ) ,
1107+ Instruction :: Push ( StackValue :: String ( "world" . to_string( ) ) ) ,
1108+ Instruction :: Call ( "start_function" . to_string( ) ) ,
1109+ Instruction :: Stdout ,
1110+ ] ;
1111+
1112+ let ( print_tx, print_rx) = mpsc:: channel ( 5 ) ;
1113+ let mut vm = VM :: new ( code. clone ( ) , "test" , print_tx) . with_max_execution_counter ( 15 ) ;
1114+
1115+ match vm. run ( ) . await {
1116+ Ok ( _) => {
1117+ assert ! ( false , "VM should have failed because of missing stackframe" ) ;
1118+ }
1119+ Err ( e) => {
1120+ assert_eq ! ( e, VMError :: StackUnderflow ) ;
1121+ assert_eq ! ( print_rx. len( ) , 0 ) ;
1122+ }
1123+ }
1124+ }
10571125}
0 commit comments