edb_engine/utils/
source.rs

1// EDB - Ethereum Debugger
2// Copyright (C) 2024 Zhuo Zhang and Wuqi Zhang
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17use foundry_compilers::artifacts::{
18    ast::SourceLocation, Block, BlockOrStatement, StateMutability, Statement, TypeName, Visibility,
19};
20use semver::VersionReq;
21
22use crate::analysis::stmt_src;
23
24/// Get the source string at the given location.
25///
26/// # Arguments
27///
28/// * `id` - The source index
29/// * `source` - The source string
30/// * `location` - The source location. The index in the `location` must be the same as the `id` if it is present.
31pub fn source_string_at_location<'a>(
32    id: u32,
33    source: &'a str,
34    location: &SourceLocation,
35) -> &'a str {
36    if let Some(index) = location.index {
37        assert_eq!(index as u32, id, "Source index mismatch");
38    }
39
40    source_string_at_location_unchecked(source, location)
41}
42
43/// Get the source string at the given location.
44///
45/// # Arguments
46///
47/// * `source` - The source string
48/// * `location` - The source location. The index in the `location` is not checked whether it is the same as the id of the source.
49pub fn source_string_at_location_unchecked<'a>(
50    source: &'a str,
51    location: &SourceLocation,
52) -> &'a str {
53    let start = location.start.unwrap_or(0);
54    let end = location.length.map(|l| start + l).unwrap_or(source.len() - 1);
55    &source[start..end]
56}
57
58/// Slice the source location.
59///
60/// # Arguments
61///
62/// * `src` - The source location
63/// * `start` - The start index
64/// * `length` - The length of the sliced source
65///
66/// # Returns
67///
68/// The sliced source location.
69///
70/// # Example
71///
72/// ```rust
73/// let src = SourceLocation { start: Some(1), length: Some(10), index: Some(0) };
74/// let sliced = slice_source_location(&src, 1, 3);
75/// assert_eq!(sliced.start, Some(2));
76/// assert_eq!(sliced.length, Some(3));
77/// assert_eq!(sliced.index, Some(0));
78/// ```
79pub fn slice_source_location(src: &SourceLocation, start: usize, length: usize) -> SourceLocation {
80    assert!(
81        src.length.map(|l| l >= start).unwrap_or(true),
82        "Sliced start is greater than the original source length"
83    );
84    assert!(
85        src.length.map(|l| l >= start + length).unwrap_or(true),
86        "Sliced source length is greater than the original source length"
87    );
88
89    SourceLocation {
90        start: src.start.map(|s| s + start).or(Some(start)),
91        length: Some(length),
92        index: src.index,
93    }
94}
95
96/// Convert the visibility to a string.
97///
98/// # Arguments
99///
100/// * `visibility` - The visibility
101///
102/// # Returns
103///
104/// The string representation of the visibility.
105///
106/// # Example
107///
108/// ```rust
109/// let visibility = Visibility::Public;
110/// let str = visibility_to_str(visibility);
111/// assert_eq!(str, "public");
112/// ```
113pub fn visibility_to_str(visibility: &Visibility) -> &'static str {
114    match visibility {
115        Visibility::Public => "public",
116        Visibility::Internal => "internal",
117        Visibility::Private => "private",
118        Visibility::External => "external",
119    }
120}
121
122/// Convert the mutability to a string.
123///
124/// # Arguments
125///
126/// * `mutability` - The mutability
127///
128/// # Returns
129///
130/// The string representation of the mutability.
131///
132/// # Example
133///
134/// ```rust
135/// let mutability = StateMutability::Pure;
136/// let str = mutability_to_str(mutability);
137/// assert_eq!(str, "pure");
138/// ```
139pub fn mutability_to_str(mutability: &StateMutability) -> &'static str {
140    match mutability {
141        StateMutability::Pure => "pure",
142        StateMutability::View => "view",
143        StateMutability::Payable => "payable",
144        StateMutability::Nonpayable => "nonpayable",
145    }
146}
147
148/// Find the index of the next character immediately after the source location.
149///
150/// # Arguments
151///
152/// * `src` - The source location
153///
154/// # Returns
155///
156/// The index of the next character immediately after the source location.
157///
158pub fn find_next_index_of_source_location(src: &SourceLocation) -> Option<usize> {
159    if let Some(start) = src.start {
160        if let Some(length) = src.length {
161            return Some(start + length);
162        }
163    }
164    None
165}
166
167/// Find the next semicolon after the source location.
168///
169/// # Arguments
170///
171/// * `source` - The source string
172/// * `src` - The source location
173///
174/// # Returns
175///
176/// The index of the next semicolon after the source location in the source string.
177///
178/// # Example
179///
180/// ```rust
181/// let source = "1;2;3;4;5";
182/// let src = SourceLocation { start: Some(0), length: Some(1), index: Some(0) };
183/// let next_semicolon = find_next_semicolon_after_source_location(source, &src);
184/// assert_eq!(next_semicolon, Some(2));
185/// ```
186pub fn find_next_semicolon_after_source_location(
187    source: &str,
188    src: &SourceLocation,
189) -> Option<usize> {
190    let start = src.start.unwrap_or(0);
191    let end = src.length.map(|l| start + l).unwrap_or(start);
192    let substr = &source[end..];
193    substr.find(";").map(|i| i + end)
194}
195
196/// Find the index of the next character immediately after the `BlockOrStatement`.
197///
198/// # Arguments
199///
200/// * `source` - The source string
201/// * `block_or_statement` - The `BlockOrStatement`
202///
203/// # Returns
204///
205/// The index of the next character immediately after the `BlockOrStatement`.
206pub fn find_next_index_of_block_or_statement(
207    source: &str,
208    block_or_statement: &BlockOrStatement,
209) -> Option<usize> {
210    match block_or_statement {
211        BlockOrStatement::Statement(statement) => find_next_index_of_statement(source, statement),
212        BlockOrStatement::Block(block) => find_next_index_of_source_location(&block.src),
213    }
214}
215
216/// Find the index of the first statement in the `BlockOrStatement`.
217///
218/// # Arguments
219///
220/// * `block_or_statement` - The `BlockOrStatement`
221///
222/// # Returns
223///
224/// The index of the first statement in the `BlockOrStatement`.
225pub fn find_index_of_first_statement_in_block_or_statement(
226    block_or_statement: &BlockOrStatement,
227) -> Option<usize> {
228    match block_or_statement {
229        BlockOrStatement::Statement(statement) => match statement {
230            Statement::Block(block) => find_index_of_first_statement_in_block(block),
231            _ => stmt_src(statement).start,
232        },
233        BlockOrStatement::Block(block) => find_index_of_first_statement_in_block(block),
234    }
235}
236
237/// Find the index of the first statement in the `Block`.
238///
239/// # Arguments
240///
241/// * `block` - The `Block`
242///
243/// # Returns
244///
245/// The index of the first statement in the `Block`.
246pub fn find_index_of_first_statement_in_block(block: &Block) -> Option<usize> {
247    block.statements.first().map_or(
248        // if the block has no statements, the index of the first statement is the start of the block '{' plus 1
249        block.src.start.map(|s| s + 1),
250        |stmt| stmt_src(stmt).start,
251    )
252}
253
254/// Find the index of the next character immediately after the last statement in the `Block`.
255///
256/// # Arguments
257///
258/// * `source` - The source string
259/// * `block` - The `Block`
260///
261/// # Returns
262///
263/// The index of the next character immediately after the last statement in the `Block`.
264pub fn find_next_index_of_last_statement_in_block(source: &str, block: &Block) -> Option<usize> {
265    block.statements.last().map_or(
266        // if the block has no statements, the index of the last statement is the end of the block '}'
267        find_next_index_of_source_location(&block.src).map(|s| s - 1),
268        |stmt| find_next_index_of_statement(source, stmt),
269    )
270}
271
272/// Find the index of the next character immediately after the `Statement`.
273///
274/// # Arguments
275///
276/// * `source` - The source string
277/// * `stmt` - The `Statement`
278///
279/// # Returns
280///
281/// The index of the next character immediately after the `Statement`.
282pub fn find_next_index_of_statement(source: &str, stmt: &Statement) -> Option<usize> {
283    match stmt {
284        Statement::Block(block) => find_next_index_of_source_location(&block.src),
285        Statement::Break(break_stmt) => {
286            find_next_semicolon_after_source_location(source, &break_stmt.src).map(|i| i + 1)
287        }
288        Statement::Continue(continue_stmt) => {
289            find_next_semicolon_after_source_location(source, &continue_stmt.src).map(|i| i + 1)
290        }
291        Statement::DoWhileStatement(do_while_statement) => {
292            find_next_index_of_source_location(&do_while_statement.src)
293        }
294        Statement::EmitStatement(emit_statement) => {
295            find_next_semicolon_after_source_location(source, &emit_statement.src).map(|i| i + 1)
296        }
297        Statement::ExpressionStatement(expression_statement) => {
298            find_next_semicolon_after_source_location(source, &expression_statement.src)
299                .map(|i| i + 1)
300        }
301        Statement::ForStatement(for_statement) => {
302            find_next_index_of_block_or_statement(source, &for_statement.body)
303        }
304        Statement::IfStatement(if_statement) => match &if_statement.false_body {
305            Some(false_body) => find_next_index_of_block_or_statement(source, false_body),
306            None => find_next_index_of_block_or_statement(source, &if_statement.true_body),
307        },
308        Statement::InlineAssembly(inline_assembly) => {
309            find_next_index_of_source_location(&inline_assembly.src)
310        }
311        Statement::PlaceholderStatement(placeholder_statement) => {
312            find_next_semicolon_after_source_location(source, &placeholder_statement.src)
313                .map(|i| i + 1)
314        }
315        Statement::Return(return_stmt) => {
316            let return_str = source_string_at_location_unchecked(source, &return_stmt.src);
317            if return_str.trim_end().ends_with(";") {
318                find_next_index_of_source_location(&return_stmt.src)
319            } else {
320                find_next_semicolon_after_source_location(source, &return_stmt.src).map(|i| i + 1)
321            }
322        }
323        Statement::RevertStatement(revert_statement) => {
324            find_next_semicolon_after_source_location(source, &revert_statement.src).map(|i| i + 1)
325        }
326        Statement::TryStatement(try_statement) => {
327            find_next_index_of_source_location(&try_statement.src)
328        }
329        Statement::UncheckedBlock(unchecked_block) => {
330            find_next_index_of_source_location(&unchecked_block.src)
331        }
332        Statement::VariableDeclarationStatement(variable_declaration_statement) => {
333            find_next_semicolon_after_source_location(source, &variable_declaration_statement.src)
334                .map(|i| i + 1)
335        }
336        Statement::WhileStatement(while_statement) => {
337            find_next_index_of_block_or_statement(source, &while_statement.body)
338        }
339    }
340}
341
342/// Check recursively if the type name contains a user defined type or a function type.
343///
344/// # Arguments
345///
346/// * `type_name` - The type name
347///
348/// # Returns
349///
350/// True if the type name contains a user defined type or a function type.
351///
352/// # Example
353///
354/// ```rust
355/// let type_name = TypeName::UserDefinedTypeName("MyType".to_string());
356/// let contains = contains_user_defined_type_or_function_type(&type_name);
357/// assert!(contains);
358/// ```
359pub fn contains_user_defined_type(type_name: &TypeName) -> bool {
360    match type_name {
361        TypeName::ArrayTypeName(array_type_name) => {
362            contains_user_defined_type(&array_type_name.base_type)
363        }
364        TypeName::ElementaryTypeName(_) => false,
365        TypeName::FunctionTypeName(_) => false,
366        TypeName::Mapping(mapping) => {
367            contains_user_defined_type(&mapping.key_type)
368                || contains_user_defined_type(&mapping.value_type)
369        }
370        TypeName::UserDefinedTypeName(_) => true,
371    }
372}
373
374/// Check recursively if the type name contains a function type.
375///
376/// # Arguments
377///
378/// * `type_name` - The type name
379///
380/// # Returns
381///
382/// True if the type name contains a function type.
383///
384/// # Example
385///
386/// ```rust
387/// let type_name = TypeName::FunctionTypeName("MyFunction".to_string());
388/// let contains = contains_function_type(&type_name);
389/// assert!(contains);
390/// ```
391pub fn contains_function_type(type_name: &TypeName) -> bool {
392    match type_name {
393        TypeName::FunctionTypeName(_) => true,
394        TypeName::ArrayTypeName(array_type_name) => {
395            contains_function_type(&array_type_name.base_type)
396        }
397        TypeName::ElementaryTypeName(_) => false,
398        TypeName::Mapping(mapping) => {
399            contains_function_type(&mapping.key_type) || contains_function_type(&mapping.value_type)
400        }
401        TypeName::UserDefinedTypeName(_) => false,
402    }
403}
404
405/// Check recursively if the type name contains a mapping type.
406///
407/// # Arguments
408///
409/// * `type_name` - The type name
410///
411/// # Returns
412///
413/// True if the type name contains a mapping type.
414pub fn contains_mapping_type(type_name: &TypeName) -> bool {
415    match type_name {
416        TypeName::Mapping(_) => true,
417        TypeName::ArrayTypeName(array_type_name) => {
418            contains_mapping_type(&array_type_name.base_type)
419        }
420        TypeName::ElementaryTypeName(_) => false,
421        TypeName::FunctionTypeName(_) => false,
422        // TODO: the user defined type may have a mapping type, this need to be inspected in the future
423        TypeName::UserDefinedTypeName(_) => false,
424    }
425}
426
427/// Check if the abi.encode function (which is available since solidity 0.4.24) is available in the given version requirement.
428///
429/// # Arguments
430///
431/// * `version_req` - The version requirement
432///
433/// # Returns
434///
435/// True if the abi.encode function is available in the given version requirement.
436///
437/// # Example
438///
439/// ```rust
440/// let version_req = VersionReq::parse("^0.4.24").unwrap();
441/// let available = abi_encode_available(&version_req);
442/// assert!(available);
443/// ```
444pub fn abi_encode_available(version_req: &VersionReq) -> bool {
445    // abi.encode function is available since solidity 0.4.24
446    let min_version = semver::Version::parse("0.4.24").unwrap();
447
448    // If any version < 0.4.24 satisfies the requirement, then abi.encode is not available
449    !version_req.comparators.iter().all(|cmp| allows_any_version_lt(&min_version, cmp))
450}
451
452/// Check if a comparator allows any version less than the given minimum version.
453fn allows_any_version_lt(min_version: &semver::Version, comparator: &semver::Comparator) -> bool {
454    use semver::Op;
455
456    match comparator.op {
457        Op::Exact | Op::Greater | Op::GreaterEq | Op::Tilde | Op::Caret | Op::Wildcard => {
458            // Exact match: check if the exact version is < min_version
459            let exact_version = semver::Version {
460                major: comparator.major,
461                minor: comparator.minor.unwrap_or(0),
462                patch: comparator.patch.unwrap_or(0),
463                pre: semver::Prerelease::EMPTY,
464                build: semver::BuildMetadata::EMPTY,
465            };
466            exact_version < *min_version
467        }
468        Op::Less | Op::LessEq => true,
469        _ => true,
470    }
471}
472
473#[cfg(test)]
474mod tests {
475    use super::*;
476    use semver::VersionReq;
477
478    #[test]
479    fn test_abi_encode_available_exact_versions() {
480        // Test exact versions that should return true (>= 0.4.24, so no versions < 0.4.24 allowed)
481        assert!(abi_encode_available(&VersionReq::parse("=0.4.24").unwrap()));
482        assert!(abi_encode_available(&VersionReq::parse("=0.4.25").unwrap()));
483        assert!(abi_encode_available(&VersionReq::parse("=0.5.0").unwrap()));
484        assert!(abi_encode_available(&VersionReq::parse("=0.5.10").unwrap()));
485        assert!(abi_encode_available(&VersionReq::parse("=0.6.0").unwrap()));
486        assert!(abi_encode_available(&VersionReq::parse("=0.7.0").unwrap()));
487        assert!(abi_encode_available(&VersionReq::parse("=0.8.0").unwrap()));
488        assert!(abi_encode_available(&VersionReq::parse("=0.8.19").unwrap()));
489
490        // Test exact versions that should return false (< 0.4.24, so versions < 0.4.24 are allowed)
491        assert!(!abi_encode_available(&VersionReq::parse("=0.4.23").unwrap()));
492        assert!(!abi_encode_available(&VersionReq::parse("=0.4.20").unwrap()));
493        assert!(!abi_encode_available(&VersionReq::parse("=0.3.0").unwrap()));
494    }
495
496    #[test]
497    fn test_abi_encode_available_greater_than() {
498        // Test greater than versions - these should return true because they don't allow versions < 0.4.24
499        assert!(!abi_encode_available(&VersionReq::parse(">0.4.23").unwrap())); // allows 0.4.24+, no < 0.4.24
500        assert!(!abi_encode_available(&VersionReq::parse(">0.4.20").unwrap())); // allows 0.4.21+, no < 0.4.24
501        assert!(!abi_encode_available(&VersionReq::parse(">0.3.0").unwrap())); // allows 0.3.1+, no < 0.4.24
502        assert!(!abi_encode_available(&VersionReq::parse(">0.1.0").unwrap())); // allows 0.1.1+, no < 0.4.24
503
504        // These should return true because they don't allow any version < 0.4.24
505        assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); // only allows 0.4.25+, no < 0.4.24
506        assert!(abi_encode_available(&VersionReq::parse(">0.4.25").unwrap())); // only allows 0.4.26+, no < 0.4.24
507        assert!(abi_encode_available(&VersionReq::parse(">0.5.0").unwrap())); // only allows 0.5.1+, no < 0.4.24
508    }
509
510    #[test]
511    fn test_abi_encode_available_greater_equal() {
512        // Test greater than or equal versions - these should return true because they don't allow versions < 0.4.24
513        assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap()));
514        assert!(abi_encode_available(&VersionReq::parse(">=0.4.25").unwrap()));
515        assert!(abi_encode_available(&VersionReq::parse(">=0.5.0").unwrap()));
516        assert!(abi_encode_available(&VersionReq::parse(">=0.6.0").unwrap()));
517
518        // These should return false because the lower bound is < 0.4.24, so versions < 0.4.24 are allowed
519        assert!(!abi_encode_available(&VersionReq::parse(">=0.4.23").unwrap()));
520        assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20").unwrap()));
521    }
522
523    #[test]
524    fn test_abi_encode_available_less_than() {
525        // Test less than versions - these should return false because they allow versions < 0.4.24
526        assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap()));
527        assert!(!abi_encode_available(&VersionReq::parse("<0.4.23").unwrap()));
528        assert!(!abi_encode_available(&VersionReq::parse("<0.3.0").unwrap()));
529
530        // These should return false because they still allow versions < 0.4.24
531        assert!(!abi_encode_available(&VersionReq::parse("<0.4.25").unwrap()));
532        assert!(!abi_encode_available(&VersionReq::parse("<0.5.0").unwrap()));
533        assert!(!abi_encode_available(&VersionReq::parse("<0.6.0").unwrap()));
534    }
535
536    #[test]
537    fn test_abi_encode_available_less_equal() {
538        // Test less than or equal versions - these should return false because they allow versions < 0.4.24
539        assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap()));
540        assert!(!abi_encode_available(&VersionReq::parse("<=0.4.25").unwrap()));
541        assert!(!abi_encode_available(&VersionReq::parse("<=0.5.0").unwrap()));
542
543        // These should return false because the upper bound is < 0.4.24, so versions < 0.4.24 are allowed
544        assert!(!abi_encode_available(&VersionReq::parse("<=0.4.23").unwrap()));
545        assert!(!abi_encode_available(&VersionReq::parse("<=0.4.20").unwrap()));
546    }
547
548    #[test]
549    fn test_abi_encode_available_tilde() {
550        // Test tilde versions - these should return true because they don't allow versions < 0.4.24
551        assert!(abi_encode_available(&VersionReq::parse("~0.4.24").unwrap()));
552        assert!(abi_encode_available(&VersionReq::parse("~0.4.25").unwrap()));
553        assert!(abi_encode_available(&VersionReq::parse("~0.5.0").unwrap()));
554        assert!(abi_encode_available(&VersionReq::parse("~0.5.1").unwrap()));
555
556        // These should return false because the lower bound is < 0.4.24, so versions < 0.4.24 are allowed
557        assert!(!abi_encode_available(&VersionReq::parse("~0.4.23").unwrap()));
558        assert!(!abi_encode_available(&VersionReq::parse("~0.4.20").unwrap()));
559
560        // Test tilde with only major version - these should return false because they allow versions < 0.4.24
561        assert!(!abi_encode_available(&VersionReq::parse("~0").unwrap()));
562        assert!(!abi_encode_available(&VersionReq::parse("~0.3").unwrap()));
563    }
564
565    #[test]
566    fn test_abi_encode_available_caret() {
567        // Test caret versions - these should return true because they don't allow versions < 0.4.24
568        assert!(abi_encode_available(&VersionReq::parse("^0.4.24").unwrap()));
569        assert!(abi_encode_available(&VersionReq::parse("^0.4.25").unwrap()));
570        assert!(abi_encode_available(&VersionReq::parse("^0.5.0").unwrap()));
571        assert!(abi_encode_available(&VersionReq::parse("^0.6.0").unwrap()));
572
573        // These should return false because the lower bound is < 0.4.24, so versions < 0.4.24 are allowed
574        assert!(!abi_encode_available(&VersionReq::parse("^0.4.23").unwrap()));
575        assert!(!abi_encode_available(&VersionReq::parse("^0.4.20").unwrap()));
576
577        // Test caret with only major version - these should return false because they allow versions < 0.4.24
578        assert!(!abi_encode_available(&VersionReq::parse("^0").unwrap()));
579        assert!(!abi_encode_available(&VersionReq::parse("^0.3").unwrap()));
580    }
581
582    #[test]
583    fn test_abi_encode_available_wildcard() {
584        // Test wildcard versions - these should return true because they don't allow versions < 0.4.24
585        assert!(abi_encode_available(&VersionReq::parse("0.4.24").unwrap()));
586        assert!(abi_encode_available(&VersionReq::parse("0.4.25").unwrap()));
587        assert!(abi_encode_available(&VersionReq::parse("0.5.0").unwrap()));
588
589        // These should return false because they're < 0.4.24, so versions < 0.4.24 are allowed
590        assert!(!abi_encode_available(&VersionReq::parse("0.4.23").unwrap()));
591        assert!(!abi_encode_available(&VersionReq::parse("0.4.20").unwrap()));
592    }
593
594    #[test]
595    fn test_abi_encode_available_complex_ranges() {
596        // Test complex version ranges - these should return true because they don't allow versions < 0.4.24
597        assert!(abi_encode_available(&VersionReq::parse(">=0.4.24, <0.9.0").unwrap()));
598        assert!(abi_encode_available(&VersionReq::parse(">=0.5.0, <0.8.0").unwrap()));
599
600        // These should return false because they allow versions < 0.4.24
601        assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20, <0.4.24").unwrap()));
602        assert!(!abi_encode_available(&VersionReq::parse(">=0.3.0, <0.4.24").unwrap()));
603    }
604
605    #[test]
606    fn test_abi_encode_available_edge_cases() {
607        // Test edge cases around 0.4.24
608        assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap())); // doesn't allow < 0.4.24
609        assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); // doesn't allow < 0.4.24
610        assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap())); // allows < 0.4.24
611        assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap())); // allows < 0.4.24
612
613        // Test with pre-release versions (should be handled correctly)
614        assert!(abi_encode_available(&VersionReq::parse(">=0.4.24-alpha").unwrap()));
615    }
616}