forked from neo-project/neo-devpack-dotnet
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathJumpTarget.cs
113 lines (107 loc) · 5.57 KB
/
JumpTarget.cs
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
using Neo.SmartContract;
using Neo.VM;
using System;
using System.Collections.Generic;
using System.Linq;
using static Neo.Optimizer.OpCodeTypes;
using static Neo.VM.OpCode;
namespace Neo.Optimizer
{
static class JumpTarget
{
public static bool SingleJumpInOperand(Instruction instruction) => SingleJumpInOperand(instruction.OpCode);
public static bool SingleJumpInOperand(OpCode opcode)
{
if (conditionalJump.Contains(opcode)) return true;
if (conditionalJump_L.Contains(opcode)) return true;
if (unconditionalJump.Contains(opcode)) return true;
if (callWithJump.Contains(opcode)) return true;
if (opcode == ENDTRY || opcode == ENDTRY_L || opcode == PUSHA) return true;
return false;
}
public static bool DoubleJumpInOperand(Instruction instruction) => DoubleJumpInOperand(instruction.OpCode);
public static bool DoubleJumpInOperand(OpCode opcode) => (opcode == TRY || opcode == TRY_L);
public static int ComputeJumpTarget(int addr, Instruction instruction)
{
if (conditionalJump.Contains(instruction.OpCode))
return addr + instruction.TokenI8;
if (conditionalJump_L.Contains(instruction.OpCode))
return addr + instruction.TokenI32;
return instruction.OpCode switch
{
JMP or CALL or ENDTRY => addr + instruction.TokenI8,
PUSHA or JMP_L or CALL_L or ENDTRY_L => addr + instruction.TokenI32,
CALLA => throw new NotImplementedException("CALLA is dynamic; not supported"),
_ => throw new NotImplementedException($"Unknown instruction {instruction.OpCode}"),
};
}
public static (int catchTarget, int finallyTarget) ComputeTryTarget(int addr, Instruction instruction)
{
return instruction.OpCode switch
{
TRY =>
(instruction.TokenI8 == 0 ? -1 : addr + instruction.TokenI8,
instruction.TokenI8_1 == 0 ? -1 : addr + instruction.TokenI8_1),
TRY_L =>
(instruction.TokenI32 == 0 ? -1 : addr + instruction.TokenI32,
instruction.TokenI32_1 == 0 ? -1 : addr + instruction.TokenI32_1),
_ => throw new NotImplementedException($"Unknown instruction {instruction.OpCode}"),
};
}
public static (Dictionary<Instruction, Instruction>,
Dictionary<Instruction, (Instruction, Instruction)>,
Dictionary<Instruction, HashSet<Instruction>>)
FindAllJumpAndTrySourceToTargets(NefFile nef)
{
Script script = nef.Script;
return FindAllJumpAndTrySourceToTargets(script);
}
public static (Dictionary<Instruction, Instruction>,
Dictionary<Instruction, (Instruction, Instruction)>,
Dictionary<Instruction, HashSet<Instruction>>)
FindAllJumpAndTrySourceToTargets(Script script) => FindAllJumpAndTrySourceToTargets(script.EnumerateInstructions().ToList());
public static (
Dictionary<Instruction, Instruction>, // jump source to target
Dictionary<Instruction, (Instruction, Instruction)>, // try source to targets
Dictionary<Instruction, HashSet<Instruction>> // target to source
)
FindAllJumpAndTrySourceToTargets(List<(int, Instruction)> addressAndInstructionsList)
{
Dictionary<int, Instruction> addressToInstruction = new();
foreach ((int a, Instruction i) in addressAndInstructionsList)
addressToInstruction.Add(a, i);
Dictionary<Instruction, Instruction> jumpSourceToTargets = new();
Dictionary<Instruction, (Instruction, Instruction)> trySourceToTargets = new();
Dictionary<Instruction, HashSet<Instruction>> targetToSources = new();
foreach ((int a, Instruction i) in addressAndInstructionsList)
{
if (SingleJumpInOperand(i))
{
Instruction target = addressToInstruction[ComputeJumpTarget(a, i)];
jumpSourceToTargets.TryAdd(i, target);
if (!targetToSources.TryGetValue(target, out HashSet<Instruction>? sources)) sources = new();
sources.Add(i);
}
if (i.OpCode == TRY)
{
(Instruction t1, Instruction t2) = (addressToInstruction[a + i.TokenI8], addressToInstruction[a + i.TokenI8_1]);
trySourceToTargets.TryAdd(i, (t1, t2));
if (!targetToSources.TryGetValue(t1, out HashSet<Instruction>? sources1)) sources1 = new();
sources1.Add(i);
if (!targetToSources.TryGetValue(t2, out HashSet<Instruction>? sources2)) sources2 = new();
sources2.Add(i);
}
if (i.OpCode == TRY_L)
{
(Instruction t1, Instruction t2) = (addressToInstruction[a + i.TokenI32], addressToInstruction[a + i.TokenI32_1]);
trySourceToTargets.TryAdd(i, (t1, t2));
if (!targetToSources.TryGetValue(t1, out HashSet<Instruction>? sources1)) sources1 = new();
sources1.Add(i);
if (!targetToSources.TryGetValue(t2, out HashSet<Instruction>? sources2)) sources2 = new();
sources2.Add(i);
}
}
return (jumpSourceToTargets, trySourceToTargets, targetToSources);
}
}
}