use fuel_asm::Word;
use fuel_crypto::Hasher;
use fuel_tx::{Bytes32, ContractId};
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use std::path::{Path, PathBuf};
use std::{io, iter, slice};
pub mod constants;
pub mod ident;
pub mod u256;
pub use ident::*;
pub mod integer_bits;
pub mod source_engine;
pub use source_engine::*;
pub mod span;
pub use span::*;
pub mod style;
pub mod ast;
pub type Id = [u8; Bytes32::LEN];
pub type Contract = [u8; ContractId::LEN];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Position {
pub line: usize,
pub col: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Range {
pub start: Position,
pub end: Position,
}
impl Range {
pub const fn is_valid(&self) -> bool {
self.start.line < self.end.line
|| self.start.line == self.end.line && self.start.col <= self.end.col
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Instruction {
pub pc: Word,
pub range: Range,
pub exit: bool,
}
impl Instruction {
pub fn to_bytes(&self) -> [u8; 41] {
let mut bytes = [0u8; 41];
bytes[..8].copy_from_slice(&(self.pc).to_be_bytes());
bytes[8..16].copy_from_slice(&(self.range.start.line as u64).to_be_bytes());
bytes[16..24].copy_from_slice(&(self.range.start.col as u64).to_be_bytes());
bytes[24..32].copy_from_slice(&(self.range.end.line as u64).to_be_bytes());
bytes[32..40].copy_from_slice(&(self.range.end.col as u64).to_be_bytes());
bytes[40] = self.exit as u8;
bytes
}
pub fn bytes<'a>(iter: impl Iterator<Item = &'a Self>) -> Vec<u8> {
iter.map(Self::to_bytes)
.fold::<Vec<u8>, _>(vec![], |mut v, b| {
v.extend(b);
v
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct ProgramId(u16);
impl ProgramId {
pub fn new(id: u16) -> Self {
Self(id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct SourceId(u32);
impl SourceId {
const SOURCE_ID_BITS: u32 = 20;
const SOURCE_ID_MASK: u32 = (1 << Self::SOURCE_ID_BITS) - 1;
pub fn new(program_id: u16, source_id: u32) -> Self {
SourceId(((program_id as u32) << Self::SOURCE_ID_BITS) | source_id)
}
pub fn program_id(&self) -> ProgramId {
ProgramId::new((self.0 >> Self::SOURCE_ID_BITS) as u16)
}
pub fn source_id(&self) -> u32 {
self.0 & Self::SOURCE_ID_MASK
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct Source {
path: PathBuf,
}
impl<T> From<T> for Source
where
T: Into<PathBuf>,
{
fn from(path: T) -> Self {
Self { path: path.into() }
}
}
impl AsRef<PathBuf> for Source {
fn as_ref(&self) -> &PathBuf {
&self.path
}
}
impl AsRef<Path> for Source {
fn as_ref(&self) -> &Path {
self.path.as_ref()
}
}
impl AsMut<PathBuf> for Source {
fn as_mut(&mut self) -> &mut PathBuf {
&mut self.path
}
}
impl Source {
pub fn bytes(&self) -> io::Result<slice::Iter<'_, u8>> {
Ok(self
.path
.as_path()
.to_str()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Failed to get the string representation of the path!",
)
})?
.as_bytes()
.iter())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CallFrame {
id: Id,
contract: Contract,
source: Source,
range: Range,
program: Vec<Instruction>,
}
impl CallFrame {
pub fn new(
contract: ContractId,
source: Source,
range: Range,
program: Vec<Instruction>,
) -> io::Result<Self> {
Context::validate_source(&source)?;
Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
let contract = Contract::from(contract);
let id = Context::id_from_repr(
Instruction::bytes(program.iter())
.iter()
.chain(contract.iter())
.chain(source.bytes()?),
);
Ok(Self {
id,
contract,
source,
range,
program,
})
}
pub const fn id(&self) -> &Id {
&self.id
}
pub const fn source(&self) -> &Source {
&self.source
}
pub const fn range(&self) -> &Range {
&self.range
}
pub fn program(&self) -> &[Instruction] {
self.program.as_slice()
}
pub fn contract(&self) -> ContractId {
self.contract.into()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TransactionScript {
id: Id,
source: Source,
range: Range,
program: Vec<Instruction>,
}
impl TransactionScript {
pub fn new(source: Source, range: Range, program: Vec<Instruction>) -> io::Result<Self> {
Context::validate_source(&source)?;
Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
let id = Context::id_from_repr(
Instruction::bytes(program.iter())
.iter()
.chain(source.bytes()?),
);
Ok(Self {
id,
source,
range,
program,
})
}
pub const fn id(&self) -> &Id {
&self.id
}
pub const fn source(&self) -> &Source {
&self.source
}
pub const fn range(&self) -> &Range {
&self.range
}
pub fn program(&self) -> &[Instruction] {
self.program.as_slice()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Context {
CallFrame(CallFrame),
TransactionScript(TransactionScript),
}
impl From<CallFrame> for Context {
fn from(frame: CallFrame) -> Self {
Self::CallFrame(frame)
}
}
impl From<TransactionScript> for Context {
fn from(script: TransactionScript) -> Self {
Self::TransactionScript(script)
}
}
impl Context {
pub fn validate_source<P>(path: P) -> io::Result<()>
where
P: AsRef<Path>,
{
if !path.as_ref().is_absolute() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"The source path must be absolute!",
));
}
if !path.as_ref().is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"The source path must be a valid Sway source file!",
));
}
if !path.as_ref().exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"The source path must point to an existing file!",
));
}
Ok(())
}
pub fn validate_range<'a>(mut range: impl Iterator<Item = &'a Range>) -> io::Result<()> {
if !range.any(|r| !r.is_valid()) {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"The provided source range is inconsistent!",
))
} else {
Ok(())
}
}
pub fn id_from_repr<'a>(bytes: impl Iterator<Item = &'a u8>) -> Id {
let bytes: Vec<u8> = bytes.copied().collect();
*Hasher::hash(bytes.as_slice())
}
pub const fn id(&self) -> &Id {
match self {
Self::CallFrame(t) => t.id(),
Self::TransactionScript(t) => t.id(),
}
}
pub const fn source(&self) -> &Source {
match self {
Self::CallFrame(t) => t.source(),
Self::TransactionScript(t) => t.source(),
}
}
pub const fn range(&self) -> &Range {
match self {
Self::CallFrame(t) => t.range(),
Self::TransactionScript(t) => t.range(),
}
}
pub fn program(&self) -> &[Instruction] {
match self {
Self::CallFrame(t) => t.program(),
Self::TransactionScript(t) => t.program(),
}
}
pub fn contract(&self) -> Option<ContractId> {
match self {
Self::CallFrame(t) => Some(t.contract()),
_ => None,
}
}
}
pub type FxBuildHasher = std::hash::BuildHasherDefault<rustc_hash::FxHasher>;
pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
pub type FxIndexSet<K> = indexmap::IndexSet<K, FxBuildHasher>;