@@ -1040,9 +1040,14 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
1040
1040
* This is the main reason of using this opcode instead of
1041
1041
* "LOAD_CONST None". */
1042
1042
return 6 ;
1043
+ case RERAISE :
1044
+ return -3 ;
1043
1045
case CALL_FINALLY :
1044
1046
return 1 ;
1045
1047
1048
+ case WITH_EXCEPT_START :
1049
+ return 1 ;
1050
+
1046
1051
case LOAD_FAST :
1047
1052
return 1 ;
1048
1053
case STORE_FAST :
@@ -1474,6 +1479,15 @@ compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
1474
1479
assert (u -> u_fblock [u -> u_nfblocks ].fb_block == b );
1475
1480
}
1476
1481
1482
+ static int
1483
+ compiler_call_exit_with_nones (struct compiler * c ) {
1484
+ ADDOP_O (c , LOAD_CONST , Py_None , consts );
1485
+ ADDOP (c , DUP_TOP );
1486
+ ADDOP (c , DUP_TOP );
1487
+ ADDOP_I (c , CALL_FUNCTION , 3 );
1488
+ return 1 ;
1489
+ }
1490
+
1477
1491
/* Unwind a frame block. If preserve_tos is true, the TOS before
1478
1492
* popping the blocks will be restored afterwards.
1479
1493
*/
@@ -1511,15 +1525,14 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
1511
1525
ADDOP (c , POP_BLOCK );
1512
1526
if (preserve_tos )
1513
1527
ADDOP (c , ROT_TWO );
1514
- ADDOP ( c , BEGIN_FINALLY );
1515
- ADDOP ( c , WITH_CLEANUP_START ) ;
1528
+ if (! compiler_call_exit_with_nones ( c ))
1529
+ return 0 ;
1516
1530
if (info -> fb_type == ASYNC_WITH ) {
1517
1531
ADDOP (c , GET_AWAITABLE );
1518
1532
ADDOP_O (c , LOAD_CONST , Py_None , consts );
1519
1533
ADDOP (c , YIELD_FROM );
1520
1534
}
1521
- ADDOP (c , WITH_CLEANUP_FINISH );
1522
- ADDOP_I (c , POP_FINALLY , 0 );
1535
+ ADDOP (c , POP_TOP );
1523
1536
return 1 ;
1524
1537
1525
1538
case HANDLER_CLEANUP :
@@ -4282,6 +4295,22 @@ expr_constant(expr_ty e)
4282
4295
return -1 ;
4283
4296
}
4284
4297
4298
+ static int
4299
+ compiler_with_except_finish (struct compiler * c ) {
4300
+ basicblock * exit ;
4301
+ exit = compiler_new_block (c );
4302
+ if (exit == NULL )
4303
+ return 0 ;
4304
+ ADDOP_JABS (c , POP_JUMP_IF_TRUE , exit );
4305
+ ADDOP (c , RERAISE );
4306
+ compiler_use_next_block (c , exit );
4307
+ ADDOP (c , POP_TOP );
4308
+ ADDOP (c , POP_TOP );
4309
+ ADDOP (c , POP_TOP );
4310
+ ADDOP (c , POP_EXCEPT );
4311
+ ADDOP (c , POP_TOP );
4312
+ return 1 ;
4313
+ }
4285
4314
4286
4315
/*
4287
4316
Implements the async with statement.
@@ -4310,14 +4339,16 @@ expr_constant(expr_ty e)
4310
4339
static int
4311
4340
compiler_async_with (struct compiler * c , stmt_ty s , int pos )
4312
4341
{
4313
- basicblock * block , * finally ;
4342
+ basicblock * block , * final1 , * final2 , * exit ;
4314
4343
withitem_ty item = asdl_seq_GET (s -> v .AsyncWith .items , pos );
4315
4344
4316
4345
assert (s -> kind == AsyncWith_kind );
4317
4346
4318
4347
block = compiler_new_block (c );
4319
- finally = compiler_new_block (c );
4320
- if (!block || !finally )
4348
+ final1 = compiler_new_block (c );
4349
+ final2 = compiler_new_block (c );
4350
+ exit = compiler_new_block (c );
4351
+ if (!block || !final1 || !final2 || !exit )
4321
4352
return 0 ;
4322
4353
4323
4354
/* Evaluate EXPR */
@@ -4328,11 +4359,11 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
4328
4359
ADDOP_O (c , LOAD_CONST , Py_None , consts );
4329
4360
ADDOP (c , YIELD_FROM );
4330
4361
4331
- ADDOP_JREL (c , SETUP_ASYNC_WITH , finally );
4362
+ ADDOP_JREL (c , SETUP_ASYNC_WITH , final2 );
4332
4363
4333
4364
/* SETUP_ASYNC_WITH pushes a finally block. */
4334
4365
compiler_use_next_block (c , block );
4335
- if (!compiler_push_fblock (c , ASYNC_WITH , block , finally )) {
4366
+ if (!compiler_push_fblock (c , ASYNC_WITH , block , final2 )) {
4336
4367
return 0 ;
4337
4368
}
4338
4369
@@ -4353,74 +4384,88 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
4353
4384
4354
4385
/* End of try block; start the finally block */
4355
4386
ADDOP (c , POP_BLOCK );
4356
- ADDOP (c , BEGIN_FINALLY );
4357
4387
compiler_pop_fblock (c , ASYNC_WITH , block );
4358
4388
4359
- compiler_use_next_block (c , finally );
4360
- if (!compiler_push_fblock (c , FINALLY_END , finally , NULL ))
4389
+ compiler_use_next_block (c , final1 );
4390
+ if (!compiler_push_fblock (c , FINALLY_END , final1 , NULL ))
4361
4391
return 0 ;
4362
4392
4363
- /* Finally block starts; context.__exit__ is on the stack under
4364
- the exception or return information. Just issue our magic
4365
- opcode. */
4366
- ADDOP (c , WITH_CLEANUP_START );
4393
+ /* `finally` block for successful outcome:
4394
+ * call __exit__(None, None, None)
4395
+ */
4396
+ if (!compiler_call_exit_with_nones (c ))
4397
+ return 0 ;
4398
+ ADDOP (c , GET_AWAITABLE );
4399
+ ADDOP_O (c , LOAD_CONST , Py_None , consts );
4400
+ ADDOP (c , YIELD_FROM );
4401
+
4402
+ ADDOP (c , POP_TOP );
4403
+
4404
+ compiler_pop_fblock (c , FINALLY_END , final1 );
4405
+ ADDOP_JABS (c , JUMP_ABSOLUTE , exit );
4367
4406
4407
+ /* `finally` block for exceptional outcome */
4408
+ compiler_use_next_block (c , final2 );
4409
+ if (!compiler_push_fblock (c , FINALLY_END , final2 , NULL ))
4410
+ return 0 ;
4411
+
4412
+ ADDOP (c , WITH_EXCEPT_START );
4368
4413
ADDOP (c , GET_AWAITABLE );
4369
4414
ADDOP_O (c , LOAD_CONST , Py_None , consts );
4370
4415
ADDOP (c , YIELD_FROM );
4416
+ compiler_with_except_finish (c );
4371
4417
4372
- ADDOP (c , WITH_CLEANUP_FINISH );
4418
+ compiler_pop_fblock (c , FINALLY_END , final2 );
4373
4419
4374
- /* Finally block ends. */
4375
- ADDOP (c , END_FINALLY );
4376
- compiler_pop_fblock (c , FINALLY_END , finally );
4420
+ compiler_use_next_block (c , exit );
4377
4421
return 1 ;
4378
4422
}
4379
4423
4380
4424
4381
4425
/*
4382
4426
Implements the with statement from PEP 343.
4383
-
4384
- The semantics outlined in that PEP are as follows:
4385
-
4386
4427
with EXPR as VAR:
4387
4428
BLOCK
4388
-
4389
- It is implemented roughly as:
4390
-
4391
- context = EXPR
4392
- exit = context.__exit__ # not calling it
4393
- value = context.__enter__( )
4394
- try:
4395
- VAR = value # if VAR present in the syntax
4396
- BLOCK
4397
- finally :
4398
- if an exception was raised:
4399
- exc = copy of ( exception, instance, traceback )
4400
- else:
4401
- exc = (None, None, None)
4402
- exit(*exc)
4429
+ is implemented as:
4430
+ <code for EXPR>
4431
+ SETUP_WITH E
4432
+ <code to store to VAR> or POP_TOP
4433
+ <code for BLOCK>
4434
+ LOAD_CONST (None, None, None )
4435
+ CALL_FUNCTION_EX 0
4436
+ JUMP_FORWARD EXIT
4437
+ E: WITH_EXCEPT_START (calls EXPR.__exit__)
4438
+ POP_JUMP_IF_TRUE T :
4439
+ RERAISE
4440
+ T: POP_TOP * 3 (remove exception from stack )
4441
+ POP_EXCEPT
4442
+ POP_TOP
4443
+ EXIT:
4403
4444
*/
4445
+
4404
4446
static int
4405
4447
compiler_with (struct compiler * c , stmt_ty s , int pos )
4406
4448
{
4407
- basicblock * block , * finally ;
4449
+ basicblock * block , * final1 , * final2 , * exit ;
4408
4450
withitem_ty item = asdl_seq_GET (s -> v .With .items , pos );
4409
4451
4410
4452
assert (s -> kind == With_kind );
4411
4453
4412
4454
block = compiler_new_block (c );
4413
- finally = compiler_new_block (c );
4414
- if (!block || !finally )
4455
+ final1 = compiler_new_block (c );
4456
+ final2 = compiler_new_block (c );
4457
+ exit = compiler_new_block (c );
4458
+ if (!block || !final1 || !final2 || !exit )
4415
4459
return 0 ;
4416
4460
4417
4461
/* Evaluate EXPR */
4418
4462
VISIT (c , expr , item -> context_expr );
4419
- ADDOP_JREL (c , SETUP_WITH , finally );
4463
+ /* Will push bound __exit__ */
4464
+ ADDOP_JREL (c , SETUP_WITH , final2 );
4420
4465
4421
- /* SETUP_WITH pushes a finally block. */
4466
+ /* SETUP_ASYNC_WITH pushes a finally block. */
4422
4467
compiler_use_next_block (c , block );
4423
- if (!compiler_push_fblock (c , WITH , block , finally )) {
4468
+ if (!compiler_push_fblock (c , WITH , block , final2 )) {
4424
4469
return 0 ;
4425
4470
}
4426
4471
@@ -4439,24 +4484,33 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
4439
4484
else if (!compiler_with (c , s , pos ))
4440
4485
return 0 ;
4441
4486
4442
- /* End of try block; start the finally block */
4487
+ /* End of try block; start the finally blocks */
4443
4488
ADDOP (c , POP_BLOCK );
4444
- ADDOP (c , BEGIN_FINALLY );
4445
4489
compiler_pop_fblock (c , WITH , block );
4446
4490
4447
- compiler_use_next_block (c , finally );
4448
- if (!compiler_push_fblock (c , FINALLY_END , finally , NULL ))
4491
+ /* `finally` block for successful outcome:
4492
+ * call __exit__(None, None, None)
4493
+ */
4494
+ compiler_use_next_block (c , final1 );
4495
+ if (!compiler_push_fblock (c , FINALLY_END , final1 , NULL ))
4449
4496
return 0 ;
4450
4497
4451
- /* Finally block starts; context.__exit__ is on the stack under
4452
- the exception or return information. Just issue our magic
4453
- opcode. */
4454
- ADDOP (c , WITH_CLEANUP_START );
4455
- ADDOP (c , WITH_CLEANUP_FINISH );
4498
+ if (! compiler_call_exit_with_nones ( c ))
4499
+ return 0 ;
4500
+ ADDOP ( c , POP_TOP );
4501
+ compiler_pop_fblock (c , FINALLY_END , final1 );
4502
+ ADDOP_JREL (c , JUMP_FORWARD , exit );
4456
4503
4457
- /* Finally block ends. */
4458
- ADDOP (c , END_FINALLY );
4459
- compiler_pop_fblock (c , FINALLY_END , finally );
4504
+ /* `finally` block for exceptional outcome */
4505
+ compiler_use_next_block (c , final2 );
4506
+ if (!compiler_push_fblock (c , FINALLY_END , final2 , NULL ))
4507
+ return 0 ;
4508
+
4509
+ ADDOP (c , WITH_EXCEPT_START );
4510
+ compiler_with_except_finish (c );
4511
+ compiler_pop_fblock (c , FINALLY_END , final2 );
4512
+
4513
+ compiler_use_next_block (c , exit );
4460
4514
return 1 ;
4461
4515
}
4462
4516
0 commit comments