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::new(
163 io::ErrorKind::Other,
164 "Failed to get the string representation of the path!",
165 )
166 })?
167 .as_bytes()
168 .iter())
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
174pub struct CallFrame {
175 id: Id,
177 contract: Contract,
179 source: Source,
181 range: Range,
183 program: Vec<Instruction>,
185}
186
187impl CallFrame {
188 pub fn new(
189 contract: ContractId,
190 source: Source,
191 range: Range,
192 program: Vec<Instruction>,
193 ) -> io::Result<Self> {
194 Context::validate_source(&source)?;
195 Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
196
197 let contract = Contract::from(contract);
198
199 let id = Context::id_from_repr(
200 Instruction::bytes(program.iter())
201 .iter()
202 .chain(contract.iter())
203 .chain(source.bytes()?),
204 );
205
206 Ok(Self {
207 id,
208 contract,
209 source,
210 range,
211 program,
212 })
213 }
214
215 pub const fn id(&self) -> &Id {
216 &self.id
217 }
218
219 pub const fn source(&self) -> &Source {
220 &self.source
221 }
222
223 pub const fn range(&self) -> &Range {
224 &self.range
225 }
226
227 pub fn program(&self) -> &[Instruction] {
228 self.program.as_slice()
229 }
230
231 pub fn contract(&self) -> ContractId {
232 self.contract.into()
233 }
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
238pub struct TransactionScript {
239 id: Id,
241 source: Source,
243 range: Range,
245 program: Vec<Instruction>,
247}
248
249impl TransactionScript {
250 pub fn new(source: Source, range: Range, program: Vec<Instruction>) -> io::Result<Self> {
251 Context::validate_source(&source)?;
252 Context::validate_range(iter::once(&range).chain(program.iter().map(|p| &p.range)))?;
253
254 let id = Context::id_from_repr(
255 Instruction::bytes(program.iter())
256 .iter()
257 .chain(source.bytes()?),
258 );
259
260 Ok(Self {
261 id,
262 source,
263 range,
264 program,
265 })
266 }
267
268 pub const fn id(&self) -> &Id {
269 &self.id
270 }
271
272 pub const fn source(&self) -> &Source {
273 &self.source
274 }
275
276 pub const fn range(&self) -> &Range {
277 &self.range
278 }
279
280 pub fn program(&self) -> &[Instruction] {
281 self.program.as_slice()
282 }
283}
284
285#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
287pub enum Context {
288 CallFrame(CallFrame),
289 TransactionScript(TransactionScript),
290}
291
292impl From<CallFrame> for Context {
293 fn from(frame: CallFrame) -> Self {
294 Self::CallFrame(frame)
295 }
296}
297
298impl From<TransactionScript> for Context {
299 fn from(script: TransactionScript) -> Self {
300 Self::TransactionScript(script)
301 }
302}
303
304impl Context {
305 pub fn validate_source<P>(path: P) -> io::Result<()>
306 where
307 P: AsRef<Path>,
308 {
309 if !path.as_ref().is_absolute() {
310 return Err(io::Error::new(
311 io::ErrorKind::InvalidData,
312 "The source path must be absolute!",
313 ));
314 }
315
316 if !path.as_ref().is_file() {
317 return Err(io::Error::new(
318 io::ErrorKind::InvalidData,
319 "The source path must be a valid Sway source file!",
320 ));
321 }
322
323 if !path.as_ref().exists() {
324 return Err(io::Error::new(
325 io::ErrorKind::NotFound,
326 "The source path must point to an existing file!",
327 ));
328 }
329
330 Ok(())
331 }
332
333 pub fn validate_range<'a>(mut range: impl Iterator<Item = &'a Range>) -> io::Result<()> {
334 if !range.any(|r| !r.is_valid()) {
335 Err(io::Error::new(
336 io::ErrorKind::InvalidData,
337 "The provided source range is inconsistent!",
338 ))
339 } else {
340 Ok(())
341 }
342 }
343
344 pub fn id_from_repr<'a>(bytes: impl Iterator<Item = &'a u8>) -> Id {
345 let bytes: Vec<u8> = bytes.copied().collect();
346
347 *Hasher::hash(bytes.as_slice())
348 }
349
350 pub const fn id(&self) -> &Id {
351 match self {
352 Self::CallFrame(t) => t.id(),
353 Self::TransactionScript(t) => t.id(),
354 }
355 }
356
357 pub const fn source(&self) -> &Source {
358 match self {
359 Self::CallFrame(t) => t.source(),
360 Self::TransactionScript(t) => t.source(),
361 }
362 }
363
364 pub const fn range(&self) -> &Range {
365 match self {
366 Self::CallFrame(t) => t.range(),
367 Self::TransactionScript(t) => t.range(),
368 }
369 }
370
371 pub fn program(&self) -> &[Instruction] {
372 match self {
373 Self::CallFrame(t) => t.program(),
374 Self::TransactionScript(t) => t.program(),
375 }
376 }
377
378 pub fn contract(&self) -> Option<ContractId> {
379 match self {
380 Self::CallFrame(t) => Some(t.contract()),
381 _ => None,
382 }
383 }
384}
385
386pub type FxBuildHasher = std::hash::BuildHasherDefault<rustc_hash::FxHasher>;
387pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
388pub type FxIndexSet<K> = indexmap::IndexSet<K, FxBuildHasher>;