1use fuel_asm::Word;
2use fuel_crypto::Hasher;
3use fuel_tx::{Bytes32, ContractId};
4use serde::{Deserialize, Serialize};
5use std::hash::Hash;
6use std::path::{Path, PathBuf};
7use std::{io, iter, slice};
8
9pub mod constants;
10
11pub mod ident;
12pub mod u256;
13pub use ident::*;
14
15pub mod integer_bits;
16
17pub mod source_engine;
18pub use source_engine::*;
19
20pub mod span;
21pub use span::*;
22
23pub mod style;
24
25pub mod ast;
26
27pub type Id = [u8; Bytes32::LEN];
28pub type Contract = [u8; ContractId::LEN];
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub struct Position {
32 pub line: usize,
33 pub col: usize,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38pub struct Range {
39 pub start: Position,
41 pub end: Position,
43}
44
45impl Range {
46 pub const fn is_valid(&self) -> bool {
47 self.start.line < self.end.line
48 || self.start.line == self.end.line && self.start.col <= self.end.col
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub struct Instruction {
54 pub pc: Word,
56 pub range: Range,
58 pub exit: bool,
60}
61
62impl Instruction {
63 pub fn to_bytes(&self) -> [u8; 41] {
64 let mut bytes = [0u8; 41];
65
66 bytes[..8].copy_from_slice(&(self.pc).to_be_bytes());
69 bytes[8..16].copy_from_slice(&(self.range.start.line as u64).to_be_bytes());
70 bytes[16..24].copy_from_slice(&(self.range.start.col as u64).to_be_bytes());
71 bytes[24..32].copy_from_slice(&(self.range.end.line as u64).to_be_bytes());
72 bytes[32..40].copy_from_slice(&(self.range.end.col as u64).to_be_bytes());
73 bytes[40] = self.exit as u8;
74
75 bytes
76 }
77
78 pub fn bytes<'a>(iter: impl Iterator<Item = &'a Self>) -> Vec<u8> {
79 iter.map(Self::to_bytes)
82 .fold::<Vec<u8>, _>(vec![], |mut v, b| {
83 v.extend(b);
84
85 v
86 })
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
91pub struct ProgramId(u16);
92
93impl ProgramId {
94 pub fn new(id: u16) -> Self {
95 Self(id)
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
100pub struct SourceId(u32);
101
102impl SourceId {
103 const SOURCE_ID_BITS: u32 = 20;
104 const SOURCE_ID_MASK: u32 = (1 << Self::SOURCE_ID_BITS) - 1;
105
106 pub fn new(program_id: u16, source_id: u32) -> Self {
108 SourceId(((program_id as u32) << Self::SOURCE_ID_BITS) | source_id)
109 }
110
111 pub fn program_id(&self) -> ProgramId {
113 ProgramId::new((self.0 >> Self::SOURCE_ID_BITS) as u16)
114 }
115
116 pub fn source_id(&self) -> u32 {
118 self.0 & Self::SOURCE_ID_MASK
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
123pub struct Source {
124 path: PathBuf,
126}
127
128impl<T> From<T> for Source
129where
130 T: Into<PathBuf>,
131{
132 fn from(path: T) -> Self {
133 Self { path: path.into() }
134 }
135}
136
137impl AsRef<PathBuf> for Source {
138 fn as_ref(&self) -> &PathBuf {
139 &self.path
140 }
141}
142
143impl AsRef<Path> for Source {
144 fn as_ref(&self) -> &Path {
145 self.path.as_ref()
146 }
147}
148
149impl AsMut<PathBuf> for Source {
150 fn as_mut(&mut self) -> &mut PathBuf {
151 &mut self.path
152 }
153}
154
155impl Source {
156 pub fn bytes(&self) -> io::Result<slice::Iter<'_, u8>> {
157 Ok(self
158 .path
159 .as_path()
160 .to_str()
161 .ok_or_else(|| {
162 io::Error::other("Failed to get the string representation of the path!")
163 })?
164 .as_bytes()
165 .iter())
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
171pub struct CallFrame {
172 id: Id,
174 contract: Contract,
176 source: Source,
178 range: Range,
180 program: Vec<Instruction>,
182}
183
184impl CallFrame {
185 pub fn new(
186 contract: ContractId,
187 source: Source,
188 range: Range,
189 program: Vec<Instruction>,
190 ) -> io::Result<Self> {
191 Context::validate_source(&source)?;
192 Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
193
194 let contract = Contract::from(contract);
195
196 let id = Context::id_from_repr(
197 Instruction::bytes(program.iter())
198 .iter()
199 .chain(contract.iter())
200 .chain(source.bytes()?),
201 );
202
203 Ok(Self {
204 id,
205 contract,
206 source,
207 range,
208 program,
209 })
210 }
211
212 pub const fn id(&self) -> &Id {
213 &self.id
214 }
215
216 pub const fn source(&self) -> &Source {
217 &self.source
218 }
219
220 pub const fn range(&self) -> &Range {
221 &self.range
222 }
223
224 pub fn program(&self) -> &[Instruction] {
225 self.program.as_slice()
226 }
227
228 pub fn contract(&self) -> ContractId {
229 self.contract.into()
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
235pub struct TransactionScript {
236 id: Id,
238 source: Source,
240 range: Range,
242 program: Vec<Instruction>,
244}
245
246impl TransactionScript {
247 pub fn new(source: Source, range: Range, program: Vec<Instruction>) -> io::Result<Self> {
248 Context::validate_source(&source)?;
249 Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
250
251 let id = Context::id_from_repr(
252 Instruction::bytes(program.iter())
253 .iter()
254 .chain(source.bytes()?),
255 );
256
257 Ok(Self {
258 id,
259 source,
260 range,
261 program,
262 })
263 }
264
265 pub const fn id(&self) -> &Id {
266 &self.id
267 }
268
269 pub const fn source(&self) -> &Source {
270 &self.source
271 }
272
273 pub const fn range(&self) -> &Range {
274 &self.range
275 }
276
277 pub fn program(&self) -> &[Instruction] {
278 self.program.as_slice()
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
284pub enum Context {
285 CallFrame(CallFrame),
286 TransactionScript(TransactionScript),
287}
288
289impl From<CallFrame> for Context {
290 fn from(frame: CallFrame) -> Self {
291 Self::CallFrame(frame)
292 }
293}
294
295impl From<TransactionScript> for Context {
296 fn from(script: TransactionScript) -> Self {
297 Self::TransactionScript(script)
298 }
299}
300
301impl Context {
302 pub fn validate_source<P>(path: P) -> io::Result<()>
303 where
304 P: AsRef<Path>,
305 {
306 if !path.as_ref().is_absolute() {
307 return Err(io::Error::new(
308 io::ErrorKind::InvalidData,
309 "The source path must be absolute!",
310 ));
311 }
312
313 if !path.as_ref().is_file() {
314 return Err(io::Error::new(
315 io::ErrorKind::InvalidData,
316 "The source path must be a valid Sway source file!",
317 ));
318 }
319
320 if !path.as_ref().exists() {
321 return Err(io::Error::new(
322 io::ErrorKind::NotFound,
323 "The source path must point to an existing file!",
324 ));
325 }
326
327 Ok(())
328 }
329
330 pub fn validate_range<'a>(mut range: impl Iterator<Item = &'a Range>) -> io::Result<()> {
331 if !range.any(|r| !r.is_valid()) {
332 Err(io::Error::new(
333 io::ErrorKind::InvalidData,
334 "The provided source range is inconsistent!",
335 ))
336 } else {
337 Ok(())
338 }
339 }
340
341 pub fn id_from_repr<'a>(bytes: impl Iterator<Item = &'a u8>) -> Id {
342 let bytes: Vec<u8> = bytes.copied().collect();
343
344 *Hasher::hash(bytes.as_slice())
345 }
346
347 pub const fn id(&self) -> &Id {
348 match self {
349 Self::CallFrame(t) => t.id(),
350 Self::TransactionScript(t) => t.id(),
351 }
352 }
353
354 pub const fn source(&self) -> &Source {
355 match self {
356 Self::CallFrame(t) => t.source(),
357 Self::TransactionScript(t) => t.source(),
358 }
359 }
360
361 pub const fn range(&self) -> &Range {
362 match self {
363 Self::CallFrame(t) => t.range(),
364 Self::TransactionScript(t) => t.range(),
365 }
366 }
367
368 pub fn program(&self) -> &[Instruction] {
369 match self {
370 Self::CallFrame(t) => t.program(),
371 Self::TransactionScript(t) => t.program(),
372 }
373 }
374
375 pub fn contract(&self) -> Option<ContractId> {
376 match self {
377 Self::CallFrame(t) => Some(t.contract()),
378 _ => None,
379 }
380 }
381}
382
383pub type FxBuildHasher = std::hash::BuildHasherDefault<rustc_hash::FxHasher>;
384pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
385pub type FxIndexSet<K> = indexmap::IndexSet<K, FxBuildHasher>;