@@ -22,7 +22,7 @@ use super::obj::objtype;
22
22
use super :: objbool;
23
23
use super :: pyobject:: {
24
24
AttributeProtocol , DictProtocol , IdProtocol , ParentProtocol , PyContext , PyFuncArgs , PyObject ,
25
- PyObjectKind , PyObjectRef , PyResult , ToRust ,
25
+ PyObjectKind , PyObjectRef , PyResult , ToRust , TypeProtocol ,
26
26
} ;
27
27
use super :: stdlib;
28
28
use super :: sysmodule;
@@ -175,24 +175,69 @@ impl VirtualMachine {
175
175
self . current_frame ( ) . last_block ( )
176
176
}
177
177
178
- fn with_exit ( & mut self ) {
178
+ fn with_exit ( & mut self , context_manager : & PyObjectRef , exc : Option < PyObjectRef > ) -> PyResult {
179
179
// Assume top of stack is __exit__ method:
180
180
// TODO: do we want to put the exit call on the stack?
181
- let exit_method = self . pop_value ( ) ;
182
- let args = PyFuncArgs :: default ( ) ;
181
+ // let exit_method = self.pop_value();
182
+ // let args = PyFuncArgs::default();
183
183
// TODO: what happens when we got an error during handling exception?
184
- self . invoke ( exit_method, args) . unwrap ( ) ;
184
+ let args = if let Some ( exc) = exc {
185
+ let exc_type = exc. typ ( ) ;
186
+ let exc_val = exc. clone ( ) ;
187
+ let exc_tb = self . ctx . none ( ) ; // TODO: retrieve traceback?
188
+ vec ! [ exc_type, exc_val, exc_tb]
189
+ } else {
190
+ let exc_type = self . ctx . none ( ) ;
191
+ let exc_val = self . ctx . none ( ) ;
192
+ let exc_tb = self . ctx . none ( ) ;
193
+ vec ! [ exc_type, exc_val, exc_tb]
194
+ } ;
195
+ self . call_method ( context_manager. clone ( ) , "__exit__" , args)
196
+ }
197
+
198
+ // Unwind all blocks:
199
+ fn unwind_blocks ( & mut self ) -> Option < PyObjectRef > {
200
+ loop {
201
+ let block = self . pop_block ( ) ;
202
+ match block {
203
+ Some ( Block :: Loop { .. } ) => { }
204
+ Some ( Block :: TryExcept { .. } ) => {
205
+ // TODO: execute finally handler
206
+ }
207
+ Some ( Block :: With {
208
+ end : _,
209
+ context_manager,
210
+ } ) => {
211
+ match self . with_exit ( & context_manager, None ) {
212
+ Ok ( ..) => { }
213
+ Err ( exc) => {
214
+ // __exit__ went wrong,
215
+ return Some ( exc) ;
216
+ }
217
+ }
218
+ }
219
+ None => break None ,
220
+ }
221
+ }
185
222
}
186
223
187
224
fn unwind_loop ( & mut self ) -> Block {
188
225
loop {
189
226
let block = self . pop_block ( ) ;
190
227
match block {
191
228
Some ( Block :: Loop { start : _, end : __ } ) => break block. unwrap ( ) ,
192
- Some ( Block :: TryExcept { .. } ) => { }
193
- Some ( Block :: With { .. } ) => {
194
- self . with_exit ( ) ;
229
+ Some ( Block :: TryExcept { .. } ) => {
230
+ // TODO: execute finally handler
195
231
}
232
+ Some ( Block :: With {
233
+ end : _,
234
+ context_manager,
235
+ } ) => match self . with_exit ( & context_manager, None ) {
236
+ Ok ( ..) => { }
237
+ Err ( exc) => {
238
+ panic ! ( "Exception in with __exit__ {:?}" , exc) ;
239
+ }
240
+ } ,
196
241
None => panic ! ( "No block to break / continue" ) ,
197
242
}
198
243
}
@@ -208,8 +253,33 @@ impl VirtualMachine {
208
253
self . jump ( handler) ;
209
254
return None ;
210
255
}
211
- Some ( Block :: With { .. } ) => {
212
- self . with_exit ( ) ;
256
+ Some ( Block :: With {
257
+ end,
258
+ context_manager,
259
+ } ) => {
260
+ match self . with_exit ( & context_manager, Some ( exc. clone ( ) ) ) {
261
+ Ok ( exit_action) => {
262
+ match objbool:: boolval ( self , exit_action) {
263
+ Ok ( handle_exception) => {
264
+ if handle_exception {
265
+ // We handle the exception, so return!
266
+ self . jump ( end) ;
267
+ return None ;
268
+ } else {
269
+ // go on with the stack unwinding.
270
+ }
271
+ }
272
+ Err ( exit_exc) => {
273
+ return Some ( exit_exc) ;
274
+ }
275
+ }
276
+ // if objtype::isinstance
277
+ }
278
+ Err ( exit_exc) => {
279
+ // TODO: what about original exception?
280
+ return Some ( exit_exc) ;
281
+ }
282
+ }
213
283
}
214
284
Some ( Block :: Loop { .. } ) => { }
215
285
None => break ,
@@ -825,7 +895,11 @@ impl VirtualMachine {
825
895
bytecode:: Instruction :: CompareOperation { ref op } => self . execute_compare ( op) ,
826
896
bytecode:: Instruction :: ReturnValue => {
827
897
let value = self . pop_value ( ) ;
828
- Some ( Ok ( value) )
898
+ if let Some ( exc) = self . unwind_blocks ( ) {
899
+ Some ( Err ( exc) )
900
+ } else {
901
+ Some ( Ok ( value) )
902
+ }
829
903
}
830
904
bytecode:: Instruction :: SetupLoop { start, end } => {
831
905
self . push_block ( Block :: Loop {
@@ -862,17 +936,11 @@ impl VirtualMachine {
862
936
{
863
937
assert ! ( end1 == end2) ;
864
938
865
- // call exit now:
866
- // TODO: improve exception handling in context manager.
867
- let exc_type = self . ctx . none ( ) ;
868
- let exc_val = self . ctx . none ( ) ;
869
- let exc_tb = self . ctx . none ( ) ;
870
- self . call_method (
871
- context_manager. clone ( ) ,
872
- "__exit__" ,
873
- vec ! [ exc_type, exc_val, exc_tb] ,
874
- ) . unwrap ( ) ;
875
- None
939
+ // call exit now with no exception:
940
+ match self . with_exit ( context_manager, None ) {
941
+ Ok ( ..) => None ,
942
+ Err ( exc) => Some ( Err ( exc) ) ,
943
+ }
876
944
} else {
877
945
panic ! ( "Block stack is incorrect, expected a with block" ) ;
878
946
}
0 commit comments