mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-13 06:01:06 +00:00
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "revive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
hex = { workspace = true }
|
||||
evmil = { workspace = true }
|
||||
ir-tac = { path = "../ir-tac" }
|
||||
@@ -0,0 +1,14 @@
|
||||
use evmil::bytecode::Disassemble;
|
||||
use ir_tac::cfg::{BasicBlockFormatOption, Program};
|
||||
|
||||
fn main() {
|
||||
let hexcode = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
|
||||
let bytecode = hex::decode(hexcode.trim()).unwrap();
|
||||
let instructions = bytecode.disassemble();
|
||||
|
||||
//for instruction in instructions.iter() {
|
||||
// println!("{instruction:?}");
|
||||
//}
|
||||
|
||||
Program::new(instructions).dot(BasicBlockFormatOption::ByteCode);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "ir-tac"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
evmil = { workspace = true }
|
||||
petgraph = { workspace = true }
|
||||
primitive-types = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
@@ -0,0 +1,58 @@
|
||||
use primitive_types::U256;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Kind {
|
||||
Constant(U256),
|
||||
Temporary(usize),
|
||||
Stack,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Address {
|
||||
pub kind: Kind,
|
||||
pub type_hint: Option<Type>,
|
||||
}
|
||||
|
||||
impl From<(Kind, Option<Type>)> for Address {
|
||||
fn from(value: (Kind, Option<Type>)) -> Self {
|
||||
Self {
|
||||
kind: value.0,
|
||||
type_hint: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn new(kind: Kind, type_hint: Option<Type>) -> Self {
|
||||
Self { kind, type_hint }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Type {
|
||||
Int { size: u8 },
|
||||
Bytes { size: u8 },
|
||||
Bool,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn int(size: u8) -> Self {
|
||||
Self::Int { size }
|
||||
}
|
||||
|
||||
fn bytes(size: u8) -> Self {
|
||||
Self::Bytes { size }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Type {
|
||||
fn default() -> Self {
|
||||
Type::Int { size: 32 }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LinearMemory {
|
||||
CallData,
|
||||
Memory,
|
||||
ReturnData,
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
use std::fmt::Write;
|
||||
use std::ops::Range;
|
||||
|
||||
use evmil::bytecode;
|
||||
use petgraph::{
|
||||
dot::{Config, Dot},
|
||||
graph::DiGraph,
|
||||
stable_graph::NodeIndex,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
instruction::{self, Instruction},
|
||||
symbol::SymbolTable,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EvmInstruction {
|
||||
pub bytecode_offset: usize,
|
||||
pub instruction: bytecode::Instruction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BasicBlock {
|
||||
pub entry: Option<Entry>,
|
||||
pub opcodes: Range<usize>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub enum BasicBlockFormatOption {
|
||||
ByteCode,
|
||||
Ir,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
impl BasicBlock {
|
||||
fn format(&self, evm_bytecode: &[EvmInstruction], options: BasicBlockFormatOption) -> String {
|
||||
let offset = evm_bytecode[self.opcodes.start].bytecode_offset;
|
||||
let start = if let Some(Entry::Start) = self.entry {
|
||||
"Start\n".to_string()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let instructions = match options {
|
||||
BasicBlockFormatOption::ByteCode => evm_bytecode[self.opcodes.start..self.opcodes.end]
|
||||
.iter()
|
||||
.fold(String::new(), |mut acc, opcode| {
|
||||
writeln!(&mut acc, "{:?}", opcode.instruction).unwrap();
|
||||
acc
|
||||
}),
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
format!("{start}Offset: 0x{offset:02x}\n---\n{instructions}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Entry {
|
||||
Start,
|
||||
Jumpdest(NodeIndex),
|
||||
Else(NodeIndex),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Jump {
|
||||
Direct,
|
||||
Indirect,
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
pub evm_instructions: Vec<EvmInstruction>,
|
||||
pub cfg: DiGraph<BasicBlock, Jump>,
|
||||
pub symbol_table: SymbolTable,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(bytecode: Vec<bytecode::Instruction>) -> Self {
|
||||
let mut cfg = DiGraph::new();
|
||||
let mut symbol_table = SymbolTable::default();
|
||||
let mut evm_instructions = Vec::with_capacity(bytecode.len());
|
||||
|
||||
let mut current_block = Some(BasicBlock {
|
||||
entry: Some(Entry::Start),
|
||||
..Default::default()
|
||||
});
|
||||
let mut bytecode_offset = 0;
|
||||
|
||||
for (index, opcode) in bytecode.iter().enumerate() {
|
||||
evm_instructions.push(EvmInstruction {
|
||||
bytecode_offset,
|
||||
instruction: opcode.clone(),
|
||||
});
|
||||
bytecode_offset += opcode.length();
|
||||
|
||||
let instructions = instruction::translate(opcode, &mut symbol_table);
|
||||
|
||||
use bytecode::Instruction::*;
|
||||
match opcode {
|
||||
JUMPDEST => {
|
||||
// If we are already in a bb, conclude it
|
||||
let entry = current_block.take().map(|mut node| {
|
||||
node.opcodes.end = index + 1;
|
||||
let entry = node.entry.clone();
|
||||
let node_index = cfg.add_node(node);
|
||||
|
||||
// If the block had an entry, add an edge from the previous block to it
|
||||
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry
|
||||
{
|
||||
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||
}
|
||||
node_index
|
||||
});
|
||||
|
||||
// JUMPDEST implicitly starts a new bb
|
||||
current_block = Some(BasicBlock {
|
||||
entry: entry.map(Entry::Jumpdest),
|
||||
opcodes: Range {
|
||||
start: index + 1,
|
||||
end: index + 1,
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
JUMP | STOP | RETURN | REVERT | INVALID => {
|
||||
// Conclude this bb; if we are not already in a bb we must create a new one
|
||||
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
|
||||
opcodes: Range {
|
||||
start: index,
|
||||
end: index + 1,
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
node.instructions.extend(instructions);
|
||||
node.opcodes.end = index + 1;
|
||||
|
||||
let entry = node.entry.clone();
|
||||
let node_index = cfg.add_node(node);
|
||||
|
||||
// If the block had an entry, add an edge from the previous block to it
|
||||
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
|
||||
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||
}
|
||||
}
|
||||
|
||||
JUMPI => {
|
||||
// Conclude this bb; if we are not already in a bb we must create a new one
|
||||
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
|
||||
opcodes: Range {
|
||||
start: index,
|
||||
end: index + 1,
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
node.instructions.extend(instructions);
|
||||
node.opcodes.end = index + 1;
|
||||
|
||||
let entry = node.entry.clone();
|
||||
let node_index = cfg.add_node(node);
|
||||
|
||||
// If the block had an entry, add an edge from the previous block to it
|
||||
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
|
||||
cfg.add_edge(incoming, node_index, Jump::Direct);
|
||||
}
|
||||
|
||||
// JUMPI implicitly starts a new bb for the else branch
|
||||
current_block = Some(BasicBlock {
|
||||
entry: Some(Entry::Else(node_index)),
|
||||
opcodes: Range {
|
||||
start: index + 1,
|
||||
end: index + 1,
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
_ => current_block
|
||||
.get_or_insert(BasicBlock {
|
||||
opcodes: Range {
|
||||
start: index,
|
||||
end: index + 1,
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.instructions
|
||||
.extend(instructions),
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
evm_instructions,
|
||||
cfg,
|
||||
symbol_table,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dot(&self, format_options: BasicBlockFormatOption) {
|
||||
let get_node_attrs = move |_, (_, node): (_, &BasicBlock)| {
|
||||
format!(
|
||||
"label = \"{}\"",
|
||||
node.format(&self.evm_instructions, format_options)
|
||||
)
|
||||
};
|
||||
|
||||
let dot = Dot::with_attr_getters(
|
||||
&self.cfg,
|
||||
&[Config::EdgeNoLabel, Config::NodeNoLabel],
|
||||
&|_, edge| format!("label = \"{:?}\"", edge.weight()),
|
||||
&get_node_attrs,
|
||||
);
|
||||
|
||||
println!("{dot:?}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
use evmil::bytecode::Instruction as EvmInstruction;
|
||||
use primitive_types::U256;
|
||||
|
||||
use crate::symbol::{Global, Symbol, SymbolTable, Type};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Instruction {
|
||||
/// `x = y op z`
|
||||
BinaryAssign {
|
||||
x: Symbol,
|
||||
y: Symbol,
|
||||
operator: Operator,
|
||||
z: Symbol,
|
||||
},
|
||||
|
||||
/// `x = op y`
|
||||
UnaryAssign {
|
||||
x: Symbol,
|
||||
y: Symbol,
|
||||
operator: Operator,
|
||||
},
|
||||
|
||||
/// `branch target`
|
||||
UncoditionalBranch { target: Symbol },
|
||||
|
||||
/// `branch target if condition`
|
||||
ConditionalBranch { condition: Symbol, target: Symbol },
|
||||
|
||||
/// `call(label, n)`
|
||||
Procedure {
|
||||
symbol: Global,
|
||||
parameters: Vec<Symbol>,
|
||||
},
|
||||
|
||||
/// `x = call(label, n)`
|
||||
Function {
|
||||
symbol: Global,
|
||||
x: Symbol,
|
||||
args: Vec<Symbol>,
|
||||
},
|
||||
|
||||
/// `x = y`
|
||||
Copy { x: Symbol, y: Symbol },
|
||||
|
||||
/// `x[index] = y`
|
||||
IndexedAssign { x: Symbol, index: Symbol, y: Symbol },
|
||||
|
||||
/// `x = y[index]`
|
||||
IndexedCopy { x: Symbol, y: Symbol, index: Symbol },
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Mul,
|
||||
Sub,
|
||||
Div,
|
||||
SDiv,
|
||||
Mod,
|
||||
SMod,
|
||||
AddMod,
|
||||
MulMod,
|
||||
Exp,
|
||||
SignExtend,
|
||||
|
||||
LessThat,
|
||||
GreaterThan,
|
||||
SignedLessThan,
|
||||
SignedGreaterThan,
|
||||
Eq,
|
||||
IsZero,
|
||||
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Not,
|
||||
Byte,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
ShiftArithmeticRight,
|
||||
}
|
||||
|
||||
struct StackPop {
|
||||
decrement: Instruction,
|
||||
load: Instruction,
|
||||
}
|
||||
|
||||
/// Pop a value from the stack.
|
||||
///
|
||||
/// Returns 2 `Instruction`: Decrementing the stack pointer and the value copy.
|
||||
fn stack_pop(symbol_table: &mut SymbolTable) -> StackPop {
|
||||
let decrement = decrement_stack_height(symbol_table);
|
||||
|
||||
let load = Instruction::IndexedCopy {
|
||||
x: symbol_table.temporary(None),
|
||||
y: symbol_table.global(Global::Stack),
|
||||
index: symbol_table.global(Global::StackHeight),
|
||||
};
|
||||
|
||||
StackPop { decrement, load }
|
||||
}
|
||||
|
||||
/// Decrease the stack height by one.
|
||||
fn decrement_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
|
||||
Instruction::BinaryAssign {
|
||||
x: symbol_table.global(Global::StackHeight),
|
||||
y: symbol_table.global(Global::StackHeight),
|
||||
operator: Operator::Sub,
|
||||
z: symbol_table.constant(U256::one(), Some(Type::Int(4))),
|
||||
}
|
||||
}
|
||||
|
||||
struct StackPush {
|
||||
assign: Instruction,
|
||||
increment: Instruction,
|
||||
}
|
||||
|
||||
/// Push a `value` to the stack.
|
||||
///
|
||||
/// Returns 2 `Instruction`: the value assign and the stack height increase.
|
||||
fn stack_push(symbol_table: &mut SymbolTable, value: Symbol) -> StackPush {
|
||||
let assign = Instruction::IndexedAssign {
|
||||
x: symbol_table.global(Global::Stack),
|
||||
index: symbol_table.global(Global::StackHeight),
|
||||
y: value,
|
||||
};
|
||||
let increment = increment_stack_height(symbol_table);
|
||||
|
||||
StackPush { assign, increment }
|
||||
}
|
||||
|
||||
/// Increment the stack height by one.
|
||||
fn increment_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
|
||||
Instruction::BinaryAssign {
|
||||
x: symbol_table.global(Global::StackHeight),
|
||||
y: symbol_table.global(Global::StackHeight),
|
||||
operator: Operator::Add,
|
||||
z: symbol_table.constant(U256::one(), Some(Type::Int(4))),
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn target_address(&self) -> Symbol {
|
||||
match self {
|
||||
Instruction::Copy { x, .. } => *x,
|
||||
Instruction::IndexedAssign { x, .. } => *x,
|
||||
Instruction::IndexedCopy { x, .. } => *x,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower an EVM instruction into corresponding 3AC instructions.
|
||||
pub fn translate(opcode: &EvmInstruction, symbol_table: &mut SymbolTable) -> Vec<Instruction> {
|
||||
use EvmInstruction::*;
|
||||
match opcode {
|
||||
JUMPDEST => Vec::new(),
|
||||
|
||||
PUSH(bytes) => {
|
||||
let type_hint = Some(Type::Bytes(bytes.len()));
|
||||
let value = symbol_table.constant(U256::from_big_endian(bytes), type_hint);
|
||||
let push = stack_push(symbol_table, value);
|
||||
|
||||
vec![push.assign, push.increment]
|
||||
}
|
||||
|
||||
POP => vec![decrement_stack_height(symbol_table)],
|
||||
|
||||
MSTORE => {
|
||||
let offset = stack_pop(symbol_table);
|
||||
let value = stack_pop(symbol_table);
|
||||
|
||||
let store = Instruction::IndexedAssign {
|
||||
x: symbol_table.global(Global::Memory),
|
||||
index: offset.load.target_address(),
|
||||
y: value.load.target_address(),
|
||||
};
|
||||
|
||||
vec![
|
||||
offset.decrement,
|
||||
offset.load,
|
||||
value.decrement,
|
||||
value.load,
|
||||
store,
|
||||
]
|
||||
}
|
||||
|
||||
JUMP => {
|
||||
let target = stack_pop(symbol_table);
|
||||
|
||||
let jump = Instruction::UncoditionalBranch {
|
||||
target: target.load.target_address(),
|
||||
};
|
||||
|
||||
vec![target.decrement, target.load, jump]
|
||||
}
|
||||
|
||||
RETURN => {
|
||||
let offset = stack_pop(symbol_table);
|
||||
let size = stack_pop(symbol_table);
|
||||
|
||||
let procedure = Instruction::Procedure {
|
||||
symbol: Global::Return,
|
||||
parameters: vec![offset.load.target_address(), size.load.target_address()],
|
||||
};
|
||||
|
||||
vec![
|
||||
offset.decrement,
|
||||
offset.load,
|
||||
size.decrement,
|
||||
size.load,
|
||||
procedure,
|
||||
]
|
||||
}
|
||||
|
||||
CALLDATACOPY => {
|
||||
let destination_offset = stack_pop(symbol_table);
|
||||
let offset = stack_pop(symbol_table);
|
||||
let size = stack_pop(symbol_table);
|
||||
|
||||
let parameters = vec![
|
||||
destination_offset.load.target_address(),
|
||||
offset.load.target_address(),
|
||||
size.load.target_address(),
|
||||
];
|
||||
|
||||
let procedure = Instruction::Procedure {
|
||||
symbol: Global::MemoryCopy,
|
||||
parameters,
|
||||
};
|
||||
|
||||
vec![
|
||||
destination_offset.decrement,
|
||||
destination_offset.load,
|
||||
offset.decrement,
|
||||
offset.load,
|
||||
size.decrement,
|
||||
size.load,
|
||||
procedure,
|
||||
]
|
||||
}
|
||||
|
||||
CALLDATALOAD => {
|
||||
let index = stack_pop(symbol_table);
|
||||
|
||||
let value = Instruction::IndexedCopy {
|
||||
x: symbol_table.temporary(None),
|
||||
y: symbol_table.global(Global::CallData),
|
||||
index: index.load.target_address(),
|
||||
};
|
||||
|
||||
let push = stack_push(symbol_table, value.target_address());
|
||||
|
||||
vec![
|
||||
index.decrement,
|
||||
index.load,
|
||||
value,
|
||||
push.assign,
|
||||
push.increment,
|
||||
]
|
||||
}
|
||||
|
||||
//_ => todo!("{opcode}"),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use evmil::bytecode;
|
||||
use primitive_types::U256;
|
||||
|
||||
use crate::{
|
||||
instruction::Operator,
|
||||
symbol::{Global, Kind, Symbol, Type},
|
||||
};
|
||||
|
||||
use super::Instruction;
|
||||
|
||||
#[test]
|
||||
fn lower_push_works() {
|
||||
let mut symbol_table = Default::default();
|
||||
|
||||
let opcode = bytecode::Instruction::PUSH(vec![0x01]);
|
||||
let result = super::translate(&opcode, &mut symbol_table);
|
||||
|
||||
let expected = vec![
|
||||
Instruction::IndexedAssign {
|
||||
x: Symbol {
|
||||
kind: Kind::Label(Global::Stack),
|
||||
type_hint: Type::Word,
|
||||
},
|
||||
index: Symbol {
|
||||
kind: Kind::Label(Global::StackHeight),
|
||||
type_hint: Type::Int(4),
|
||||
},
|
||||
y: Symbol {
|
||||
kind: Kind::Constant(U256::one()),
|
||||
type_hint: Type::Bytes(1),
|
||||
},
|
||||
},
|
||||
Instruction::BinaryAssign {
|
||||
x: Symbol {
|
||||
kind: Kind::Label(Global::StackHeight),
|
||||
type_hint: Type::Int(4),
|
||||
},
|
||||
y: Symbol {
|
||||
kind: Kind::Label(Global::StackHeight),
|
||||
type_hint: Type::Int(4),
|
||||
},
|
||||
operator: Operator::Add,
|
||||
z: Symbol {
|
||||
kind: Kind::Constant(U256::one()),
|
||||
type_hint: Type::Int(4),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
pub mod cfg;
|
||||
pub mod instruction;
|
||||
pub mod symbol;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn it_works() {}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
use indexmap::IndexSet;
|
||||
use primitive_types::U256;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum Kind {
|
||||
Constant(U256),
|
||||
Temporary(usize),
|
||||
Label(Global),
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
pub fn from_be_bytes(bytes: &[u8]) -> Self {
|
||||
Self::Constant(U256::from_big_endian(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct Symbol {
|
||||
pub kind: Kind,
|
||||
pub type_hint: Type,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
fn global(symbol: Global) -> Self {
|
||||
let type_hint = match symbol {
|
||||
Global::StackHeight => Type::Int(4),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
Self::new(Kind::Label(symbol), type_hint)
|
||||
}
|
||||
|
||||
fn new(kind: Kind, type_hint: Type) -> Self {
|
||||
Self { kind, type_hint }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Default, Clone, Copy)]
|
||||
pub enum Type {
|
||||
#[default]
|
||||
Word,
|
||||
Int(usize),
|
||||
Bytes(usize),
|
||||
Bool,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn pointer() -> Self {
|
||||
Self::Int(4)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum Global {
|
||||
/// Pointer
|
||||
Stack,
|
||||
/// Stack height variable
|
||||
StackHeight,
|
||||
|
||||
/// Pointer
|
||||
CallData,
|
||||
/// Pointer
|
||||
Memory,
|
||||
/// Pointer
|
||||
ReturnData,
|
||||
|
||||
/// Low level `memcpy` like function
|
||||
MemoryCopy,
|
||||
|
||||
// EVM
|
||||
Sha3,
|
||||
Address,
|
||||
CallDataLoad,
|
||||
CallDataSize,
|
||||
CallDataCopy,
|
||||
CodeSize,
|
||||
CodeCopy,
|
||||
GasPrice,
|
||||
ExtCodeSize,
|
||||
ExtCodeCopy,
|
||||
ReturnDataSize,
|
||||
ReturnDataCopy,
|
||||
ExtCodeHash,
|
||||
BlockHash,
|
||||
Coinbase,
|
||||
Timestamp,
|
||||
BlockNumber,
|
||||
PrevRanDao,
|
||||
GasLimit,
|
||||
ChainId,
|
||||
SelfBalance,
|
||||
BaseFee,
|
||||
SLoad,
|
||||
SStore,
|
||||
Gas,
|
||||
Create,
|
||||
Create2,
|
||||
Call,
|
||||
StaticCall,
|
||||
DelegateCall,
|
||||
CallCode,
|
||||
Return,
|
||||
Stop,
|
||||
Revert,
|
||||
SelfDestruct,
|
||||
Event,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SymbolTable {
|
||||
symbols: IndexSet<Symbol>,
|
||||
nonce: usize,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
fn next(&mut self) -> usize {
|
||||
let current = self.nonce;
|
||||
self.nonce += 1;
|
||||
current
|
||||
}
|
||||
|
||||
pub fn temporary(&mut self, type_hint: Option<Type>) -> Symbol {
|
||||
let id = self.next();
|
||||
let symbol = Symbol::new(Kind::Temporary(id), type_hint.unwrap_or_default());
|
||||
assert!(self.symbols.insert(symbol));
|
||||
|
||||
symbol
|
||||
}
|
||||
|
||||
pub fn constant(&mut self, value: U256, type_hint: Option<Type>) -> Symbol {
|
||||
let symbol = Symbol::new(Kind::Constant(value), type_hint.unwrap_or_default());
|
||||
self.symbols.insert(symbol);
|
||||
|
||||
symbol
|
||||
}
|
||||
|
||||
pub fn global(&mut self, label: Global) -> Symbol {
|
||||
let symbol = Symbol::global(label);
|
||||
self.symbols.insert(symbol);
|
||||
|
||||
symbol
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user