@@ -34,6 +34,14 @@ import (
3434 "runtime/debug"
3535)
3636
37+ const (
38+ nameErrorMsg = "name '%s' is not defined"
39+ globalNameErrorMsg = "global name '%s' is not defined"
40+ unboundLocalErrorMsg = "local variable '%s' referenced before assignment"
41+ unboundFreeErrorMsg = "free variable '%s' referenced before assignment in enclosing scope"
42+ cannotCatchMsg = "catching '%s' that does not inherit from BaseException is not allowed"
43+ )
44+
3745// Stack operations
3846func (vm * Vm ) STACK_LEVEL () int { return len (vm .frame .Stack ) }
3947func (vm * Vm ) EMPTY () bool { return len (vm .frame .Stack ) == 0 }
@@ -63,20 +71,48 @@ func (vm *Vm) PUSH(obj py.Object) {
6371 vm .frame .Stack = append (vm .frame .Stack , obj )
6472}
6573
74+ // Set an exception in the VM
75+ //
76+ // The exception must be a valid exception instance (eg as returned by
77+ // py.MakeException)
78+ //
79+ // It sets vm.exc.* and sets vm.exit to exitException
80+ func (vm * Vm ) SetException (exception py.Object ) {
81+ vm .old_exc = vm .exc
82+ vm .exc .Value = exception
83+ vm .exc .Type = exception .Type ()
84+ vm .exc .Traceback = py .None // FIXME start the traceback
85+ vm .exit = exitException
86+ }
87+
6688// Check for an exception (panic)
6789//
68- // Must be called as a defer function
69- func (vm * Vm ) CheckException () {
70- if r := recover (); r != nil {
71- // Coerce whatever was raised into a *Exception
72- vm .exception = py .MakeException (r )
90+ // Should be called with the result of recover
91+ func (vm * Vm ) CheckExceptionRecover (r interface {}) {
92+ // If what was raised was an ExceptionInfo the stuff this into the current vm
93+ if exc , ok := r .(py.ExceptionInfo ); ok {
94+ vm .old_exc = vm .exc
95+ vm .exc = exc
7396 vm .exit = exitException
97+ fmt .Printf ("*** Propagating exception: %s\n " , exc .Error ())
98+ } else {
99+ // Coerce whatever was raised into a *Exception
100+ vm .SetException (py .MakeException (r ))
74101 fmt .Printf ("*** Exception raised %v\n " , r )
75102 // Dump the goroutine stack
76103 debug .PrintStack ()
77104 }
78105}
79106
107+ // Check for an exception (panic)
108+ //
109+ // Must be called as a defer function
110+ func (vm * Vm ) CheckException () {
111+ if r := recover (); r != nil {
112+ vm .CheckExceptionRecover (r )
113+ }
114+ }
115+
80116// Illegal instruction
81117func do_ILLEGAL (vm * Vm , arg int32 ) {
82118 defer vm .CheckException ()
@@ -517,15 +553,56 @@ func do_POP_BLOCK(vm *Vm, arg int32) {
517553// exception state.
518554func do_POP_EXCEPT (vm * Vm , arg int32 ) {
519555 defer vm .CheckException ()
520- vm .NotImplemented ("POP_EXCEPT" , arg )
556+ frame := vm .frame
557+ b := vm .frame .Block
558+ frame .PopBlock ()
559+ if b .Type != EXCEPT_HANDLER {
560+ vm .SetException (py .ExceptionNewf (py .SystemError , "popped block is not an except handler" ))
561+ } else {
562+ vm .UnwindExceptHandler (frame , b )
563+ }
521564}
522565
523566// Terminates a finally clause. The interpreter recalls whether the
524567// exception has to be re-raised, or whether the function returns, and
525568// continues with the outer-next block.
526569func do_END_FINALLY (vm * Vm , arg int32 ) {
527570 defer vm .CheckException ()
528- vm .NotImplemented ("END_FINALLY" , arg )
571+ v := vm .POP ()
572+ if vInt , ok := v .(py.Int ); ok {
573+ vm .exit = vmExit (vInt )
574+ if vm .exit == exitYield {
575+ panic ("Unexpected exitYield in END_FINALLY" )
576+ }
577+ if vm .exit == exitReturn || vm .exit == exitContinue {
578+ // Leave return value on the stack
579+ // retval = vm.POP()
580+ }
581+ if vm .exit == exitSilenced {
582+ // An exception was silenced by 'with', we must
583+ // manually unwind the EXCEPT_HANDLER block which was
584+ // created when the exception was caught, otherwise
585+ // the stack will be in an inconsistent state.
586+ frame := vm .frame
587+ b := vm .frame .Block
588+ frame .PopBlock ()
589+ if b .Type != EXCEPT_HANDLER {
590+ panic ("Expecting EXCEPT_HANDLER in END_FINALLY" )
591+ }
592+ vm .UnwindExceptHandler (frame , b )
593+ vm .exit = exitNot
594+ }
595+ } else if py .ExceptionClassCheck (v ) {
596+ w := vm .POP ()
597+ u := vm .POP ()
598+ // FIXME PyErr_Restore(v, w, u)
599+ vm .exc .Type = v
600+ vm .exc .Value = w
601+ vm .exc .Traceback = u
602+ vm .exit = exitReraise
603+ } else if v != py .None {
604+ vm .SetException (py .ExceptionNewf (py .SystemError , "'finally' pops bad exception %#v" , v ))
605+ }
529606}
530607
531608// Loads the __build_class__ helper function to the stack which
@@ -719,15 +796,13 @@ func do_COMPARE_OP(vm *Vm, opname int32) {
719796 if bTuple , ok := b .(py.Tuple ); ok {
720797 for _ , exc := range bTuple {
721798 if ! py .ExceptionClassCheck (exc ) {
722- vm .exception = py .ExceptionNewf (py .TypeError , "Catching '%s' that does not inherit from BaseException is not allowed" , exc .Type ().Name )
723- vm .exit = exitException
799+ vm .SetException (py .ExceptionNewf (py .TypeError , cannotCatchMsg , exc .Type ().Name ))
724800 goto finished
725801 }
726802 }
727803 } else {
728804 if ! py .ExceptionClassCheck (b ) {
729- vm .exception = py .ExceptionNewf (py .TypeError , "Catching '%s' that does not inherit from BaseException is not allowed" , b .Type ().Name )
730- vm .exit = exitException
805+ vm .SetException (py .ExceptionNewf (py .TypeError , cannotCatchMsg , b .Type ().Name ))
731806 goto finished
732807 }
733808 }
@@ -815,7 +890,6 @@ func do_JUMP_ABSOLUTE(vm *Vm, target int32) {
815890// iterator indicates it is exhausted TOS is popped, and the bytecode
816891// counter is incremented by delta.
817892func do_FOR_ITER (vm * Vm , delta int32 ) {
818- defer vm .CheckException ()
819893 defer func () {
820894 if r := recover (); r != nil {
821895 // FIXME match subclasses of StopIteration too?
@@ -824,8 +898,9 @@ func do_FOR_ITER(vm *Vm, delta int32) {
824898 } else if ex , ok := r .(* py.Type ); ok && ex == py .StopIteration {
825899 // StopIteration raised
826900 } else {
827- // re-raise the panic
828- panic (r )
901+ // Deal with the exception as normal
902+ vm .CheckExceptionRecover (r )
903+ return
829904 }
830905 vm .DROP ()
831906 vm .frame .Lasti += delta
@@ -875,8 +950,13 @@ func do_STORE_MAP(vm *Vm, arg int32) {
875950// Pushes a reference to the local co_varnames[var_num] onto the stack.
876951func do_LOAD_FAST (vm * Vm , var_num int32 ) {
877952 defer vm .CheckException ()
878- fmt .Printf ("LOAD_FAST %q\n " , vm .frame .Code .Varnames [var_num ])
879- vm .PUSH (vm .frame .Locals [vm .frame .Code .Varnames [var_num ]])
953+ varname := vm .frame .Code .Varnames [var_num ]
954+ fmt .Printf ("LOAD_FAST %q\n " , varname )
955+ if value , ok := vm .frame .Locals [varname ]; ok {
956+ vm .PUSH (value )
957+ } else {
958+ vm .SetException (py .ExceptionNewf (py .UnboundLocalError , unboundLocalErrorMsg , varname ))
959+ }
880960}
881961
882962// Stores TOS into the local co_varnames[var_num].
@@ -888,7 +968,12 @@ func do_STORE_FAST(vm *Vm, var_num int32) {
888968// Deletes local co_varnames[var_num].
889969func do_DELETE_FAST (vm * Vm , var_num int32 ) {
890970 defer vm .CheckException ()
891- vm .NotImplemented ("DELETE_FAST" , var_num )
971+ varname := vm .frame .Code .Varnames [var_num ]
972+ if _ , ok := vm .frame .Locals [varname ]; ok {
973+ delete (vm .frame .Locals , varname )
974+ } else {
975+ vm .SetException (py .ExceptionNewf (py .UnboundLocalError , unboundLocalErrorMsg , varname ))
976+ }
892977}
893978
894979// Pushes a reference to the cell contained in slot i of the cell and
@@ -934,18 +1019,21 @@ func do_DELETE_DEREF(vm *Vm, i int32) {
9341019func (vm * Vm ) raise (exc , cause py.Object ) {
9351020 if exc == nil {
9361021 // raise (with no parameters == re-raise)
937- if vm .exception == nil {
938- vm .exception = py .ExceptionNewf (py .RuntimeError , "No active exception to reraise" )
1022+ if vm .exc .Value == nil {
1023+ vm .SetException (py .ExceptionNewf (py .RuntimeError , "No active exception to reraise" ))
1024+ } else {
1025+ // Signal the existing exception again
1026+ vm .exit = exitReraise
9391027 }
9401028 } else {
9411029 // raise <instance>
9421030 // raise <type>
943- vm .exception = py .MakeException (exc )
1031+ excException := py .MakeException (exc )
1032+ vm .SetException (excException )
9441033 if cause != nil {
945- vm . exception .Cause = py .MakeException (cause )
1034+ excException .Cause = py .MakeException (cause )
9461035 }
9471036 }
948- vm .exit = exitException
9491037}
9501038
9511039// Raises an exception. argc indicates the number of parameters to the
@@ -1151,16 +1239,16 @@ func (vm *Vm) UnwindExceptHandler(frame *py.Frame, block *py.TryBlock) {
11511239 } else {
11521240 frame .Stack = frame .Stack [:block .Level + 3 ]
11531241 }
1154- vm .exc_type = vm .POP ()
1155- vm .exc_value = vm .POP ()
1156- vm .exc_traceback = vm .POP ()
1242+ vm .exc . Type = vm .POP ()
1243+ vm .exc . Value = vm .POP ()
1244+ vm .exc . Traceback = vm .POP ()
11571245}
11581246
11591247// Run the virtual machine on a Frame object
11601248//
11611249// FIXME figure out how we are going to signal exceptions!
11621250//
1163- // Returns an Object and an error
1251+ // Returns an Object and an error. The error will be a py.ExceptionInfo
11641252func RunFrame (frame * py.Frame ) (res py.Object , err error ) {
11651253 vm := NewVm (frame )
11661254 // defer func() {
@@ -1237,32 +1325,32 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
12371325 frame .Lasti = b .Handler
12381326 break
12391327 }
1240- if vm .exit == exitException && (b .Type == SETUP_EXCEPT || b .Type == SETUP_FINALLY ) {
1328+ if vm .exit & ( exitException | exitReraise ) != 0 && (b .Type == SETUP_EXCEPT || b .Type == SETUP_FINALLY ) {
12411329 fmt .Printf ("*** Exception\n " )
12421330 var exc , val , tb py.Object
12431331 handler := b .Handler
12441332 // This invalidates b
12451333 frame .PushBlock (EXCEPT_HANDLER , - 1 , vm .STACK_LEVEL ())
1246- vm .PUSH (vm .exc_traceback )
1247- vm .PUSH (vm .exc_value )
1248- if vm .exc_type != nil {
1249- vm .PUSH (vm .exc_type )
1334+ vm .PUSH (vm .old_exc . Traceback )
1335+ vm .PUSH (vm .old_exc . Value )
1336+ if vm .old_exc . Type != nil {
1337+ vm .PUSH (vm .exc . Type )
12501338 } else {
12511339 vm .PUSH (py .None )
12521340 }
12531341 // FIXME PyErr_Fetch(&exc, &val, &tb)
1254- exc = vm .exc_type
1255- val = vm .exc_value
1256- tb = vm .exc_traceback
1342+ exc = vm .exc . Type
1343+ val = vm .exc . Value
1344+ tb = vm .exc . Traceback
12571345 // Make the raw exception data
12581346 // available to the handler,
12591347 // so a program can emulate the
12601348 // Python main loop.
12611349 // FIXME PyErr_NormalizeException(exc, &val, &tb)
12621350 // FIXME PyException_SetTraceback(val, tb)
1263- vm .exc_type = exc
1264- vm .exc_value = val
1265- vm .exc_traceback = tb
1351+ vm .exc . Type = exc
1352+ vm .exc . Value = val
1353+ vm .exc . Traceback = tb
12661354 if tb == nil {
12671355 tb = py .None
12681356 }
@@ -1287,15 +1375,17 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
12871375 }
12881376 }
12891377 }
1290-
1291- return vm .result , vm .exception
1378+ if vm .exc .Value != nil {
1379+ return vm .result , vm .exc
1380+ }
1381+ return vm .result , nil
12921382}
12931383
12941384// Run the virtual machine on a Code object
12951385//
12961386// Any parameters are expected to have been decoded into locals
12971387//
1298- // Returns an Object and an error
1388+ // Returns an Object and an error. The error will be a py.ExceptionInfo
12991389func Run (globals , locals py.StringDict , code * py.Code ) (res py.Object , err error ) {
13001390 frame := py .NewFrame (globals , locals , code )
13011391
0 commit comments