Skip to content

Commit 0d4a6d4

Browse files
committed
vm: tests and fixes for exceptions
* Add IsSet method to ExceptionInfo and use it to fix bad tests * Correct raise handling * Correct exception handling * Add finished marker to the tests to detect silent early exit * Still some exceptions tests failing
1 parent 5ceac9c commit 0d4a6d4

File tree

10 files changed

+249
-37
lines changed

10 files changed

+249
-37
lines changed

‎py/exception.go‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ func (exc *ExceptionInfo) TracebackDump(w io.Writer){
119119
fmt.Fprintf(w, "%v: %v\n", exc.Type.Name, exc.Value)
120120
}
121121

122+
// Test for being set
123+
func (exc*ExceptionInfo) IsSet() bool{
124+
returnexc.Type!=nil
125+
}
126+
122127
// ExceptionNew
123128
funcExceptionNew(metatype*Type, argsTuple, kwargsStringDict) Object{
124129
iflen(kwargs) !=0{
@@ -229,7 +234,7 @@ func ExceptionGivenMatches(err, exc Object) bool{
229234

230235
// Test the tuple case recursively
231236
ifexcTuple, ok:=exc.(Tuple); ok{
232-
fori:=0; i<len(excTuple); i++{
237+
fori:=rangeexcTuple{
233238
ifExceptionGivenMatches(err, excTuple[i]){
234239
returntrue
235240
}

‎vm/eval.go‎

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,8 @@ func do_END_FINALLY(vm *Vm, arg int32){
660660
switchvm.exit{
661661
caseexitYield:
662662
panic("Unexpected exitYield in END_FINALLY")
663+
caseexitException:
664+
panic("Unexpected exitException in END_FINALLY")
663665
caseexitReturn, exitContinue:
664666
vm.result=vm.POP()
665667
caseexitSilenced:
@@ -689,7 +691,7 @@ func do_END_FINALLY(vm *Vm, arg int32){
689691
} else{
690692
vm.ClearException()
691693
}
692-
694+
debugf("END_FINALLY: vm.exit = %v\n", vm.exit)
693695
}
694696

695697
// Loads the __build_class__ helper function to the stack which
@@ -1201,7 +1203,7 @@ func do_DELETE_DEREF(vm *Vm, i int32){
12011203
func (vm*Vm) raise(exc, cause py.Object){
12021204
ifexc==nil{
12031205
// raise (with no parameters == re-raise)
1204-
ifvm.exc.Value==nil{
1206+
if!vm.exc.IsSet(){
12051207
vm.SetException(py.ExceptionNewf(py.RuntimeError, "No active exception to reraise"))
12061208
} else{
12071209
// Signal the existing exception again
@@ -1211,6 +1213,7 @@ func (vm *Vm) raise(exc, cause py.Object){
12111213
// raise <instance>
12121214
// raise <type>
12131215
excException:=py.MakeException(exc)
1216+
debugf("raise: excException = %v\n", excException)
12141217
vm.SetException(excException)
12151218
ifcause!=nil{
12161219
excException.Cause=py.MakeException(cause)
@@ -1431,24 +1434,17 @@ func (vm *Vm) UnwindBlock(frame *py.Frame, block *py.TryBlock){
14311434

14321435
// Unwinds the stack in the presence of an exception
14331436
func (vm*Vm) UnwindExceptHandler(frame*py.Frame, block*py.TryBlock){
1437+
debugf("** UnwindExceptHandler stack depth %v\n", vm.STACK_LEVEL())
14341438
ifvm.STACK_LEVEL() <block.Level+3{
14351439
panic("Couldn't find traceback on stack")
14361440
} else{
14371441
frame.Stack=frame.Stack[:block.Level+3]
14381442
}
1439-
// If have just raised an exception, don't overwrite it
1440-
//
1441-
// FIXME if have two exceptions python shows both tracebacks
1442-
//
1443-
// FIXME this is a departure from python's way not sure it is
1444-
// correct
1445-
ifvm.exc.Value!=nil{
1446-
vm.DROPN(3)
1447-
} else{
1448-
vm.exc.Type=vm.POP().(*py.Type)
1449-
vm.exc.Value=vm.POP()
1450-
vm.exc.Traceback=vm.POP().(*py.Traceback)
1451-
}
1443+
debugf("** UnwindExceptHandler stack depth now %v\n", vm.STACK_LEVEL())
1444+
vm.exc.Type=vm.POP().(*py.Type)
1445+
vm.exc.Value=vm.POP()
1446+
vm.exc.Traceback=vm.POP().(*py.Traceback)
1447+
debugf("** UnwindExceptHandler exc = (type: %v, value: %v, traceback: %v)\n", vm.exc.Type, vm.exc.Value, vm.exc.Traceback)
14521448
}
14531449

14541450
// Run the virtual machine on a Frame object
@@ -1534,13 +1530,13 @@ func RunFrame(frame *py.Frame) (res py.Object, err error){
15341530
frame.Lasti=b.Handler
15351531
break
15361532
}
1537-
ifvm.exit&(exitException|exitReraise) !=0&& (b.Type==SETUP_EXCEPT||b.Type==SETUP_FINALLY){
1533+
if(vm.exit==exitException||vm.exit==exitReraise)&& (b.Type==SETUP_EXCEPT||b.Type==SETUP_FINALLY){
15381534
debugf("*** Exception\n")
15391535
handler:=b.Handler
15401536
// This invalidates b
15411537
frame.PushBlock(EXCEPT_HANDLER, -1, vm.STACK_LEVEL())
1542-
vm.PUSH(vm.old_exc.Traceback)
1543-
vm.PUSH(vm.old_exc.Value)
1538+
vm.PUSH(vm.exc.Traceback)
1539+
vm.PUSH(vm.exc.Value)
15441540
vm.PUSH(vm.exc.Type) // can be nil
15451541
// FIXME PyErr_Fetch(&exc, &val, &tb)
15461542
exc:=vm.exc.Type
@@ -1563,7 +1559,7 @@ func RunFrame(frame *py.Frame) (res py.Object, err error){
15631559
break
15641560
}
15651561
ifb.Type==SETUP_FINALLY{
1566-
ifvm.exit&(exitReturn|exitContinue) !=0{
1562+
ifvm.exit==exitReturn||vm.exit==exitContinue{
15671563
vm.PUSH(vm.result)
15681564
}
15691565
vm.PUSH(py.Int(vm.exit))
@@ -1573,7 +1569,7 @@ func RunFrame(frame *py.Frame) (res py.Object, err error){
15731569
}
15741570
}
15751571
}
1576-
ifvm.exc.Value!=nil{
1572+
ifvm.exc.IsSet(){
15771573
returnvm.result, vm.exc
15781574
}
15791575
returnvm.result, nil

‎vm/stringer.go‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// generated by stringer -type=vmExit -output stringer.go; DO NOT EDIT
2+
3+
package vm
4+
5+
import"fmt"
6+
7+
const_vmExit_name="exitNotexitExceptionexitReraiseexitReturnexitBreakexitContinueexitYieldexitSilenced"
8+
9+
var_vmExit_index= [...]uint8{0, 7, 20, 31, 41, 50, 62, 71, 83}
10+
11+
func (ivmExit) String() string{
12+
ifi+1>=vmExit(len(_vmExit_index)){
13+
returnfmt.Sprintf("vmExit(%d)", i)
14+
}
15+
return_vmExit_name[_vmExit_index[i]:_vmExit_index[i+1]]
16+
}

‎vm/tests/class.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,5 @@ def method1(self, x):
4646
c=x()
4747
assertc.method1(1) ==2
4848

49+
# End with this
50+
finished=True

‎vm/tests/comprehensions.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@
2525
A={"a":1, "b":2, "c":3}
2626
B={k:kforkin ("a","b","c") }
2727
assertB["b"] =="b"
28+
29+
# End with this
30+
finished=True

‎vm/tests/exceptions.py‎

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env python3.4
2+
3+
# Test exceptions
4+
5+
# Straight forward
6+
ok=False
7+
try:
8+
raiseValueError
9+
exceptValueError:
10+
ok=True
11+
assertok
12+
13+
ok=False
14+
try:
15+
raiseValueError
16+
exceptValueErrorase:
17+
ok=True
18+
assertok
19+
20+
ok=False
21+
try:
22+
raiseValueError
23+
exceptValueErrorase:
24+
ok=True
25+
assertok
26+
27+
ok=False
28+
try:
29+
raiseValueError
30+
except (IOError, ValueError) ase:
31+
ok=True
32+
assertok
33+
34+
ok=False
35+
try:
36+
raiseValueError("Potato")
37+
except (IOError, ValueError) ase:
38+
ok=True
39+
assertok
40+
41+
# hierarchy
42+
# FIXME doesn't work because IsSubtype is broken ValueError.IsSubtype(Exception) == false
43+
# ok = False
44+
# try:
45+
# raise ValueError("potato")
46+
# except Exception:
47+
# ok = True
48+
# assert ok
49+
50+
ok=False
51+
try:
52+
raiseValueError
53+
exceptIOError:
54+
assertFalse, "Not expecting IO Error"
55+
exceptValueError:
56+
ok=True
57+
assertok
58+
59+
# no exception
60+
ok=False
61+
try:
62+
pass
63+
exceptValueError:
64+
assertFalse, "Not expecting ValueError"
65+
else:
66+
ok=True
67+
assertok
68+
69+
# nested
70+
ok=False
71+
try:
72+
try:
73+
raiseValueError("potato")
74+
exceptIOErrorase:
75+
assertFalse, "Not expecting IOError"
76+
else:
77+
assertFalse, "Expecting ValueError"
78+
exceptValueError:
79+
ok=True
80+
else:
81+
assertFalse, "Expecting ValueError (outer)"
82+
assertok
83+
84+
ok1=False
85+
ok2=False
86+
try:
87+
try:
88+
raiseIOError("potato")
89+
exceptIOErrorase:
90+
ok1=True
91+
else:
92+
assertFalse, "Expecting ValueError"
93+
exceptValueError:
94+
assertFalse, "Expecting IOError"
95+
exceptIOError:
96+
assertFalse, "Expecting IOError"
97+
else:
98+
ok2=True
99+
assertok
100+
101+
# re-raise
102+
ok1=False
103+
ok2=False
104+
try:
105+
try:
106+
raiseValueError("potato")
107+
exceptValueErrorase:
108+
ok2=True
109+
raise
110+
else:
111+
assertFalse, "Expecting ValueError (inner)"
112+
exceptValueError:
113+
ok1=True
114+
else:
115+
assertFalse, "Expecting ValueError (outer)"
116+
assertok1andok2
117+
118+
# try/finally
119+
ok1=False
120+
ok2=False
121+
ok3=False
122+
try:
123+
try:
124+
ok1=True
125+
finally:
126+
ok2=True
127+
exceptValueError:
128+
assertFalse, "Not expecting ValueError (outer)"
129+
else:
130+
ok3=True
131+
assertok1andok2andok3
132+
133+
# FIXME
134+
# ok1 = False
135+
# ok2 = False
136+
# try:
137+
# try:
138+
# raise ValueError()
139+
# finally:
140+
# ok1 = True
141+
# except ValueError:
142+
# ok2 = True
143+
# else:
144+
# assert False, "Expecting ValueError (outer)"
145+
# assert ok1 and ok2
146+
147+
# End with this
148+
finished=True

‎vm/tests/functions.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,7 @@ def fn12(*args,a=2,b=3,**kwargs) -> "RET":
153153

154154

155155
#FIXME decorators
156+
157+
158+
# End with this
159+
finished=True

‎vm/tests/ops.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,37 @@
9292
assertTrueisTrue
9393
assertTrueisnotFalse
9494
# FIXME EXC_MATCH
95+
96+
# logical
97+
t=True
98+
f=False
99+
assert (fandf) ==False
100+
assert (fandt) ==False
101+
assert (tandf) ==False
102+
assert (tandt) ==True
103+
104+
assert (fandfandf) ==False
105+
assert (fandfandt) ==False
106+
assert (fandtandf) ==False
107+
assert (fandtandt) ==False
108+
assert (tandfandf) ==False
109+
assert (tandfandt) ==False
110+
assert (tandtandf) ==False
111+
assert (tandtandt) ==True
112+
113+
assert (forf) ==False
114+
assert (fort) ==True
115+
assert (torf) ==True
116+
assert (tort) ==True
117+
118+
assert (forforf) ==False
119+
assert (forfort) ==True
120+
assert (fortorf) ==True
121+
assert (fortort) ==True
122+
assert (torforf) ==True
123+
assert (torfort) ==True
124+
assert (tortorf) ==True
125+
assert (tortort) ==True
126+
127+
# End with this
128+
finished=True

‎vm/vm.go‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import (
55
"github.com/ncw/gpython/py"
66
)
77

8+
//go:generate stringer -type=vmExit -output stringer.go
9+
810
// VM exit type
911
typevmExitbyte
1012

1113
// VM exit values
1214
const (
13-
exitNot=vmExit(iota)// No error
14-
exitException// Exception occurred
15-
exitReraise// Exception re-raised by 'finally'
16-
exitReturn// 'return' statement
17-
exitBreak// 'break' statement
18-
exitContinue// 'continue' statement
19-
exitYield// 'yield' operator
20-
exitSilenced// Exception silenced by 'with'
15+
exitNotvmExit=iota// No error
16+
exitException// Exception occurred
17+
exitReraise// Exception re-raised by 'finally'
18+
exitReturn// 'return' statement
19+
exitBreak// 'break' statement
20+
exitContinue// 'continue' statement
21+
exitYield// 'yield' operator
22+
exitSilenced// Exception silenced by 'with'
2123
)
2224

2325
// Virtual machine state

0 commit comments

Comments
(0)