1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
/// Our representation of an source file.
/// We use this representation because we need to package a file's
/// contents and AST and target so that we can both analyze it and
/// later produce an interface for it and a bridge file. In other
/// words: we need this information packaged together multiple times
use crate::{CubistSdkError, Result};
use cubist_config::{Config, Target};
use cubist_util::fs::is_within;
use solang_parser::pt;
use std::fs;
use std::path::{Path, PathBuf};
/// A source file with its AST and additional meta information
#[derive(Debug)]
pub struct SourceFile {
/// The source file's absolute path
pub file_name: PathBuf,
/// The source file's relative path to a root contracts folder
pub rel_path: PathBuf,
/// The AST the file contains
pub pt: pt::SourceUnit,
/// Comments from the source file
pub comments: Vec<pt::Comment>,
/// What target the code in the file runs on
pub target: Target,
}
impl SourceFile {
/// Create a new source file given a Cubist contract config.
/// Errors if it encounters problems with in file system or the parser.
pub fn new(file: impl AsRef<Path>, rel_path: PathBuf, target: Target) -> Result<Self> {
let code = fs::read_to_string(file.as_ref())
.map_err(|e| CubistSdkError::ReadFileError(file.as_ref().into(), e))?;
let file_name = file.as_ref().to_path_buf();
match solang_parser::parse(&code, 0) {
Ok((pt, comments)) => Ok(SourceFile {
file_name,
rel_path,
pt,
comments,
target,
}),
Err(es) => Err(CubistSdkError::ParseError(file_name, es)),
}
}
/// Returns a list of import directives in the source file
pub fn extract_import_directives(&self) -> Vec<pt::Import> {
self.pt
.0
.iter()
.filter_map(|part| match part {
pt::SourceUnitPart::ImportDirective(imp) => Some(imp.clone()),
_ => None,
})
.collect::<Vec<_>>()
}
/// Check the imports in this file for:
/// (1) Absolute paths that point into the contracts root directory.
/// This is a problem since Cubist copies the contents of the contracts dir.
/// (2) Relative paths that point outside the contracts root directory.
/// This is a problem for the same reason as the previous.
pub fn check_imports(&self, config: &Config) -> Result<()> {
let root_dir = &config.contracts().root_dir;
for import in self.extract_import_directives() {
let import_lit = match import {
pt::Import::Plain(s, ..) => s,
pt::Import::GlobalSymbol(s, ..) => s,
pt::Import::Rename(s, ..) => s,
};
// Check for external (node) imports. Those are fine.
// In the future we'll need to extend this for other external imports we support
if import_lit.string.starts_with('@') {
continue;
}
if import_lit.unicode {
return Err(CubistSdkError::UnicodeImportError(
import_lit.string,
self.file_name.clone(),
));
}
let path = Path::new(&import_lit.string);
// Is it an absolute path that points into the root directory?
if path.is_absolute() {
match is_within(path, root_dir) {
Err(e) => {
return Err(CubistSdkError::CanonicalizationError(
import_lit.string,
self.file_name.clone(),
Some(e),
))
}
Ok(points_into_root) => {
if points_into_root {
return Err(CubistSdkError::AbsolutePathError(
import_lit.string,
self.file_name.clone(),
));
}
}
}
}
// Is it a relative path that points outside the root directory?
if path.is_relative() {
let parent_path = self.file_name.parent();
if parent_path.is_none() {
return Err(CubistSdkError::CanonicalizationError(
import_lit.string,
self.file_name.clone(),
None,
));
}
let full_path = parent_path.unwrap().join(path);
match is_within(&full_path, root_dir) {
Err(e) => {
return Err(CubistSdkError::CanonicalizationError(
import_lit.string,
self.file_name.clone(),
Some(e),
))
}
Ok(is_within_root) => {
if !is_within_root {
return Err(CubistSdkError::RelativePathError(
import_lit.string,
self.file_name.clone(),
));
}
}
}
}
}
Ok(())
}
}