Skip to content

Commit b428976

Browse files
authored
Merge pull request #599 from daiche330-png/wip/python311-exception-opcodes-20260405
Handle Python 3.11 exception opcodes in AST builder
2 parents a05ddec + 413fbd8 commit b428976

4 files changed

Lines changed: 132 additions & 3 deletions

File tree

ASTree.cpp

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
9393
bool else_pop = false;
9494
bool need_try = false;
9595
bool variable_annotations = false;
96+
std::vector<PycExceptionTableEntry> exception_entries;
97+
size_t next_exception_entry = 0;
98+
99+
if (mod->verCompare(3, 11) >= 0) {
100+
exception_entries = code->exceptionTableEntries();
101+
}
96102

97103
while (!source.atEof()) {
98104
#if defined(BLOCK_DEBUG) || defined(STACK_DEBUG)
@@ -108,6 +114,84 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
108114
fprintf(stderr, "\n");
109115
#endif
110116

117+
while (next_exception_entry < exception_entries.size()
118+
&& exception_entries[next_exception_entry].start_offset < pos) {
119+
next_exception_entry++;
120+
}
121+
122+
if (next_exception_entry < exception_entries.size()) {
123+
const auto& entry = exception_entries[next_exception_entry];
124+
if (entry.start_offset == pos
125+
&& entry.stack_depth == 0
126+
&& !entry.push_lasti) {
127+
if (curblock->blktype() == ASTBlock::BLK_CONTAINER) {
128+
curblock.cast<ASTContainerBlock>()->setExcept(entry.target);
129+
} else {
130+
PycRef<ASTBlock> next = new ASTContainerBlock(0, entry.target);
131+
blocks.push(next.cast<ASTBlock>());
132+
curblock = blocks.top();
133+
}
134+
135+
stack_hist.push(stack);
136+
PycRef<ASTBlock> tryblock = new ASTBlock(ASTBlock::BLK_TRY, entry.target, true);
137+
blocks.push(tryblock.cast<ASTBlock>());
138+
curblock = blocks.top();
139+
next_exception_entry++;
140+
}
141+
}
142+
143+
if (curblock->blktype() == ASTBlock::BLK_TRY
144+
&& curblock->end() == pos
145+
&& blocks.size() > 1) {
146+
PycRef<ASTBlock> prev = curblock;
147+
blocks.pop();
148+
curblock = blocks.top();
149+
150+
if (curblock->blktype() == ASTBlock::BLK_CONTAINER
151+
&& curblock.cast<ASTContainerBlock>()->hasExcept()) {
152+
if (!stack_hist.empty()) {
153+
stack = stack_hist.top();
154+
stack_hist.pop();
155+
}
156+
157+
curblock->append(prev.cast<ASTNode>());
158+
stack_hist.push(stack);
159+
160+
PycRef<ASTBlock> except = new ASTCondBlock(ASTBlock::BLK_EXCEPT, 0, NULL, false);
161+
except->init();
162+
blocks.push(except);
163+
curblock = blocks.top();
164+
} else {
165+
blocks.push(prev);
166+
curblock = prev;
167+
}
168+
}
169+
170+
if (curblock->blktype() == ASTBlock::BLK_EXCEPT
171+
&& curblock->end() == pos
172+
&& blocks.size() > 1) {
173+
PycRef<ASTBlock> prev = curblock;
174+
blocks.pop();
175+
curblock = blocks.top();
176+
177+
if (!stack_hist.empty()) {
178+
stack = stack_hist.top();
179+
stack_hist.pop();
180+
}
181+
182+
if (prev->size() != 0) {
183+
curblock->append(prev.cast<ASTNode>());
184+
}
185+
186+
if (curblock->blktype() == ASTBlock::BLK_CONTAINER
187+
&& !curblock.cast<ASTContainerBlock>()->hasFinally()) {
188+
PycRef<ASTBlock> cont = curblock;
189+
blocks.pop();
190+
curblock = blocks.top();
191+
curblock->append(cont.cast<ASTNode>());
192+
}
193+
}
194+
111195
curpos = pos;
112196
bc_next(source, mod, opcode, operand, pos);
113197

@@ -1093,15 +1177,17 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
10931177

10941178
if (cond.type() == ASTNode::NODE_COMPARE
10951179
&& cond.cast<ASTCompare>()->op() == ASTCompare::CMP_EXCEPTION) {
1180+
int except_end = offs;
10961181
if (curblock->blktype() == ASTBlock::BLK_EXCEPT
10971182
&& curblock.cast<ASTCondBlock>()->cond() == NULL) {
1183+
except_end = curblock->end();
10981184
blocks.pop();
10991185
curblock = blocks.top();
11001186

11011187
stack_hist.pop();
11021188
}
11031189

1104-
ifblk = new ASTCondBlock(ASTBlock::BLK_EXCEPT, offs, cond.cast<ASTCompare>()->right(), false);
1190+
ifblk = new ASTCondBlock(ASTBlock::BLK_EXCEPT, except_end, cond.cast<ASTCompare>()->right(), false);
11051191
} else if (curblock->blktype() == ASTBlock::BLK_ELSE
11061192
&& curblock->size() == 0) {
11071193
/* Collapse into elif statement */
@@ -1370,8 +1456,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
13701456
} else if (prev->blktype() == ASTBlock::BLK_TRY
13711457
&& prev->end() < pos+offs) {
13721458
/* Need to add an except/finally block */
1373-
stack = stack_hist.top();
1374-
stack.pop();
1459+
if (!stack_hist.empty()) {
1460+
stack = stack_hist.top();
1461+
stack_hist.pop();
1462+
}
13751463

13761464
if (blocks.top()->blktype() == ASTBlock::BLK_CONTAINER) {
13771465
PycRef<ASTContainerBlock> cont = blocks.top().cast<ASTContainerBlock>();
@@ -1689,6 +1777,19 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
16891777
case Pyc::POP_EXCEPT:
16901778
/* Do nothing. */
16911779
break;
1780+
case Pyc::PUSH_EXC_INFO:
1781+
/* Python 3.11+: pushes exception info tuple. We ignore here to keep decompilation going. */
1782+
break;
1783+
case Pyc::CHECK_EXC_MATCH:
1784+
{
1785+
/* Python 3.11+: compares exception against handler type. */
1786+
PycRef<ASTNode> right = stack.top();
1787+
stack.pop();
1788+
PycRef<ASTNode> left = stack.top();
1789+
stack.pop();
1790+
stack.push(new ASTCompare(left, right, ASTCompare::CMP_EXCEPTION));
1791+
}
1792+
break;
16921793
case Pyc::END_FOR:
16931794
{
16941795
stack.pop();
@@ -1830,6 +1931,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
18301931
}
18311932
}
18321933
break;
1934+
case Pyc::RERAISE:
1935+
case Pyc::RERAISE_A:
1936+
/* Python 3.11 cleanup opcode. */
1937+
break;
18331938
case Pyc::RETURN_VALUE:
18341939
case Pyc::INSTRUMENTED_RETURN_VALUE_A:
18351940
{
@@ -1920,6 +2025,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
19202025
curblock = blocks.top();
19212026
}
19222027
break;
2028+
case Pyc::BEFORE_WITH:
2029+
/* Python 3.11: setup for with block; ignore. */
2030+
break;
19232031
case Pyc::WITH_CLEANUP:
19242032
case Pyc::WITH_CLEANUP_START:
19252033
{
479 Bytes
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def handle_specific(flag):
2+
try:
3+
if flag:
4+
raise ValueError('boom')
5+
except ValueError:
6+
return 'value'
7+
return 'ok'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
def handle_specific ( flag ) : <EOL>
2+
<INDENT>
3+
try : <EOL>
4+
<INDENT>
5+
if flag : <EOL>
6+
<INDENT>
7+
raise ValueError ( 'boom' ) <EOL>
8+
<OUTDENT>
9+
<OUTDENT>
10+
except ValueError : <EOL>
11+
<INDENT>
12+
return 'value' <EOL>
13+
<OUTDENT>
14+
return 'ok' <EOL>

0 commit comments

Comments
 (0)