1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use super::super::{HighLevelEmitter, LoopContext};
impl HighLevelEmitter {
pub(crate) fn advance_to(&mut self, offset: usize) {
let entering_else = self.else_targets.contains_key(&offset);
if let Some(count) = self.pending_closers.remove(&offset) {
for _ in 0..count {
self.statements.push("}".into());
}
if entering_else {
// Before restoring else-entry state, capture the then-branch
// terminal stack for the upcoming merge closer (if any). This
// allows merge-time recovery when the else branch terminates.
if let Some((&merge_offset, _)) = self.pending_closers.range((offset + 1)..).next()
{
self.branch_saved_stacks
.entry(merge_offset)
.or_insert_with(|| self.stack.clone());
}
// Entering an else block: its entry stack must match the
// pre-branch stack snapshot, not the stack mutated by the
// then-branch instructions emitted just above.
if let Some(saved) = self.branch_saved_stacks.get(&offset).cloned() {
self.stack = saved;
}
} else {
self.reconcile_branch_stack_at(offset);
}
}
// Restore try block's exit stack at the resume point after a
// try-catch. This must live outside the pending_closers gate because
// the catch closer may be registered at the finally offset rather than
// the ENDTRY target offset.
if let Some(saved) = self.try_exit_stacks.remove(&offset) {
self.stack = saved;
}
self.close_loops_at(offset);
self.open_exception_handlers_at(offset);
self.open_structural_headers_at(offset);
}
fn reconcile_branch_stack_at(&mut self, offset: usize) {
// Merge point after an if/else (or plain if). Reconcile the stack
// states from both branches.
let Some(saved) = self.branch_saved_stacks.remove(&offset) else {
return;
};
let pre_depth = self.pre_branch_stack_depth.remove(&offset).unwrap_or(0);
if self.stack.is_empty() && !saved.is_empty() {
self.stack = saved;
} else if !self.stack.is_empty()
&& !saved.is_empty()
&& self.stack.len() == saved.len()
&& self.stack.len() > pre_depth
{
let close_idx = self
.statements
.iter()
.rposition(|s| s.trim() == "}")
.unwrap_or(self.statements.len());
let mut inserts = Vec::new();
for (current, saved_name) in self.stack.iter().zip(saved.iter()).skip(pre_depth) {
if current != saved_name {
inserts.push(format!("let {} = {};", saved_name, current));
}
}
for (j, stmt) in inserts.into_iter().enumerate() {
self.statements.insert(close_idx + j, stmt);
}
self.stack = saved;
}
}
fn open_exception_handlers_at(&mut self, offset: usize) {
// Catch/finally MUST be emitted before else so that exception handlers
// appear as siblings of the try block rather than nesting inside an
// else branch when both targets share the same offset.
if let Some(count) = self.catch_targets.remove(&offset) {
// Save the try block's exit stack before the catch handler clears
// it. This lets us restore the stack at the resume point after the
// try-catch so values carried through ENDTRY are not lost.
if let Some(resume) = self.try_catch_resume.remove(&offset) {
if !self.stack.is_empty() {
self.try_exit_stacks
.entry(resume)
.or_insert_with(|| self.stack.clone());
}
}
for _ in 0..count {
self.statements.push("catch {".into());
}
// Neo VM enters catch handlers with the exception object on top of
// an unwound evaluation stack.
self.stack.clear();
self.stack.push("exception".into());
}
if let Some(count) = self.finally_targets.remove(&offset) {
for _ in 0..count {
self.statements.push("finally {".into());
}
}
}
fn open_structural_headers_at(&mut self, offset: usize) {
if let Some(count) = self.else_targets.remove(&offset) {
for _ in 0..count {
self.statements.push("else {".into());
}
// Keep the saved pre-branch snapshot until the else block closes.
// If the else branch terminates, merge-time restoration still
// needs this snapshot.
}
if let Some(entries) = self.do_while_headers.remove(&offset) {
for entry in entries {
self.statements.push("do {".into());
self.active_do_while_tails.insert(entry.tail_offset);
self.loop_stack.push(LoopContext {
break_offset: entry.break_offset,
continue_offset: entry.tail_offset,
});
}
}
if let Some(headers) = self.pending_if_headers.remove(&offset) {
for header in headers {
self.statements.push(header);
}
}
if self.transfer_labels.remove(&offset) {
self.statements
.push(format!("{}:", Self::transfer_label_name(offset)));
}
}
}