1 //: Make sure that we never jump from one function to within another.
 2 //:
 3 //: (The check for label types already ensures we can't jump to the start of
 4 //: another function.)
 5 
 6 :(scenario jump_to_different_function)
 7 % Hide_errors = true;
 8 == 0x1
 9 fn1:
10   7e/jump-if $target/disp8
11 fn2:
12  $target:
13 +error: '7e/jump-if $target/disp8' in function 'fn1': jump to within another function 'fn2' is a *really* bad idea
14 
15 :(before "Rewrite Labels(segment code)")
16 check_local_jumps(code);
17 if (trace_contains_errors()) return;
18 :(code)
19 void check_local_jumps(const segment& code) {
20   map</*jump target*/string, /*containing call target*/string> function;
21   compute_function_target(code, function);
22   if (trace_contains_errors()) return;
23   string current_function;
24   for (int i = 0;  i < SIZE(code.lines);  ++i) {
25     const line& inst = code.lines.at(i);
26     if (SIZE(inst.words) == 1 && is_label(inst.words.at(0))) {
27       // label definition
28       if (inst.words.at(0).data.at(0) != '$')
29         current_function = drop_last(inst.words.at(0).data);
30     }
31     else if (is_jump(inst)) {
32       const word& target = inst.words.at(first_operand(inst));
33       if (!contains_key(function, target.data)) continue;  // error/warning handled elsewhere
34       if (get(function, target.data) == current_function) continue;
35       raise << "'" << to_string(inst) << "' in function '" << current_function << "': jump to within another function '" << get(function, target.data) << "' is a *really* bad idea\n" << end();
36       return;
37     }
38   }
39 }
40 
41 void compute_function_target(const segment& code, map<string, string>& out) {
42   string current_function;
43   for (int i = 0;  i < SIZE(code.lines);  ++i) {
44     const line& inst = code.lines.at(i);
45     if (SIZE(inst.words) != 1) continue;
46     const word& curr = inst.words.at(0);
47     if (!is_label(curr)) continue;
48     const string& label = drop_last(curr.data);
49     if (label.at(0) != '$') {
50       current_function = label;
51       continue;
52     }
53     if (contains_key(out, label)) {
54       raise << "duplicate label '" << label << "'\n" << end();
55       return;
56     }
57     // current_function can be empty! if so that would be 'main'.
58     put(out, label, current_function);
59   }
60 }