@@ -34,6 +34,14 @@ import (
34
34
"runtime/debug"
35
35
)
36
36
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
8000
' referenced before assignment in enclosing scope"
42
+ cannotCatchMsg = "catching '%s' that does not inherit from BaseException is not allowed"
43
+ )
44
+
37
45
// Stack operations
38
46
func (vm * Vm ) STACK_LEVEL () int { return len (vm .frame .Stack ) }
39
47
func (vm * Vm ) EMPTY () bool { return len (vm .frame .Stack ) == 0 }
@@ -63,20 +71,48 @@ func (vm *Vm) PUSH(obj py.Object) {
63
71
vm .frame .Stack = append (vm .frame .Stack , obj )
64
72
}
65
73
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
+
66
88
// Check for an exception (panic)
67
89
//
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
73
96
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 ))
74
101
fmt .Printf ("*** Exception raised %v\n " , r )
75
102
// Dump the goroutine stack
76
103
debug .PrintStack ()
77
104
}
78
105
}
79
106
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
+
80
116
// Illegal instruction
81
117
func do_ILLEGAL (vm * Vm , arg int32 ) {
82
118
defer vm .CheckException ()
@@ -517,15 +553,56 @@ func do_POP_BLOCK(vm *Vm, arg int32) {
517
553
// exception state.
518
554
func do_POP_EXCEPT (vm * Vm , arg int32 ) {
519
555
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
+ }
521
564
}
522
565
523
566
// Terminates a finally clause. The interpreter recalls whether the
524
567
// exception has to be re-raised, or whether the function returns, and
525
568
// continues with the outer-next block.
526
569
func do_END_FINALLY (vm * Vm , arg int32 ) {
527
570
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
+ }
529
606
}
530
607
531
608
// Loads the __build_class__ helper function to the stack which
@@ -719,15 +796,13 @@ func do_COMPARE_OP(vm *Vm, opname int32) {
719
796
if bTuple , ok := b .(py.Tuple ); ok {
720
797
for _ , exc := range bTuple {
721
798
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 ))
724
800
goto finished
725
801
}
726
802
}
727
803
} else {
728
804
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 ))
731
806
goto finished
732
807
}
733
808
}
@@ -815,7 +890,6 @@ func do_JUMP_ABSOLUTE(vm *Vm, target int32) {
815
890
// iterator indicates it is exhausted TOS is popped, and the bytecode
816
891
// counter is incremented by delta.
817
892
func do_FOR_ITER (vm * Vm , delta int32 ) {
818
- defer vm .CheckException ()
819
893
defer func () {
820
894
if r := recover (); r != nil {
821
895
// FIXME match subclasses of StopIteration too?
@@ -824,8 +898,9 @@ func do_FOR_ITER(vm *Vm, delta int32) {
824
898
} else if ex , ok := r .(* py.Type ); ok && ex == py .StopIteration {
825
899
// StopIteration raised
826
900
} else {
827
- // re-raise the panic
828
- panic (r )
901
+ // Deal with the exception as normal
902
+ vm .CheckExceptionRecover (r )
903
+ return
829
904
}
830
905
vm .DROP ()
831
906
vm .frame .Lasti += delta
@@ -875,8 +950,13 @@ func do_STORE_MAP(vm *Vm, arg int32) {
875
950
// Pushes a reference to the local co_varnames[var_num] onto the stack.
876
951
func do_LOAD_FAST (vm * Vm , var_num int32 ) {
877
952
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
+ }
880
960
}
881
961
882
962
// Stores TOS into the local co_varnames[var_num].
@@ -888,7 +968,12 @@ func do_STORE_FAST(vm *Vm, var_num int32) {
888
968
// Deletes local co_varnames[var_num].
889
969
func do_DELETE_FAST (vm * Vm , var_num int32 ) {
890
970
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
+ }
892
977
}
893
978
894
979
// 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) {
934
1019
func (vm * Vm ) raise (exc , cause py.Object ) {
935
1020
if exc == nil {
936
1021
// 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
939
1027
}
940
1028
} else {
941
1029
// raise <instance>
942
1030
// raise <type>
943
- vm .exception = py .MakeException (exc )
1031
+ excException := py .MakeException (exc )
1032
+ vm .SetException (excException )
944
1033
if cause != nil {
945
- vm . exception .Cause = py .MakeException (cause )
1034
+ excException .Cause = py .MakeException (cause )
946
1035
}
947
1036
}
948
- vm .exit = exitException
949
1037
}
950
1038
951
1039
// 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) {
1151
1239
} else {
1152
1240
frame .Stack = frame .Stack [:block .Level + 3 ]
1153
1241
}
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 ()
1157
1245
}
1158
1246
1159
1247
// Run the virtual machine on a Frame object
1160
1248
//
1161
1249
// FIXME figure out how we are going to signal exceptions!
1162
1250
//
1163
- // Returns an Object and an error
1251
+ // Returns an Object and an error. The error will be a py.ExceptionInfo
1164
1252
func RunFrame (frame * py.Frame ) (res py.Object , err error ) {
1165
1253
vm := NewVm (frame )
1166
1254
// defer func() {
@@ -1237,32 +1325,32 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1237
1325
frame .Lasti = b .Handler
1238
1326
break
1239
1327
}
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 ) {
1241
1329
fmt .Printf ("*** Exception\n " )
1242
1330
var exc , val , tb py.Object
1243
1331
handler := b .Handler
1244
1332
// This invalidates b
1245
1333
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 )
1250
1338
} else {
1251
1339
vm .PUSH (py .None )
1252
1340
}
1253
1341
// 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
1257
1345
// Make the raw exception data
1258
1346
// available to the handler,
1259
1347
// so a program can emulate the
1260
1348
// Python main loop.
1261
1349
// FIXME PyErr_NormalizeException(exc, &val, &tb)
1262
1350
// 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
1266
1354
if tb == nil {
1267
1355
tb = py .None
1268
1356
}
@@ -1287,15 +1375,17 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
1287
1375
}
1288
1376
}
1289
1377
}
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
1292
1382
}
1293
1383
1294
1384
// Run the virtual machine on a Code object
1295
1385
//
1296
1386
// Any parameters are expected to have been decoded into locals
1297
1387
//
1298
- // Returns an Object and an error
1388
+ // Returns an Object and an error. The error will be a py.ExceptionInfo
1299
1389
func Run (globals , locals py.StringDict , code * py.Code ) (res py.Object , err error ) {
1300
1390
frame := py .NewFrame (globals , locals , code )
1301
1391