Skip to content

Commit 0fe9d8d

Browse files
authored
Pass constant as parameter for buildin (#32)
1 parent 77f7d39 commit 0fe9d8d

File tree

6 files changed

+170
-57
lines changed

6 files changed

+170
-57
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
- Introduce new lexer token type `Bytes`, as not all hex should be parsed to bytes32.
1313
- For constants in code tables the bytes are copied as defined with e.g. leading zeros.
1414
- For all other cases leading zeros are removed and the smallest push operation is used.
15+
- New built-in function `__LEFTPAD` to a hex input or a function result in a code table.
16+
- The function can only be used in a code table.
17+
- Example: `__LEFTPAD(0x123)` -> `0000000000000000000000000000000000000000000000000000000000000123`
18+
- Allow to pass a constant as a parameter to `__RIGHTPAD` or `__LEFTPAD`.
19+
- Example: `__RIGHTPAD([CONST])`
1520

1621
## [1.0.10] - 2025-01-31
1722
- Add windows binary to the release.

crates/codegen/src/irgen/builtin_function.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::irgen::constants::constant_gen;
12
use crate::Codegen;
23
use alloy_primitives::{hex, keccak256};
34
use huff_neo_utils::bytecode::{BytecodeRes, Bytes, CircularCodeSizeIndices, Jump, Jumps};
@@ -8,6 +9,7 @@ use huff_neo_utils::opcodes::Opcode;
89
use huff_neo_utils::prelude::{
910
BuiltinFunctionArg, BuiltinFunctionCall, BuiltinFunctionKind, Contract, MacroDefinition, MacroInvocation, TableDefinition,
1011
};
12+
use std::fmt::Display;
1113

1214
// TODO: First step to refactor and split the function into smaller functions
1315
#[allow(clippy::too_many_arguments)]
@@ -98,10 +100,17 @@ pub fn builtin_function_gen<'a>(
98100
bytes.push((starting_offset, Bytes(push_bytes)));
99101
}
100102
BuiltinFunctionKind::RightPad => {
101-
let push_bytes = right_pad(evm_version, contract, bf)?;
103+
let push_bytes = builtin_pad(evm_version, contract, bf, PadDirection::Right)?;
102104
*offset += push_bytes.len() / 2;
103105
bytes.push((starting_offset, Bytes(push_bytes)));
104106
}
107+
BuiltinFunctionKind::LeftPad => {
108+
return Err(CodegenError {
109+
kind: CodegenErrorKind::InvalidArguments(String::from("LeftPad is not supported in a function or macro")),
110+
span: bf.span.clone(),
111+
token: None,
112+
});
113+
}
105114
BuiltinFunctionKind::DynConstructorArg => {
106115
if bf.args.len() != 2 {
107116
tracing::error!(
@@ -475,19 +484,38 @@ pub fn function_signature(contract: &Contract, bf: &BuiltinFunctionCall) -> Resu
475484
Ok(push_bytes)
476485
}
477486

478-
pub fn right_pad(evm_version: &EVMVersion, contract: &Contract, bf: &BuiltinFunctionCall) -> Result<String, CodegenError> {
487+
#[derive(Debug, Clone, PartialEq)]
488+
pub enum PadDirection {
489+
Left,
490+
Right,
491+
}
492+
impl Display for PadDirection {
493+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
494+
match self {
495+
PadDirection::Left => write!(f, "__LEFTPAD"),
496+
PadDirection::Right => write!(f, "__RIGHTPAD"),
497+
}
498+
}
499+
}
500+
501+
pub fn builtin_pad(
502+
evm_version: &EVMVersion,
503+
contract: &Contract,
504+
bf: &BuiltinFunctionCall,
505+
direction: PadDirection,
506+
) -> Result<String, CodegenError> {
479507
if bf.args.len() != 1 {
480-
tracing::error!(target = "codegen", "Incorrect number of arguments passed to __RIGHTPAD, should be 1: {}", bf.args.len());
508+
tracing::error!(target = "codegen", "Incorrect number of arguments passed to {direction}, should be 1: {}", bf.args.len());
481509
return Err(CodegenError {
482510
kind: CodegenErrorKind::InvalidArguments(format!(
483-
"Incorrect number of arguments passed to __RIGHTPAD, should be 1: {}",
511+
"Incorrect number of arguments passed to {direction}, should be 1: {}",
484512
bf.args.len()
485513
)),
486514
span: bf.span.clone(),
487515
token: None,
488516
});
489517
}
490-
let first_arg = match bf.args[0] {
518+
let first_arg = match &bf.args[0] {
491519
BuiltinFunctionArg::Argument(ref arg) => arg.name.clone().unwrap_or_default(),
492520
BuiltinFunctionArg::BuiltinFunctionCall(ref inner_call) => {
493521
match inner_call.kind {
@@ -500,27 +528,33 @@ pub fn right_pad(evm_version: &EVMVersion, contract: &Contract, bf: &BuiltinFunc
500528
push_bytes[2..].to_string() // remove opcode
501529
}
502530
_ => {
503-
tracing::error!(target: "codegen", "Invalid argument type passed to __RIGHTPAD");
531+
tracing::error!(target: "codegen", "Invalid function call argument type passed to {direction}");
504532
return Err(CodegenError {
505-
kind: CodegenErrorKind::InvalidArguments(String::from("Invalid argument type passed to __RIGHTPAD")),
533+
kind: CodegenErrorKind::InvalidArguments(format!("Invalid argument type passed to {direction}")),
506534
span: bf.span.clone(),
507535
token: None,
508536
});
509537
}
510538
}
511539
}
540+
BuiltinFunctionArg::Constant(name, span) => {
541+
let push_bytes = constant_gen(evm_version, name, contract, span)?;
542+
push_bytes[2..].to_string() // remove opcode
543+
}
512544
_ => {
513-
tracing::error!(target: "codegen", "Invalid argument type passed to __RIGHTPAD");
545+
tracing::error!(target: "codegen", "Invalid argument type passed to {direction}");
514546
return Err(CodegenError {
515-
kind: CodegenErrorKind::InvalidArguments(String::from("Invalid argument type passed to __RIGHTPAD")),
547+
kind: CodegenErrorKind::InvalidArguments(format!("Invalid argument type passed to {direction}")),
516548
span: bf.span.clone(),
517549
token: None,
518550
});
519551
}
520552
};
521553
let hex = format_even_bytes(first_arg);
522-
let push_bytes = format!("{}{hex}{}", Opcode::Push32, "0".repeat(64 - hex.len()));
523-
Ok(push_bytes)
554+
if direction == PadDirection::Left {
555+
return Ok(format!("{}{}{hex}", Opcode::Push32, "0".repeat(64 - hex.len())));
556+
}
557+
Ok(format!("{}{hex}{}", Opcode::Push32, "0".repeat(64 - hex.len())))
524558
}
525559

526560
pub fn builtin_bytes(evm_version: &EVMVersion, bf: &BuiltinFunctionCall) -> Result<String, CodegenError> {

crates/codegen/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![warn(unused_extern_crates)]
44
#![forbid(unsafe_code)]
55

6-
use crate::irgen::builtin_function::{builtin_bytes, function_signature, right_pad};
6+
use crate::irgen::builtin_function::{builtin_bytes, builtin_pad, function_signature, PadDirection};
77
use alloy_primitives::hex;
88
use huff_neo_utils::ast::huff::*;
99
use huff_neo_utils::ast::span::AstSpan;
@@ -163,7 +163,11 @@ impl Codegen {
163163
byte_code = format!("{byte_code}{}", &bytes[2..]);
164164
}
165165
BuiltinFunctionKind::RightPad => {
166-
let bytes = right_pad(evm_version, contract, &bf)?;
166+
let bytes = builtin_pad(evm_version, contract, &bf, PadDirection::Right)?;
167+
byte_code = format!("{byte_code}{}", &bytes[2..]);
168+
}
169+
BuiltinFunctionKind::LeftPad => {
170+
let bytes = builtin_pad(evm_version, contract, &bf, PadDirection::Left)?;
167171
byte_code = format!("{byte_code}{}", &bytes[2..]);
168172
}
169173
BuiltinFunctionKind::Bytes => {

crates/core/tests/code_table.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,44 @@ fn test_code_table_constant() {
122122
assert_eq!(mbytes, "60 14 61 0005 0101010101010101010101010101010101010101".replace(" ", ""));
123123
}
124124

125+
#[test]
126+
fn test_code_table_leftpad() {
127+
let source: &str = r#"
128+
#define table CODE_TABLE() {
129+
__LEFTPAD(0x0123)
130+
}
131+
132+
#define macro MAIN() = takes(0) returns (0) {
133+
__tablesize(CODE_TABLE)
134+
__tablestart(CODE_TABLE)
135+
}
136+
"#;
137+
138+
// Parse tokens
139+
let flattened_source = FullFileSource { source, file: None, spans: vec![] };
140+
let lexer = Lexer::new(flattened_source);
141+
let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::<Vec<Token>>();
142+
let mut parser = Parser::new(tokens, None);
143+
144+
// Parse the AST
145+
let mut contract = parser.parse().unwrap();
146+
147+
// Derive storage pointers
148+
contract.derive_storage_pointers();
149+
150+
// Instantiate Codegen
151+
let cg = Codegen::new();
152+
153+
// The codegen instance should have no artifact
154+
assert!(cg.artifact.is_none());
155+
156+
// Have the Codegen create the constructor bytecode
157+
let mbytes = Codegen::generate_main_bytecode(&EVMVersion::default(), &contract, None).unwrap();
158+
159+
// 60 = PUSH1, 20 = 32 bytes table size, 61 = PUSH2, 0005 = 5 bytes table start
160+
assert_eq!(mbytes, "60 20 61 0005 0000000000000000000000000000000000000000000000000000000000000123".replace(" ", ""));
161+
}
162+
125163
#[test]
126164
fn test_code_table_constant_with_leading_zeros() {
127165
let source: &str = r#"
@@ -207,6 +245,8 @@ fn test_code_table_all_features() {
207245
__RIGHTPAD(__FUNC_SIG("transfer(address,uint256)"))
208246
0xDEADBEEF
209247
[ADDR]
248+
__LEFTPAD(0x123)
249+
__LEFTPAD([ADDR])
210250
}
211251
212252
#define macro MAIN() = takes(0) returns (0) {
@@ -236,10 +276,13 @@ fn test_code_table_all_features() {
236276
// Have the Codegen create the constructor bytecode
237277
let mbytes = Codegen::generate_main_bytecode(&EVMVersion::default(), &contract, None).unwrap();
238278

239-
// 60 = PUSH1, 36 = 56 bytes table size (32 + 4 + 20), 61 = PUSH2, 0005 = 5 bytes table start, a9059cbb = transfer(address,uint256)
279+
// 60 = PUSH1, 78 = 120 bytes table size (32 + 4 + 20), 61 = PUSH2, 0005 = 5 bytes table start, a9059cbb = transfer(address,uint256)
240280
assert_eq!(
241281
mbytes,
242-
"60 38 61 0005 a9059cbb00000000000000000000000000000000000000000000000000000000 deadbeef 0101010101010101010101010101010101010101"
282+
"60 78 61 0005 a9059cbb00000000000000000000000000000000000000000000000000000000 deadbeef \
283+
0101010101010101010101010101010101010101 \
284+
0000000000000000000000000000000000000000000000000000000000000123\
285+
0000000000000000000000000101010101010101010101010101010101010101"
243286
.replace(" ", "")
244287
);
245288
}

crates/parser/src/lib.rs

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -807,49 +807,68 @@ impl Parser {
807807

808808
tracing::debug!(target: "parser", "PARSING ARGs: {:?}", self.current_token.kind);
809809
while !self.check(TokenKind::CloseParen) {
810-
// Check for strings
811-
if let TokenKind::Str(s) = &self.current_token.kind {
812-
args.push(BuiltinFunctionArg::Argument(Argument {
813-
name: Some(s.to_owned()), // Place the string in the "name" field
814-
arg_type: None,
815-
indexed: false,
816-
span: AstSpan(vec![self.current_token.span.clone()]),
817-
arg_location: None,
818-
}));
819-
self.consume();
820-
}
821-
822-
// Check for literals
823-
if let TokenKind::Literal(l) = &self.current_token.kind {
824-
args.push(BuiltinFunctionArg::Argument(Argument {
825-
// Place literal in the "name" field
826-
name: Some(bytes32_to_hex_string(l, false)),
827-
arg_location: None,
828-
arg_type: None,
829-
indexed: false,
830-
span: AstSpan(vec![self.current_token.span.clone()]),
831-
}));
832-
self.consume();
833-
}
834-
835-
// Check for identifiers
836-
if let TokenKind::Ident(ident) = &self.current_token.kind {
837-
args.push(BuiltinFunctionArg::Argument(Argument {
838-
name: Some(ident.to_owned()),
839-
arg_type: None,
840-
indexed: false,
841-
span: AstSpan(vec![self.current_token.span.clone()]),
842-
arg_location: None,
843-
}));
844-
self.consume();
845-
}
846-
847810
let mut is_builtin = false;
848811
let mut builtin = "".to_string();
849-
if let TokenKind::BuiltinFunction(builtin_ref) = &self.current_token.kind {
850-
is_builtin = true;
851-
builtin = builtin_ref.clone();
852-
self.consume();
812+
813+
match &self.current_token.kind {
814+
TokenKind::Str(s) => {
815+
args.push(BuiltinFunctionArg::Argument(Argument {
816+
name: Some(s.to_owned()), // Place the string in the "name" field
817+
arg_type: None,
818+
indexed: false,
819+
span: AstSpan(vec![self.current_token.span.clone()]),
820+
arg_location: None,
821+
}));
822+
self.consume();
823+
}
824+
TokenKind::Bytes(bytes) => {
825+
args.push(BuiltinFunctionArg::Argument(Argument {
826+
name: Some(bytes.to_owned()),
827+
arg_type: None,
828+
indexed: false,
829+
span: AstSpan(vec![self.current_token.span.clone()]),
830+
arg_location: None,
831+
}));
832+
self.consume();
833+
}
834+
TokenKind::Literal(l) => {
835+
args.push(BuiltinFunctionArg::Argument(Argument {
836+
// Place literal in the "name" field
837+
name: Some(bytes32_to_hex_string(l, false)),
838+
arg_location: None,
839+
arg_type: None,
840+
indexed: false,
841+
span: AstSpan(vec![self.current_token.span.clone()]),
842+
}));
843+
self.consume();
844+
}
845+
TokenKind::Ident(ident) => {
846+
args.push(BuiltinFunctionArg::Argument(Argument {
847+
name: Some(ident.to_owned()),
848+
arg_type: None,
849+
indexed: false,
850+
span: AstSpan(vec![self.current_token.span.clone()]),
851+
arg_location: None,
852+
}));
853+
self.consume();
854+
}
855+
TokenKind::BuiltinFunction(builtin_ref) => {
856+
is_builtin = true;
857+
builtin = builtin_ref.clone();
858+
self.consume();
859+
}
860+
TokenKind::OpenBracket => {
861+
let (constant_name, span) = self.parse_constant_push()?;
862+
args.push(BuiltinFunctionArg::Constant(constant_name, AstSpan(vec![span])));
863+
}
864+
_ => {
865+
return Err(ParserError {
866+
kind: ParserErrorKind::UnexpectedType(self.current_token.kind.clone()),
867+
hint: Some("Expected string, literal, hex, identifier or build-in".to_string()),
868+
spans: AstSpan(vec![self.current_token.span.clone()]),
869+
cursor: self.cursor,
870+
});
871+
}
853872
}
854873

855874
if is_builtin {
@@ -1147,7 +1166,7 @@ impl Parser {
11471166
});
11481167
}
11491168
let mut curr_spans = vec![self.current_token.span.clone()];
1150-
self.match_kind(TokenKind::BuiltinFunction(String::default()))?;
1169+
self.consume();
11511170
let args = self.parse_builtin_args()?;
11521171
args.iter().for_each(|a| curr_spans.extend_from_slice(a.span().inner_ref()));
11531172
tracing::info!(target: "parser", "PARSING CODE TABLE BODY: [BUILTIN FN: {}({:?})]", f, args);

crates/utils/src/ast/huff.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ pub enum BuiltinFunctionArg {
533533
BuiltinFunctionCall(BuiltinFunctionCall),
534534
/// Abi Argument
535535
Argument(Argument),
536+
/// Constant Argument
537+
Constant(String, AstSpan),
536538
}
537539

538540
impl BuiltinFunctionArg {
@@ -542,6 +544,7 @@ impl BuiltinFunctionArg {
542544
BuiltinFunctionArg::Literal(_) => AstSpan::default(),
543545
BuiltinFunctionArg::BuiltinFunctionCall(b) => b.span.clone(),
544546
BuiltinFunctionArg::Argument(a) => a.span.clone(),
547+
BuiltinFunctionArg::Constant(_, span) => span.clone(),
545548
}
546549
}
547550
}
@@ -577,6 +580,8 @@ pub enum BuiltinFunctionKind {
577580
Error,
578581
/// Rightpad function
579582
RightPad,
583+
/// Leftpad function (only available in code tables)
584+
LeftPad,
580585
/// Dynamic constructor arg function
581586
DynConstructorArg,
582587
/// Inject Raw Bytes
@@ -595,6 +600,7 @@ impl From<String> for BuiltinFunctionKind {
595600
"__EVENT_HASH" => BuiltinFunctionKind::EventHash,
596601
"__ERROR" => BuiltinFunctionKind::Error,
597602
"__RIGHTPAD" => BuiltinFunctionKind::RightPad,
603+
"__LEFTPAD" => BuiltinFunctionKind::LeftPad,
598604
"__CODECOPY_DYN_ARG" => BuiltinFunctionKind::DynConstructorArg,
599605
"__VERBATIM" => BuiltinFunctionKind::Verbatim,
600606
"__BYTES" => BuiltinFunctionKind::Bytes,
@@ -617,6 +623,7 @@ impl TryFrom<&String> for BuiltinFunctionKind {
617623
"__EVENT_HASH" => Ok(BuiltinFunctionKind::EventHash),
618624
"__ERROR" => Ok(BuiltinFunctionKind::Error),
619625
"__RIGHTPAD" => Ok(BuiltinFunctionKind::RightPad),
626+
"__LEFTPAD" => Ok(BuiltinFunctionKind::LeftPad),
620627
"__CODECOPY_DYN_ARG" => Ok(BuiltinFunctionKind::DynConstructorArg),
621628
"__VERBATIM" => Ok(BuiltinFunctionKind::Verbatim),
622629
"__BYTES" => Ok(BuiltinFunctionKind::Bytes),
@@ -635,6 +642,7 @@ impl Display for BuiltinFunctionKind {
635642
BuiltinFunctionKind::EventHash => write!(f, "__EVENT_HASH"),
636643
BuiltinFunctionKind::Error => write!(f, "__ERROR"),
637644
BuiltinFunctionKind::RightPad => write!(f, "__RIGHTPAD"),
645+
BuiltinFunctionKind::LeftPad => write!(f, "__LEFTPAD"),
638646
BuiltinFunctionKind::DynConstructorArg => write!(f, "__CODECOPY_DYN_ARG"),
639647
BuiltinFunctionKind::Verbatim => write!(f, "__VERBATIM"),
640648
BuiltinFunctionKind::Bytes => write!(f, "__BYTES"),

0 commit comments

Comments
 (0)