Skip to content

Commit 3faaa02

Browse files
committed
Port to solar
1 parent 25e5ef9 commit 3faaa02

File tree

2 files changed

+86
-57
lines changed

2 files changed

+86
-57
lines changed

crates/compilers/src/cache.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ impl<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
681681
.collect();
682682

683683
let interface_repr_hash =
684-
self.is_source_file(&file).then(|| interface_representation_hash(source));
684+
self.is_source_file(&file).then(|| interface_representation_hash(source, &file));
685685

686686
let entry = CacheEntry {
687687
last_modification_date: CacheEntry::read_last_modification_date(&file)
@@ -949,7 +949,7 @@ impl<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
949949
if let hash_map::Entry::Vacant(entry) =
950950
self.interface_repr_hashes.entry(file.clone())
951951
{
952-
entry.insert(interface_representation_hash(&source));
952+
entry.insert(interface_representation_hash(&source, file));
953953
}
954954
}
955955
}

crates/compilers/src/preprocessor.rs

Lines changed: 84 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,74 +16,100 @@ use foundry_compilers_artifacts::{
1616
use foundry_compilers_core::utils;
1717
use itertools::Itertools;
1818
use md5::Digest;
19+
use solar_parse::{
20+
ast::{Span, Visibility},
21+
interface::diagnostics::EmittedDiagnostics,
22+
};
1923
use std::{
2024
collections::{BTreeMap, BTreeSet},
2125
path::{Path, PathBuf},
2226
};
23-
use solar_parse::interface::diagnostics::Diagnostic;
2427

2528
/// Removes parts of the contract which do not alter its interface:
2629
/// - Internal functions
2730
/// - External functions bodies
2831
///
2932
/// Preserves all libraries and interfaces.
30-
pub(crate) fn interface_representation(content: &str) -> Result<String, Vec<Diagnostic>> {
31-
// let (source_unit, _) = solang_parser::parse(content, 0)?;
32-
// let mut locs_to_remove = Vec::new();
33-
//
34-
// for part in source_unit.0 {
35-
// if let pt::SourceUnitPart::ContractDefinition(contract) = part {
36-
// if matches!(contract.ty, pt::ContractTy::Interface(_) | pt::ContractTy::Library(_)) {
37-
// continue;
38-
// }
39-
// for part in contract.parts {
40-
// if let pt::ContractPart::FunctionDefinition(func) = part {
41-
// let is_exposed = func.ty == pt::FunctionTy::Function
42-
// && func.attributes.iter().any(|attr| {
43-
// matches!(
44-
// attr,
45-
// pt::FunctionAttribute::Visibility(
46-
// pt::Visibility::External(_) | pt::Visibility::Public(_)
47-
// )
48-
// )
49-
// })
50-
// || matches!(
51-
// func.ty,
52-
// pt::FunctionTy::Constructor
53-
// | pt::FunctionTy::Fallback
54-
// | pt::FunctionTy::Receive
55-
// );
56-
//
57-
// if !is_exposed {
58-
// locs_to_remove.push(func.loc);
59-
// }
60-
//
61-
// if let Some(ref body) = func.body {
62-
// locs_to_remove.push(body.loc());
63-
// }
64-
// }
65-
// }
66-
// }
67-
// }
68-
//
69-
// let mut content = content.to_string();
70-
// let mut offset = 0;
71-
//
72-
// for loc in locs_to_remove {
73-
// let start = loc.start() - offset;
74-
// let end = loc.end() - offset;
75-
//
76-
// content.replace_range(start..end, "");
77-
// offset += end - start;
78-
// }
33+
pub(crate) fn interface_representation(
34+
content: &str,
35+
file: &PathBuf,
36+
) -> Result<String, EmittedDiagnostics> {
37+
let mut spans_to_remove: Vec<Span> = Vec::new();
38+
let sess =
39+
solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build();
40+
sess.enter(|| {
41+
let arena = solar_parse::ast::Arena::new();
42+
let filename = solar_parse::interface::source_map::FileName::Real(file.to_path_buf());
43+
let Ok(mut parser) =
44+
solar_parse::Parser::from_source_code(&sess, &arena, filename, content.to_string())
45+
else {
46+
return;
47+
};
48+
let Ok(ast) = parser.parse_file().map_err(|e| e.emit()) else { return };
49+
for item in ast.items {
50+
if let solar_parse::ast::ItemKind::Contract(contract) = &item.kind {
51+
if contract.kind.is_interface() | contract.kind.is_library() {
52+
continue;
53+
}
54+
for contract_item in contract.body.iter() {
55+
if let solar_parse::ast::ItemKind::Function(function) = &contract_item.kind {
56+
let is_exposed = match function.kind {
57+
// Function with external or public visibility
58+
solar_parse::ast::FunctionKind::Function => {
59+
function.header.visibility.is_some_and(|visibility| {
60+
visibility == Visibility::External
61+
|| visibility == Visibility::Public
62+
})
63+
}
64+
solar_parse::ast::FunctionKind::Constructor
65+
| solar_parse::ast::FunctionKind::Fallback
66+
| solar_parse::ast::FunctionKind::Receive => true,
67+
// Other (modifiers)
68+
_ => false,
69+
};
70+
71+
// If function is not exposed we remove the entire span (signature and
72+
// body). Otherwise we keep function signature and
73+
// remove only the body.
74+
if !is_exposed {
75+
spans_to_remove.push(contract_item.span);
76+
} else {
77+
spans_to_remove.push(function.body_span);
78+
}
79+
}
80+
}
81+
}
82+
}
83+
});
84+
85+
// Return original content if errors.
86+
if let Err(err) = sess.emitted_errors().unwrap() {
87+
let e = err.to_string();
88+
trace!("failed parsing {file:?}: {e}");
89+
return Err(err);
90+
}
91+
92+
let mut content = content.to_string();
93+
let mut offset = 0;
94+
95+
for span in spans_to_remove {
96+
let range = span.to_range();
97+
let start = range.start - offset;
98+
let end = range.end - offset;
99+
100+
content.replace_range(start..end, "");
101+
offset += end - start;
102+
}
79103

80104
let content = content.replace("\n", "");
81105
Ok(utils::RE_TWO_OR_MORE_SPACES.replace_all(&content, "").to_string())
82106
}
83107

84108
/// Computes hash of [`interface_representation`] of the source.
85-
pub(crate) fn interface_representation_hash(source: &Source) -> String {
86-
let Ok(repr) = interface_representation(&source.content) else { return source.content_hash() };
109+
pub(crate) fn interface_representation_hash(source: &Source, file: &PathBuf) -> String {
110+
let Ok(repr) = interface_representation(&source.content, file) else {
111+
return source.content_hash();
112+
};
87113
let mut hasher = md5::Md5::new();
88114
hasher.update(&repr);
89115
let result = hasher.finalize();
@@ -278,7 +304,7 @@ impl ContractData<'_> {
278304

279305
let abi_encode_args =
280306
params.parameters.iter().map(|param| format!("args.{}", param.name)).join(", ");
281-
307+
282308
let vm_interface_name = format!("VmContractHelper{}", ast_id);
283309
let vm = format!("{vm_interface_name}(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)");
284310

@@ -477,7 +503,10 @@ impl BytecodeDependencyOptimizer<'_> {
477503
updates.insert((
478504
new_loc.start,
479505
new_loc.end,
480-
format!("deployCode{id}(DeployHelper{id}.ConstructorArgs", id = dep.referenced_contract),
506+
format!(
507+
"deployCode{id}(DeployHelper{id}.ConstructorArgs",
508+
id = dep.referenced_contract
509+
),
481510
));
482511
updates.insert((dep.loc.end, dep.loc.end, ")".to_string()));
483512
}
@@ -605,7 +634,7 @@ contract A {
605634
}
606635
}"#;
607636

608-
let result = interface_representation(content).unwrap();
637+
let result = interface_representation(content, &PathBuf::new()).unwrap();
609638
assert_eq!(
610639
result,
611640
r#"library Lib {function libFn() internal {// logic to keep}}contract A {function a() externalfunction b() publicfunction e() external }"#

0 commit comments

Comments
 (0)