1use crate::ast::span::AstSpan;
6use crate::scope::ScopeId;
7use crate::{
8 evm_version::EVMVersion,
9 prelude::{Statement, TableDefinition},
10};
11use std::{
12 collections::BTreeMap,
13 fmt::{self, Display},
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
18pub enum PushOpcode {
19 Push1,
21 Push2,
23 Push3,
26}
27
28impl PushOpcode {
29 pub fn to_hex(&self) -> &'static str {
31 match self {
32 PushOpcode::Push1 => "60",
33 PushOpcode::Push2 => "61",
34 PushOpcode::Push3 => "62",
35 }
36 }
37
38 pub fn byte_size(&self) -> usize {
40 match self {
41 PushOpcode::Push1 => 1,
42 PushOpcode::Push2 => 2,
43 PushOpcode::Push3 => 3,
44 }
45 }
46
47 pub fn can_represent(&self, value: usize) -> bool {
49 value < 1 << (8 * self.byte_size())
50 }
51
52 pub fn optimal_for(value: usize) -> Self {
54 if value <= 0xFF {
55 PushOpcode::Push1
56 } else if value <= 0xFFFF {
57 PushOpcode::Push2
58 } else {
59 PushOpcode::Push3
60 }
61 }
62
63 pub fn format_value(&self, value: usize) -> String {
65 format!("{value:0width$x}", width = self.byte_size() * 2)
66 }
67
68 pub fn from_hex(s: &str) -> Option<Self> {
70 match s {
71 "60" => Some(PushOpcode::Push1),
72 "61" => Some(PushOpcode::Push2),
73 "62" => Some(PushOpcode::Push3),
74 _ => None,
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
81pub struct JumpPlaceholderData {
82 pub label: String,
84 pub push_opcode: PushOpcode,
86 pub suffix: String,
88 pub target_offset: Option<usize>,
90}
91
92impl JumpPlaceholderData {
93 pub fn new(label: String, push_opcode: PushOpcode, suffix: String) -> Self {
95 Self { label, push_opcode, suffix, target_offset: None }
96 }
97
98 pub fn with_target(label: String, push_opcode: PushOpcode, suffix: String, target_offset: usize) -> Self {
100 Self { label, push_opcode, suffix, target_offset: Some(target_offset) }
101 }
102
103 pub fn resolve(&mut self, target_offset: usize) {
105 self.target_offset = Some(target_offset);
106 }
107
108 pub fn to_hex(&self) -> String {
111 let value_hex = if let Some(offset) = self.target_offset {
112 self.push_opcode.format_value(offset)
114 } else {
115 "x".repeat(self.push_opcode.byte_size() * 2)
117 };
118
119 format!("{}{}{}", self.push_opcode.to_hex(), value_hex, self.suffix)
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
125pub struct CircularCodesizePlaceholderData {
126 pub macro_name: String,
128 pub push_opcode: PushOpcode,
130 pub resolved_value: Option<usize>,
132 pub measured_range: Option<(usize, usize)>,
136 pub size_hint: Option<usize>,
139}
140
141impl CircularCodesizePlaceholderData {
142 pub fn new(macro_name: String, size_hint: Option<usize>) -> Self {
145 Self { macro_name, push_opcode: PushOpcode::Push1, resolved_value: None, measured_range: None, size_hint }
146 }
147
148 pub fn size(&self, measured: impl FnOnce((usize, usize)) -> usize, total_length: usize) -> usize {
151 self.measured_range.map(measured).or(self.size_hint).unwrap_or(total_length)
152 }
153
154 pub fn resolve(&mut self, size: usize) -> usize {
159 let old_size = self.push_opcode.byte_size();
160 if !self.push_opcode.can_represent(size) {
161 self.push_opcode = PushOpcode::optimal_for(size);
162 }
163 self.resolved_value = Some(size);
164 self.push_opcode.byte_size() - old_size
165 }
166
167 pub fn to_hex(&self) -> String {
170 if let Some(size) = self.resolved_value {
171 format!("{}{}", self.push_opcode.to_hex(), self.push_opcode.format_value(size))
173 } else {
174 "cccc".to_string()
176 }
177 }
178}
179
180pub const ARGS_START_PLACEHOLDER: &str = "61yyyy";
183
184#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
187pub struct AssertPcPlaceholderData {
188 pub expected_offset: usize,
190 pub span: AstSpan,
192}
193
194impl AssertPcPlaceholderData {
195 pub fn new(expected_offset: usize, span: AstSpan) -> Self {
197 Self { expected_offset, span }
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
203pub enum Bytes {
204 Raw(String),
206 JumpPlaceholder(JumpPlaceholderData),
208 CircularCodesizePlaceholder(CircularCodesizePlaceholderData),
210 ArgsStartPlaceholder(AstSpan),
213 AssertPcPlaceholder(AssertPcPlaceholderData),
215}
216
217impl Bytes {
218 pub fn as_str(&self) -> String {
220 match self {
221 Bytes::Raw(s) => s.clone(),
222 Bytes::JumpPlaceholder(data) => data.to_hex(),
223 Bytes::CircularCodesizePlaceholder(data) => data.to_hex(),
224 Bytes::ArgsStartPlaceholder(_) => ARGS_START_PLACEHOLDER.to_string(),
225 Bytes::AssertPcPlaceholder(_) => String::new(), }
227 }
228
229 pub fn to_hex(&self) -> String {
231 self.as_str()
232 }
233
234 pub fn len(&self) -> usize {
237 match self {
238 Bytes::Raw(s) => s.len() / 2,
239 Bytes::JumpPlaceholder(data) => {
240 1 + data.push_opcode.byte_size() + (data.suffix.len() / 2)
242 }
243 Bytes::CircularCodesizePlaceholder(data) => {
244 1 + data.push_opcode.byte_size()
246 }
247 Bytes::ArgsStartPlaceholder(_) => ARGS_START_PLACEHOLDER.len() / 2,
248 Bytes::AssertPcPlaceholder(_) => 0, }
250 }
251
252 pub fn is_empty(&self) -> bool {
254 self.len() == 0
255 }
256}
257
258impl Display for Bytes {
259 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260 write!(f, "{}", self.as_str())
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
266pub struct BytecodeSegment {
267 pub offset: usize,
269 pub bytes: Bytes,
271}
272
273impl BytecodeSegment {
274 pub fn new(offset: usize, bytes: Bytes) -> Self {
276 Self { offset, bytes }
277 }
278}
279
280#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
282pub struct BytecodeSegments {
283 segments: Vec<BytecodeSegment>,
284}
285
286impl BytecodeSegments {
287 pub fn new() -> Self {
289 Self { segments: Vec::new() }
290 }
291
292 pub fn push(&mut self, segment: BytecodeSegment) {
294 self.segments.push(segment);
295 }
296
297 pub fn push_with_offset(&mut self, offset: usize, bytes: Bytes) {
299 self.segments.push(BytecodeSegment::new(offset, bytes));
300 }
301
302 pub fn extend(&mut self, other: BytecodeSegments) {
304 self.segments.extend(other.segments);
305 }
306
307 pub fn iter(&self) -> impl Iterator<Item = &BytecodeSegment> {
309 self.segments.iter()
310 }
311
312 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BytecodeSegment> {
314 self.segments.iter_mut()
315 }
316
317 pub fn len(&self) -> usize {
319 self.segments.len()
320 }
321
322 pub fn is_empty(&self) -> bool {
324 self.segments.is_empty()
325 }
326
327 pub fn calculate_offsets(&self) -> Vec<usize> {
330 let mut offsets = Vec::with_capacity(self.segments.len());
331 let mut current_offset = 0;
332
333 for segment in &self.segments {
334 offsets.push(current_offset);
335 current_offset += segment.bytes.len();
336 }
337
338 offsets
339 }
340
341 pub fn total_byte_size(&self) -> usize {
343 self.segments.iter().map(|seg| seg.bytes.len()).sum()
344 }
345
346 pub fn find_jump_placeholders(&self) -> Vec<(usize, &JumpPlaceholderData)> {
349 let mut jumps = Vec::new();
350 let mut offset = 0;
351
352 for segment in &self.segments {
353 if let Bytes::JumpPlaceholder(ref data) = segment.bytes {
354 jumps.push((offset, data));
355 }
356 offset += segment.bytes.len();
357 }
358
359 jumps
360 }
361
362 pub fn resolve_jumps(&mut self, jump_table: &JumpTable, label_indices: &LabelIndices) -> Result<Vec<Jump>, LabelError> {
365 let mut unmatched_jumps = Vec::new();
366
367 for segment in &mut self.segments {
368 let code_index = segment.offset;
369
370 if let Some(jt) = jump_table.get(&code_index) {
372 if let Bytes::JumpPlaceholder(ref mut data) = segment.bytes {
374 if let Some(jump) = jt.first() {
376 assert_eq!(
379 data.label, jump.label,
380 "Label mismatch at offset {:#x}: placeholder has \"{}\", jump table has \"{}\"",
381 code_index, data.label, jump.label
382 );
383
384 match label_indices.get(jump.label.as_str(), &jump.scope_id) {
386 Ok(Some(target_offset)) => {
387 if !data.push_opcode.can_represent(target_offset) {
389 return Err(LabelError::JumpTargetTooLarge {
390 label: jump.label.clone(),
391 target: target_offset,
392 opcode: format!("{:?}", data.push_opcode),
393 });
394 }
395 data.resolve(target_offset);
397 }
398 Ok(None) => {
399 unmatched_jumps.push(Jump {
401 label: jump.label.clone(),
402 bytecode_index: code_index,
403 span: jump.span.clone(),
404 scope_id: jump.scope_id.clone(),
405 });
406 }
407 Err(err_msg) => {
408 return Err(err_msg);
409 }
410 }
411 }
412 }
413 }
414 }
415
416 Ok(unmatched_jumps)
417 }
418
419 pub fn measure_circular_codesize(&mut self, macro_name: &str, start: usize, end: usize) {
425 for segment in &mut self.segments {
426 if let Bytes::CircularCodesizePlaceholder(ref mut data) = segment.bytes
427 && data.macro_name == macro_name
428 && data.measured_range.is_none()
429 {
430 data.measured_range = Some((start, end));
431 }
432 }
433 }
434
435 pub fn resolve_circular_codesize(&mut self) -> usize {
439 let total_length = self.total_byte_size();
440 let mut growth = 0;
441 for segment in &mut self.segments {
442 if let Bytes::CircularCodesizePlaceholder(ref mut data) = segment.bytes {
443 let size = data.size(|(start, end)| end - start, total_length);
444 growth += data.resolve(size);
445 }
446 }
447 growth
448 }
449}
450
451impl IntoIterator for BytecodeSegments {
452 type Item = BytecodeSegment;
453 type IntoIter = std::vec::IntoIter<BytecodeSegment>;
454
455 fn into_iter(self) -> Self::IntoIter {
456 self.segments.into_iter()
457 }
458}
459
460impl<'a> IntoIterator for &'a BytecodeSegments {
461 type Item = &'a BytecodeSegment;
462 type IntoIter = std::slice::Iter<'a, BytecodeSegment>;
463
464 fn into_iter(self) -> Self::IntoIter {
465 self.segments.iter()
466 }
467}
468
469#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
471pub struct IRBytes<'a> {
472 pub ty: IRByteType,
474 pub span: &'a AstSpan,
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
480pub enum IRByteType {
481 Bytes(Bytes),
483 Statement(Box<Statement>),
485 Constant(String),
487 ArgCall(String, String),
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
494pub struct IRBytecode<'a>(pub Vec<IRBytes<'a>>);
495
496pub trait ToIRBytecode<E> {
500 fn to_irbytecode(&self, evm_version: &EVMVersion) -> Result<IRBytecode<'_>, E>;
502}
503
504#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
506pub struct Bytecode(pub String);
507
508pub trait ToBytecode<'a, E> {
512 fn to_bytecode(&self) -> Result<Bytecode, E>;
514}
515
516impl From<Vec<Bytes>> for Bytecode {
517 fn from(b: Vec<Bytes>) -> Self {
518 Bytecode(b.iter().fold("".to_string(), |acc, b| format!("{acc}{}", b.as_str())))
519 }
520}
521
522#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
525pub struct BytecodeRes {
526 pub bytes: BytecodeSegments,
528 pub spans: Vec<Option<(usize, usize)>>,
530 pub label_indices: LabelIndices,
532 pub unmatched_jumps: Jumps,
534 pub table_instances: Jumps,
536 pub utilized_tables: Vec<TableDefinition>,
538 pub embedded_tables: BTreeMap<String, usize>,
540}
541
542impl Display for BytecodeRes {
543 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544 write!(
545 f,
546 r#"BytecodeRes(
547 bytes: [{}],
548 label_indices: {:?},
549 unmatched_jumps: {:?}
550 table_instances: {:?}
551 )"#,
552 self.bytes.iter().fold("".to_string(), |acc, b| format!("{acc}{}", b.bytes.as_str())),
553 self.label_indices,
554 self.unmatched_jumps,
555 self.table_instances
556 )
557 }
558}
559
560#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
562pub struct Jump {
563 pub label: String,
565 pub bytecode_index: usize,
567 pub span: AstSpan,
569 pub scope_id: ScopeId,
571}
572
573impl Default for Jump {
574 fn default() -> Self {
575 Self { label: String::new(), bytecode_index: 0, span: AstSpan::default(), scope_id: ScopeId::new(vec![], 0) }
576 }
577}
578
579pub type Jumps = Vec<Jump>;
581
582#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
584pub struct ScopedLabel {
585 pub offset: usize,
587 pub scope_id: ScopeId,
589}
590
591#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
593pub enum LabelError {
594 #[error("Duplicate label '{0}' in the same scope")]
596 DuplicateLabelInScope(String),
597 #[error("Duplicate label '{0}' across sibling scopes")]
599 DuplicateLabelAcrossSiblings(String),
600 #[error("Jump target {target:#x} too large for {opcode} in label \"{label}\"")]
602 JumpTargetTooLarge {
603 label: String,
605 target: usize,
607 opcode: String,
609 },
610}
611
612#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
614pub struct ScopedLabelIndices {
615 labels: BTreeMap<String, Vec<ScopedLabel>>,
617}
618
619impl ScopedLabelIndices {
620 pub fn new() -> Self {
622 Self { labels: BTreeMap::new() }
623 }
624
625 pub fn insert(&mut self, name: String, offset: usize, scope_id: &ScopeId) -> Result<(), LabelError> {
628 let entry = self.labels.entry(name.clone()).or_default();
629
630 for existing in entry.iter() {
632 if existing.scope_id == *scope_id {
633 return Err(LabelError::DuplicateLabelInScope(name));
634 }
635 }
636
637 entry.push(ScopedLabel { offset, scope_id: scope_id.clone() });
638 Ok(())
639 }
640
641 pub fn get(&self, name: &str, current_scope: &ScopeId) -> Result<Option<usize>, LabelError> {
645 let current_scope_depth = current_scope.depth;
646 let current_scope_path = ¤t_scope.path;
647
648 self.labels.get(name).map_or(Ok(None), |labels| {
649 let in_scope = labels
651 .iter()
652 .filter(|label| {
653 label.scope_id.depth <= current_scope_depth
656 && label.scope_id.path.len() <= current_scope_path.len()
657 && label.scope_id.path.iter().zip(current_scope_path.iter()).all(|(a, b)| a == b)
658 })
659 .max_by_key(|label| label.scope_id.depth)
660 .map(|label| label.offset);
661
662 if in_scope.is_some() {
663 return Ok(in_scope);
664 }
665
666 let child_labels: Vec<&ScopedLabel> = labels
669 .iter()
670 .filter(|label| {
671 label.scope_id.path.len() > current_scope_path.len()
673 && current_scope_path.iter().zip(label.scope_id.path.iter()).all(|(a, b)| a == b)
674 })
675 .collect();
676
677 if !child_labels.is_empty() {
678 if child_labels.len() > 1 {
680 }
683 return Ok(child_labels.first().map(|label| label.offset));
684 }
685
686 for depth in (0..current_scope_path.len()).rev() {
690 let search_scope = ¤t_scope_path[..depth + 1];
692
693 let cousin_labels: Vec<&ScopedLabel> = labels
695 .iter()
696 .filter(|label| {
697 if label.scope_id.path.len() <= depth {
699 return false;
700 }
701
702 if label.scope_id.path[..depth] != search_scope[..depth] {
704 return false;
705 }
706
707 if depth == current_scope_path.len() - 1 {
709 label.scope_id.path.len() == current_scope_path.len() && label.scope_id.path != *current_scope_path
711 } else {
712 if label.scope_id.path.len() > depth {
714 label.scope_id.path.get(depth) != current_scope_path.get(depth)
716 } else {
717 false
718 }
719 }
720 })
721 .collect();
722
723 if !cousin_labels.is_empty() {
724 if cousin_labels.len() > 1 {
726 let unique_scopes: std::collections::HashSet<_> = cousin_labels.iter().map(|label| &label.scope_id.path).collect();
728
729 if unique_scopes.len() > 1 {
730 return Err(LabelError::DuplicateLabelAcrossSiblings(name.to_string()));
732 }
733 }
734
735 return Ok(cousin_labels.first().map(|label| label.offset));
736 }
737 }
738
739 Ok(None)
740 })
741 }
742
743 pub fn get_any(&self, name: &str) -> Option<usize> {
747 self.labels.get(name).and_then(|labels| labels.first().map(|label| label.offset))
748 }
749
750 pub fn extend(&mut self, other: ScopedLabelIndices) {
752 for (name, new_labels) in other.labels {
753 self.labels.entry(name).or_default().extend(new_labels);
754 }
755 }
756
757 pub fn is_empty(&self) -> bool {
759 self.labels.is_empty()
760 }
761
762 pub fn iter(&self) -> impl Iterator<Item = (&String, &Vec<ScopedLabel>)> {
764 self.labels.iter()
765 }
766
767 pub fn update_offsets(&mut self, offset_mapping: &BTreeMap<usize, usize>) {
770 for labels in self.labels.values_mut() {
771 for label in labels.iter_mut() {
772 if let Some(&new_offset) = offset_mapping.get(&label.offset) {
773 label.offset = new_offset;
774 }
775 }
776 }
777 }
778}
779
780pub type LabelIndices = ScopedLabelIndices;
782
783pub type JumpTable = BTreeMap<usize, Jumps>;