1
1
// Evaluate opcodes
2
2
package vm
3
3
4
+ // FIXME make opcode its own type so can stringer
5
+
4
6
// FIXME use LocalVars instead of storing everything in the Locals dict
5
7
// see frameobject.c dict_to_map and LocalsToFast
6
8
@@ -34,6 +36,7 @@ objects so they can be GCed
34
36
*/
35
37
36
38
import (
39
+ "fmt"
37
40
"runtime/debug"
38
41
"strings"
39
42
@@ -48,9 +51,13 @@ const (
48
51
cannotCatchMsg = "catching '%s' that does not inherit from BaseException is not allowed"
49
52
)
50
53
54
+ const debugging = false
55
+
51
56
// Debug print
52
57
func debugf (format string , a ... interface {}) {
53
- // fmt.Printf(format, a...)
58
+ if debugging {
59
+ fmt .Printf (format , a ... )
60
+ }
54
61
}
55
62
56
63
// Stack operations
@@ -108,43 +115,33 @@ func (vm *Vm) AddTraceback(exc *py.ExceptionInfo) {
108
115
// The exception must be a valid exception instance (eg as returned by
109
116
// py.MakeException)
110
117
//
111
- // It sets vm.exc .* and sets vm.exit to exitException
118
+ // It sets vm.curexc .* and sets vm.exit to exitException
112
119
func (vm * Vm ) SetException (exception py.Object ) {
113
- vm .old_exc = vm .exc
114
- vm .exc .Value = exception
115
- vm .exc .Type = exception .Type ()
116
- vm .exc .Traceback = nil
117
- vm .AddTraceback (& vm .exc )
120
+ vm .curexc .Value = exception
121
+ vm .curexc .Type = exception .Type ()
122
+ vm .curexc .Traceback = nil
123
+ vm .AddTraceback (& vm .curexc )
118
124
vm .exit = exitException
119
125
}
120
126
121
- // Clears the current exception
122
- //
123
- // Doesn't adjust the exit code
124
- func (vm * Vm ) ClearException () {
125
- // Clear the exception
126
- vm .exc .Type = nil
127
- vm .exc .Value = nil
128
- vm .exc .Traceback = nil
129
- }
130
-
131
127
// Check for an exception (panic)
132
128
//
133
129
// Should be called with the result of recover
134
130
func (vm * Vm ) CheckExceptionRecover (r interface {}) {
135
131
// If what was raised was an ExceptionInfo the stuff this into the current vm
136
132
if exc , ok := r .(py.ExceptionInfo ); ok {
137
- vm .old_exc = vm .exc
138
- vm .exc = exc
139
- vm .AddTraceback (& vm .exc )
133
+ vm .curexc = exc
134
+ vm .AddTraceback (& vm .curexc )
140
135
vm .exit = exitException
141
136
debugf ("*** Propagating exception: %s\n " , exc .Error ())
142
137
} else {
143
138
// Coerce whatever was raised into a *Exception
144
139
vm .SetException (py .MakeException (r ))
145
140
debugf ("*** Exception raised %v\n " , r )
146
141
// Dump the goroutine stack
147
- debug .PrintStack ()
142
+ if debugging {
143
+ debug .PrintStack ()
144
+ }
148
145
}
149
146
}
150
147
@@ -153,24 +150,11 @@ func (vm *Vm) CheckExceptionRecover(r interface{}) {
153
150
// Must be called as a defer function
154
151
func (vm * Vm ) CheckException () {
155
152
if r := recover (); r != nil {
153
+ debugf ("*** Panic recovered %v\n " , r )
156
154
vm .CheckExceptionRecover (r )
157
155
}
158
156
}
159
157
160
- // Checks if r is StopIteration and if so returns true
161
- //
162
- // Otherwise deals with the as per vm.CheckException and returns false
163
- func (vm * Vm ) catchStopIteration (r interface {}) bool {
164
- if py .IsException (py .StopIteration , r ) {
165
- // StopIteration or subclass raises
166
- return true
167
- } else {
168
- // Deal with the exception as normal
169
- vm .CheckExceptionRecover (r )
170
- }
171
- return false
172
- }
173
-
174
158
// Illegal instruction
175
159
func do_ILLEGAL (vm * Vm , arg int32 ) {
176
160
defer vm .CheckException ()
@@ -722,9 +706,13 @@ func do_POP_EXCEPT(vm *Vm, arg int32) {
722
706
func do_END_FINALLY (vm * Vm , arg int32 ) {
723
707
defer vm .CheckException ()
724
708
v := vm .POP ()
725
- debugf ("END_FINALLY v=%v\n " , v )
726
- if vInt , ok := v .(py.Int ); ok {
709
+ debugf ("END_FINALLY v=%#v\n " , v )
710
+ if v == py .None {
711
+ // None exception
712
+ debugf (" END_FINALLY: None\n " )
713
+ } else if vInt , ok := v .(py.Int ); ok {
727
714
vm .exit = vmExit (vInt )
715
+ debugf (" END_FINALLY: Int %v\n " , vm .exit )
728
716
switch vm .exit {
729
717
case exitYield :
730
718
panic ("Unexpected exitYield in END_FINALLY" )
@@ -749,15 +737,14 @@ func do_END_FINALLY(vm *Vm, arg int32) {
749
737
} else if py .ExceptionClassCheck (v ) {
750
738
w := vm .POP ()
751
739
u := vm .POP ()
740
+ debugf (" END_FINALLY: Exc %v, Type %v, Traceback %v\n " , v , w , u )
752
741
// FIXME PyErr_Restore(v, w, u)
753
- vm .exc .Type = v .(* py.Type )
754
- vm .exc .Value = w
755
- vm .exc .Traceback = u .(* py.Traceback )
756
- vm .exit = exitReraise
757
- } else if v != py .None {
758
- vm .SetException (py .ExceptionNewf (py .SystemError , "'finally' pops bad exception %#v" , v ))
742
+ vm .curexc .Type , _ = v .(* py.Type )
743
+ vm .curexc .Value = w
744
+ vm .curexc .Traceback , _ = u .(* py.Traceback )
745
+ vm .exit = exitException
759
746
} else {
760
- vm .ClearException ( )
747
+ vm .SetException ( py . ExceptionNewf ( py . SystemError , "'finally' pops bad exception %#v" , v ) )
761
748
}
762
749
debugf ("END_FINALLY: vm.exit = %v\n " , vm .exit )
763
750
}
@@ -1269,8 +1256,11 @@ func (vm *Vm) raise(exc, cause py.Object) {
1269
1256
if ! vm .exc .IsSet () {
1270
1257
vm .SetException (py .ExceptionNewf (py .RuntimeError , "No active exception to reraise" ))
1271
1258
} else {
1259
+ // Resignal the exception
1260
+ vm .curexc = vm .exc
1272
1261
// Signal the existing exception again
1273
- vm .exit = exitReraise
1262
+ vm .exit = exitException
1263
+
1274
1264
}
1275
1265
} else {
1276
1266
// raise <instance>
@@ -1504,9 +1494,9 @@ func (vm *Vm) UnwindExceptHandler(frame *py.Frame, block *py.TryBlock) {
1504
1494
frame .Stack = frame .Stack [:block .Level + 3 ]
1505
1495
}
1506
1496
debugf ("** UnwindExceptHandler stack depth now %v\n " , vm .STACK_LEVEL ())
1507
- vm .exc .Type = vm .POP ().(* py.Type )
1497
+ vm .exc .Type , _ = vm .POP ().(* py.Type )
1508
1498
vm .exc .Value = vm .POP ()
1509
- vm .exc .Traceback = vm .POP ().(* py.Traceback )
1499
+ vm .exc .Traceback , _ = vm .POP ().(* py.Traceback )
1510
1500
debugf ("** UnwindExceptHandler exc = (type: %v, value: %v, traceback: %v)\n " , vm .exc .Type , vm .exc .Value , vm .exc .Traceback )
1511
1501
}
1512
1502
@@ -1565,6 +1555,9 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1565
1555
// }
1566
1556
// }
1567
1557
}
1558
+ if vm .exit == exitYield {
1559
+ goto fast_yield
1560
+ }
1568
1561
1569
1562
// Something exceptional has happened - unwind the block stack
1570
1563
// and find out what
@@ -1574,8 +1567,11 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1574
1567
b := frame .Block
1575
1568
debugf ("*** Unwinding %#v vm %#v\n " , b , vm )
1576
1569
1577
- if vm .exit == exitYield {
1578
- return vm .result , nil
1570
+ if b .Type == SETUP_LOOP && vm .exit == exitContinue {
1571
+ vm .exit = exitNot
1572
+ dest := vm .result .(py.Int )
1573
+ frame .Lasti = int32 (dest )
1574
+ break
1579
1575
}
1580
1576
1581
1577
// Now we have to pop the block.
@@ -1593,18 +1589,25 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1593
1589
frame .Lasti = b .Handler
1594
1590
break
1595
1591
}
1596
- if ( vm .exit == exitException || vm . exit == exitReraise ) && (b .Type == SETUP_EXCEPT || b .Type == SETUP_FINALLY ) {
1592
+ if vm .exit == exitException && (b .Type == SETUP_EXCEPT || b .Type == SETUP_FINALLY ) {
1597
1593
debugf ("*** Exception\n " )
1598
1594
handler := b .Handler
1599
1595
// This invalidates b
1600
1596
frame .PushBlock (EXCEPT_HANDLER , - 1 , vm .STACK_LEVEL ())
1601
1597
vm .PUSH (vm .exc .Traceback )
1602
1598
vm .PUSH (vm .exc .Value )
1603
- vm .PUSH (vm .exc .Type ) // can be nil
1599
+ if vm .exc .Type == nil {
1600
+ vm .PUSH (py .None )
1601
+ } else {
1602
+ vm .PUSH (vm .exc .Type ) // can be nil
1603
+ }
1604
1604
// FIXME PyErr_Fetch(&exc, &val, &tb)
1605
- exc := vm .exc .Type
1606
- val := vm .exc .Value
1607
- tb := vm .exc .Traceback
1605
+ exc := vm .curexc .Type
1606
+ val := vm .curexc .Value
1607
+ tb := vm .curexc .Traceback
1608
+ vm .curexc .Type = nil
1609
+ vm .curexc .Value = nil
1610
+ vm .curexc .Traceback = nil
1608
1611
// Make the raw exception data
1609
1612
// available to the handler,
1610
1613
// so a program can emulate the
@@ -1616,7 +1619,11 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1616
1619
vm .exc .Traceback = tb
1617
1620
vm .PUSH (tb )
1618
1621
vm .PUSH (val )
1619
- vm .PUSH (exc )
1622
+ if exc == nil {
1623
+ vm .PUSH (py .None )
1624
+ } else {
1625
+ vm .PUSH (exc )
1626
+ }
1620
1627
vm .exit = exitNot
1621
1628
frame .Lasti = handler
1622
1629
break
@@ -1632,8 +1639,41 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1632
1639
}
1633
1640
}
1634
1641
}
1635
- if vm .exc .IsSet () {
1636
- return vm .result , vm .exc
1642
+ debugf ("EXIT with %v\n " , vm .exit )
1643
+ if vm .exit != exitReturn {
1644
+ vm .result = nil
1645
+ }
1646
+ if vm .result == nil && ! vm .curexc .IsSet () {
1647
+ panic ("vm: no result or exception" )
1648
+ }
1649
+ if vm .result != nil && vm .curexc .IsSet () {
1650
+ panic ("vm: result and exception" )
1651
+ }
1652
+
1653
+ fast_yield:
1654
+ // FIXME
1655
+ // if (co->co_flags & CO_GENERATOR) {
1656
+ // /* The purpose of this block is to put aside the generator's exception
1657
+ // state and restore that of the calling frame. If the current
1658
+ // exception state is from the caller, we clear the exception values
1659
+ // on the generator frame, so they are not swapped back in latter. The
1660
+ // origin of the current exception state is determined by checking for
1661
+ // except handler blocks, which we must be in iff a new exception
1662
+ // state came into existence in this frame. (An uncaught exception
1663
+ // would have why == WHY_EXCEPTION, and we wouldn't be here). */
1664
+ // int i;
1665
+ // for (i = 0; i < f->f_iblock; i++)
1666
+ // if (f->f_blockstack[i].b_type == EXCEPT_HANDLER)
1667
+ // break;
1668
+ // if (i == f->f_iblock)
1669
+ // /* We did not create this exception. */
1670
+ // restore_and_clear_exc_state(tstate, f);
1671
+ // else
1672
+ // swap_exc_state(tstate, f);
1673
+ // }
1674
+
1675
+ if vm .curexc .IsSet () {
1676
+ return vm .result , vm .curexc
1637
1677
}
1638
1678
return vm .result , nil
1639
1679
}
0 commit comments