@@ -55,6 +55,90 @@ impl<'a> VM<'a> {
5555 slots
5656 }
5757
58+ /*
59+ Budget-Checked Jump
60+ Decrements op budget, validates target, returns new ip.
61+ */
62+
63+ #[ inline]
64+ fn checked_jump ( & mut self , target : usize , limit : usize ) -> Result < usize , VmErr > {
65+ if self . budget == 0 { return Err ( cold_budget ( ) ) ; }
66+ self . budget -= 1 ;
67+ if target > limit { return Err ( VmErr :: Runtime ( "jump target out of bounds" . into ( ) ) ) ; }
68+ Ok ( target)
69+ }
70+
71+ /*
72+ Iterator Frame Construction
73+ Converts a heap object to the appropriate IterFrame for ForIter dispatch.
74+ */
75+
76+ fn make_iter_frame ( & mut self , obj : Val ) -> Result < IterFrame , VmErr > {
77+ if !obj. is_heap ( ) { return Err ( VmErr :: Type ( "not iterable" . into ( ) ) ) ; }
78+ Ok ( match self . heap . get ( obj) {
79+ HeapObj :: Range ( s, e, st) => IterFrame :: Range { cur : * s, end : * e, step : * st } ,
80+ HeapObj :: List ( v) => IterFrame :: Seq { items : v. borrow ( ) . clone ( ) , idx : 0 } ,
81+ HeapObj :: Tuple ( v) => IterFrame :: Seq { items : v. clone ( ) , idx : 0 } ,
82+ HeapObj :: Dict ( p) => IterFrame :: Seq { items : p. borrow ( ) . keys ( ) . copied ( ) . collect ( ) , idx : 0 } ,
83+ HeapObj :: Set ( s) => IterFrame :: Seq { items : s. borrow ( ) . clone ( ) , idx : 0 } ,
84+ HeapObj :: Str ( s) => {
85+ let chars: Vec < char > = s. chars ( ) . collect ( ) ;
86+ drop ( s) ;
87+ let mut items = Vec :: with_capacity ( chars. len ( ) ) ;
88+ for c in chars { items. push ( self . heap . alloc ( HeapObj :: Str ( c. to_string ( ) ) ) ?) ; }
89+ IterFrame :: Seq { items, idx : 0 }
90+ }
91+ _ => return Err ( VmErr :: Type ( "not iterable" . into ( ) ) ) ,
92+ } )
93+ }
94+
95+ /*
96+ Sequence Unpack
97+ Destructures list, tuple, or string into exactly `expected` stack values.
98+ */
99+
100+ fn exec_unpack_seq ( & mut self , expected : usize ) -> Result < ( ) , VmErr > {
101+ let obj = self . pop ( ) ?;
102+ if !obj. is_heap ( ) { return Err ( VmErr :: Type ( "cannot unpack non-sequence" . into ( ) ) ) ; }
103+ let items: Vec < Val > = match self . heap . get ( obj) {
104+ HeapObj :: List ( v) => v. borrow ( ) . clone ( ) ,
105+ HeapObj :: Tuple ( v) => v. clone ( ) ,
106+ HeapObj :: Str ( s) => {
107+ let chars: Vec < char > = s. chars ( ) . collect ( ) ;
108+ drop ( s) ;
109+ if chars. len ( ) != expected {
110+ return Err ( VmErr :: Value ( format ! ( "expected {} values to unpack, got {}" , expected, chars. len( ) ) ) ) ;
111+ }
112+ let mut out = Vec :: with_capacity ( expected) ;
113+ for c in chars { out. push ( self . heap . alloc ( HeapObj :: Str ( c. to_string ( ) ) ) ?) ; }
114+ out
115+ }
116+ _ => return Err ( VmErr :: Type ( "unpack" . into ( ) ) ) ,
117+ } ;
118+ if items. len ( ) != expected {
119+ return Err ( VmErr :: Value ( format ! ( "expected {} values to unpack, got {}" , expected, items. len( ) ) ) ) ;
120+ }
121+ for item in items. into_iter ( ) . rev ( ) { self . push ( item) ; }
122+ Ok ( ( ) )
123+ }
124+
125+ /*
126+ SSA Phi Propagation
127+ Merges two SSA branches into target slot and back-propagates through prev_slots chain.
128+ */
129+
130+ fn exec_phi ( op : u16 , rip : usize , phi_map : & [ usize ] , slots : & mut Vec < Option < Val > > , prev_slots : & [ Option < u16 > ] , phi_sources : & [ ( u16 , u16 ) ] ) {
131+ let target = op as usize ;
132+ let ( ia, ib) = phi_sources[ phi_map[ rip] ] ;
133+ let val = slots[ ia as usize ] . or ( slots[ ib as usize ] ) . unwrap_or ( Val :: none ( ) ) ;
134+ slots[ target] = Some ( val) ;
135+ let mut cur = target;
136+ while let Some ( prev) = prev_slots. get ( cur) . and_then ( |p| * p) {
137+ slots[ prev as usize ] = Some ( val) ;
138+ cur = prev as usize ;
139+ }
140+ }
141+
58142 pub fn with_limits ( chunk : & ' a SSAChunk , limits : Limits ) -> Self {
59143 let mut vm = Self {
60144 stack : Vec :: with_capacity ( 256 ) ,
@@ -363,23 +447,9 @@ impl<'a> VM<'a> {
363447
364448 OpCode :: JumpIfFalse => {
365449 let v = self . pop ( ) ?;
366- if !self . truthy ( v) {
367- if self . budget == 0 { return Err ( cold_budget ( ) ) ; }
368- self . budget -= 1 ;
369- let target = op as usize ;
370- if target > chunk. instructions . len ( ) { return Err ( VmErr :: Runtime ( "jump target out of bounds" . into ( ) ) ) ; }
371- ip = target;
372- }
373- }
374- OpCode :: Jump => {
375- if self . budget == 0 { return Err ( cold_budget ( ) ) ; }
376- self . budget -= 1 ;
377- let target = op as usize ;
378- if target > chunk. instructions . len ( ) {
379- return Err ( VmErr :: Runtime ( "jump target out of bounds" . into ( ) ) ) ;
380- }
381- ip = target;
450+ if !self . truthy ( v) { ip = self . checked_jump ( op as usize , n) ?; }
382451 }
452+ OpCode :: Jump => { ip = self . checked_jump ( op as usize , n) ?; }
383453 OpCode :: PopTop => { self . pop ( ) ?; }
384454 OpCode :: Dup2 => {
385455 let b = self . pop ( ) ?;
@@ -405,11 +475,9 @@ impl<'a> VM<'a> {
405475
406476 OpCode :: BuildList => { let v = self . pop_n ( op as usize ) ?; let val = self . heap . alloc ( HeapObj :: List ( Rc :: new ( RefCell :: new ( v) ) ) ) ?; self . push ( val) ; }
407477 OpCode :: BuildTuple => { let v = self . pop_n ( op as usize ) ?; let val = self . heap . alloc ( HeapObj :: Tuple ( v) ) ?; self . push ( val) ; }
408- OpCode :: BuildDict => {
409- let mut pairs: Vec < ( Val , Val ) > = Vec :: with_capacity ( op as usize ) ;
410- for _ in 0 ..op { let v = self . pop ( ) ?; let k = self . pop ( ) ?; pairs. push ( ( k, v) ) ; }
411- pairs. reverse ( ) ;
412- let dm = DictMap :: from_pairs ( pairs) ;
478+ OpCode :: BuildDict => {
479+ let flat = self . pop_n ( op as usize * 2 ) ?;
480+ let dm = DictMap :: from_pairs ( flat. chunks ( 2 ) . map ( |c| ( c[ 0 ] , c[ 1 ] ) ) . collect ( ) ) ;
413481 let val = self . heap . alloc ( HeapObj :: Dict ( Rc :: new ( RefCell :: new ( dm) ) ) ) ?; self . push ( val) ;
414482 }
415483 OpCode :: BuildString => {
@@ -421,25 +489,7 @@ impl<'a> VM<'a> {
421489 OpCode :: BuildSlice => { self . build_slice ( op) ?; }
422490 OpCode :: GetItem => { self . get_item ( ) ?; }
423491 OpCode :: StoreItem => { self . store_item ( ) ?; }
424- OpCode :: UnpackSequence => {
425- let obj = self . pop ( ) ?; let expected = op as usize ;
426- if !obj. is_heap ( ) { return Err ( VmErr :: Type ( "cannot unpack non-sequence" . into ( ) ) ) ; }
427- let items: Vec < Val > = match self . heap . get ( obj) {
428- HeapObj :: List ( v) => v. borrow ( ) . clone ( ) ,
429- HeapObj :: Tuple ( v) => v. clone ( ) ,
430- HeapObj :: Str ( s) => {
431- let chars: Vec < char > = s. chars ( ) . collect ( ) ;
432- if chars. len ( ) != expected { return Err ( VmErr :: Value ( format ! ( "expected {} values to unpack, got {}" , expected, chars. len( ) ) ) ) ; }
433- let chars = chars; drop ( s) ;
434- let mut out = Vec :: with_capacity ( chars. len ( ) ) ;
435- for c in chars { out. push ( self . heap . alloc ( HeapObj :: Str ( c. to_string ( ) ) ) ?) ; }
436- out
437- }
438- _ => return Err ( VmErr :: Type ( "unpack" . into ( ) ) ) ,
439- } ;
440- if items. len ( ) != expected { return Err ( VmErr :: Value ( format ! ( "expected {} values to unpack, got {}" , expected, items. len( ) ) ) ) ; }
441- for item in items. into_iter ( ) . rev ( ) { self . push ( item) ; }
442- }
492+ OpCode :: UnpackSequence => { self . exec_unpack_seq ( op as usize ) ?; }
443493 OpCode :: UnpackEx => { self . unpack_ex ( op) ?; }
444494 OpCode :: FormatValue => {
445495 if op == 1 { self . pop ( ) ?; }
@@ -451,21 +501,7 @@ impl<'a> VM<'a> {
451501
452502 OpCode :: GetIter => {
453503 let obj = self . pop ( ) ?;
454- if !obj. is_heap ( ) { return Err ( VmErr :: Type ( "not iterable" . into ( ) ) ) ; }
455- let frame = match self . heap . get ( obj) {
456- HeapObj :: Range ( s, e, st) => IterFrame :: Range { cur : * s, end : * e, step : * st } ,
457- HeapObj :: List ( v) => IterFrame :: Seq { items : v. borrow ( ) . clone ( ) , idx : 0 } ,
458- HeapObj :: Tuple ( v) => IterFrame :: Seq { items : v. clone ( ) , idx : 0 } ,
459- HeapObj :: Dict ( p) => IterFrame :: Seq { items : p. borrow ( ) . keys ( ) . copied ( ) . collect ( ) , idx : 0 } ,
460- HeapObj :: Set ( s) => IterFrame :: Seq { items : s. borrow ( ) . clone ( ) , idx : 0 } ,
461- HeapObj :: Str ( s) => {
462- let chars: Vec < char > = s. chars ( ) . collect ( ) ; drop ( s) ;
463- let mut items = Vec :: with_capacity ( chars. len ( ) ) ;
464- for c in chars { items. push ( self . heap . alloc ( HeapObj :: Str ( c. to_string ( ) ) ) ?) ; }
465- IterFrame :: Seq { items, idx : 0 }
466- }
467- _ => return Err ( VmErr :: Type ( "not iterable" . into ( ) ) ) ,
468- } ;
504+ let frame = self . make_iter_frame ( obj) ?;
469505 self . iter_stack . push ( frame) ;
470506 }
471507 OpCode :: ForIter => {
@@ -476,26 +512,15 @@ impl<'a> VM<'a> {
476512 Some ( item) => self . push ( item) ,
477513 None => {
478514 self . iter_stack . pop ( ) ;
479- let target = op as usize ;
480- if target > chunk. instructions . len ( ) { return Err ( VmErr :: Runtime ( "for iter target out of bounds" . into ( ) ) ) ; }
481- ip = target;
515+ if op as usize > n { return Err ( VmErr :: Runtime ( "jump target out of bounds" . into ( ) ) ) ; }
516+ ip = op as usize ;
482517 }
483518 }
484519 }
485520
486521 // SSA Phi
487522
488- OpCode :: Phi => {
489- let target = op as usize ;
490- let ( ia, ib) = chunk. phi_sources [ phi_map[ rip] ] ;
491- let val = slots[ ia as usize ] . or ( slots[ ib as usize ] ) . unwrap_or ( Val :: none ( ) ) ;
492- slots[ target] = Some ( val) ;
493- let mut cur = target;
494- while let Some ( prev) = prev_slots. get ( cur) . and_then ( |p| * p) {
495- slots[ prev as usize ] = Some ( val) ;
496- cur = prev as usize ;
497- }
498- }
523+ OpCode :: Phi => { Self :: exec_phi ( op, rip, & phi_map, slots, prev_slots, & chunk. phi_sources ) ; }
499524
500525 // Functions
501526
0 commit comments