huff_neo_codegen/irgen/
constants.rs1use crate::Codegen;
2use alloy_primitives::U256;
3use huff_neo_utils::ast::span::AstSpan;
4use huff_neo_utils::prelude::{CodegenError, CodegenErrorKind, ConstVal, ConstantDefinition, Contract, PushValue, str_to_bytes32};
5
6pub fn constant_gen(name: &str, contract: &Contract, ir_byte_span: &AstSpan) -> Result<Option<PushValue>, CodegenError> {
9 let constant = lookup_constant(name, contract, ir_byte_span)?;
10
11 tracing::info!(target: "codegen", "FOUND CONSTANT DEFINITION: {}", constant.name);
15 let push_value = match &constant.value {
16 ConstVal::Bytes(bytes) => {
17 let hex_literal = str_to_bytes32(&bytes.as_str());
18 Some(PushValue::from(hex_literal))
19 }
20 ConstVal::String(_s) => {
21 tracing::error!(target: "codegen", "STRING CONSTANT \"{}\" CANNOT BE USED DIRECTLY", constant.name);
23 return Err(CodegenError {
24 kind: CodegenErrorKind::StringConstantNotBytes(format!(
25 "String constant [{}] cannot be pushed directly. Use __BYTES([{}]) to convert it to bytes.",
26 name, name
27 )),
28 span: ir_byte_span.clone_box(),
29 token: None,
30 });
31 }
32 ConstVal::StoragePointer(sp) => Some(PushValue::from(sp)),
33 ConstVal::BuiltinFunctionCall(bf) => Some(Codegen::gen_builtin_bytecode(contract, bf, ir_byte_span.clone())?),
34 ConstVal::Expression(expr) => {
35 tracing::info!(target: "codegen", "EVALUATING CONSTANT EXPRESSION FOR \"{}\"", constant.name);
36 let evaluated = contract.evaluate_constant_expression(expr)?;
37 Some(PushValue::from(evaluated))
38 }
39 ConstVal::Noop => {
40 tracing::info!(target: "codegen", "CONSTANT \"{}\" IS __NOOP - GENERATING NO BYTECODE", constant.name);
41 None }
43 ConstVal::FreeStoragePointer(fsp) => {
44 tracing::error!(target: "codegen", "STORAGE POINTERS INCORRECTLY DERIVED FOR \"{:?}\"", fsp);
47 return Err(CodegenError { kind: CodegenErrorKind::StoragePointersNotDerived, span: constant.span.clone_box(), token: None });
48 }
49 };
50
51 Ok(push_value)
52}
53
54pub fn lookup_constant(name: &str, contract: &Contract, ir_byte_span: &AstSpan) -> Result<ConstantDefinition, CodegenError> {
56 let constants = contract.constants.lock().map_err(|_| CodegenError::new(CodegenErrorKind::LockingError, AstSpan(vec![]), None))?;
58 let constant = if let Some(m) = constants.iter().find(|const_def| const_def.name.eq(&name)) {
59 m.clone()
60 } else {
61 tracing::error!(target: "codegen", "MISSING CONSTANT DEFINITION \"{}\"", name);
62
63 return Err(CodegenError {
64 kind: CodegenErrorKind::MissingConstantDefinition(name.to_string()),
65 span: ir_byte_span.clone_box(),
66 token: None,
67 });
68 };
69 Ok(constant)
70}
71
72pub fn evaluate_constant_value(name: &str, contract: &Contract, span: &AstSpan) -> Result<usize, CodegenError> {
83 let constant = lookup_constant(name, contract, span)?;
84
85 let literal = match &constant.value {
86 ConstVal::Bytes(bytes) => str_to_bytes32(&bytes.as_str()),
87 ConstVal::Expression(expr) => {
88 tracing::info!(target: "codegen", "EVALUATING CONSTANT EXPRESSION \"{}\" FOR NUMERIC VALUE", constant.name);
89 contract.evaluate_constant_expression(expr)?
90 }
91 ConstVal::StoragePointer(lit) => *lit,
92 ConstVal::String(_) => {
93 return Err(CodegenError {
94 kind: CodegenErrorKind::InvalidArguments(format!(
95 "Constant [{}] is a string which cannot be evaluated to a numeric value",
96 name
97 )),
98 span: span.clone_box(),
99 token: None,
100 });
101 }
102 ConstVal::Noop => {
103 return Err(CodegenError {
104 kind: CodegenErrorKind::InvalidArguments(format!(
105 "Constant [{}] is __NOOP which has no numeric value and cannot be used as a bytecode position",
106 name
107 )),
108 span: span.clone_box(),
109 token: None,
110 });
111 }
112 ConstVal::BuiltinFunctionCall(bf) => {
113 return Err(CodegenError {
114 kind: CodegenErrorKind::InvalidArguments(format!(
115 "Constant [{}] is a builtin function call ({}) which cannot be evaluated to a numeric position. \
116 Only literal values, arithmetic expressions, and storage pointers are supported for __ASSERT_PC",
117 name, bf.kind
118 )),
119 span: span.clone_box(),
120 token: None,
121 });
122 }
123 ConstVal::FreeStoragePointer(_) => {
124 return Err(CodegenError { kind: CodegenErrorKind::StoragePointersNotDerived, span: span.clone_box(), token: None });
125 }
126 };
127
128 let value = U256::from_be_bytes(literal);
130
131 if value > U256::from(usize::MAX) {
136 return Err(CodegenError {
137 kind: CodegenErrorKind::InvalidArguments(format!(
138 "Constant [{}] value ({}) exceeds maximum bytecode position size ({}). \
139 Bytecode positions must fit within a usize for the target platform",
140 name,
141 value,
142 usize::MAX
143 )),
144 span: span.clone_box(),
145 token: None,
146 });
147 }
148
149 Ok(value.to::<usize>())
150}