mirror of
https://github.com/FuelLabs/sway.git
synced 2025-08-09 13:18:44 +00:00
Debug
trait and its auto implementation (#7015)
## Description This PR implements the `__dbg(...)` intrinsic, which is very similar to Rust `dbg!(...)` macro. Up until now, it has being the norm to use `__log` to debug values. Given that this is NOT the first use case for log, we have always found some issues with it: log does not work on predicates, log does not show "private" fields like `Vec::capacity` and others. To solve these problems `__dbg` is being introduced: 1 - it will work on all program types, including predicates; 2 - it also prints the file name, line and column; 3 - All types will have an automatic implementation of Debug if possible, which can still be customized. 4 - Even `raw_ptr` and other non "loggable" types, have `Debug` impls. 5 - `__dbg` will be completely stripped in the release build by default. It can be turned on again if needed. So this: ``` // Aggregates let _ = __dbg((1u64, 2u64)); let _ = __dbg([1u64, 2u64]); // Structs and Enums let _ = __dbg(S { }); let _ = __dbg(E::None); let _ = __dbg(E::Some(S { })); ``` will generate this: ``` [src/main.sw:19:13] = (1, 2) [src/main.sw:20:13] = [1, 2] [src/main.sw:23:13] = S { } [src/main.sw:24:13] = None [src/main.sw:25:13] = E(S { }) ``` How does this work? `__dbg(value)` intrinsic is desugared into `{ let f = Formatter{}; f.print_str(...); let value = value; value.fmt(f); value }`. `Formatter` is similar to Rust's one. The difference is that we still do not support string formatting, so the `Formatter` has a lot of `print_*` functions. And each `print` function calls a "syscall". This `syscall` uses `ecal` under the hood and it follows unix write syscall schema. ```sway // ssize_t write(int fd, const void buf[.count], size_t count); fn syscall_write(fd: u64, buf: raw_ptr, count: u64) { asm(id: 1000, fd: fd, buf: buf, count: count) { ecal id fd buf count; } } ``` For that to work, the VM interpreter must have its `EcalState` setup and interpret syscall number 1000 as `write`. This PR does this for `forc test` and our `e2e test suite`. Each test in `forc test` will capture these calls and only print to the terminal when requested with the `--log` flag. ## Garbage Collector and auto generated Before, we were associating all auto-generated code with a pseudo file called "<autogenerated>.sw" that was never garbage collected. This generated a problem inside the LSP when the `auto_impl.rs` ran a second time because of a collision in the "shareable type" map. When we try to solve this collision, choosing to keep the old value or to insert the new, the type inside the map points to already collected types and the compiler panics. This is a known problem. The workaround for this is to break the auto-generated code into multiple files. Now they are named "main.autogenerated.sw", for example. We create one pseudo-file for each real file that needs one. When we garbage collect one file, `main.sw`, for example, we also collect its associated auto-generated file. ## Checklist - [ ] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers.
This commit is contained in:
parent
f607a674e3
commit
f736fce7ac
78 changed files with 4132 additions and 161 deletions
|
@ -245,3 +245,5 @@ StringSlice
|
|||
calldata
|
||||
Cfg
|
||||
evm
|
||||
AbiEncode
|
||||
AbiDecode
|
||||
|
|
|
@ -11,3 +11,48 @@ node to exercise your Sway code. Instruction-by-instruction debugging is availab
|
|||
|
||||
- [Debugging with CLI](./debugging_with_cli.md)
|
||||
- [Debugging with IDE](./debugging_with_ide.md)
|
||||
|
||||
## `__dbg` intrinsic function
|
||||
|
||||
Sway also offers the `__dbg` intrinsic function to help debug all applications types: scripts, contracts and predicates.
|
||||
When called, this intrinsic function will print the current file, line and column, together with a customizable print of the specified value.
|
||||
|
||||
```sway
|
||||
script;
|
||||
fn main() -> u64 {
|
||||
__dbg(1u64)
|
||||
}
|
||||
```
|
||||
|
||||
The application above will print:
|
||||
|
||||
```terminal
|
||||
[src/main.sw:3:5] = 1
|
||||
```
|
||||
|
||||
Structs can be customized by implementing the `Debug` trait.
|
||||
|
||||
```sway
|
||||
script;
|
||||
struct S { }
|
||||
impl Debug for S {
|
||||
fn fmt(self, ref mut f: Formatter) {
|
||||
f.debug_struct("S2")
|
||||
.field("field1", 1)
|
||||
.field("field2", "Hello")
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
fn main() -> u64 {
|
||||
let _ = __dbg(S {});
|
||||
__dbg(1u64)
|
||||
}
|
||||
```
|
||||
|
||||
This code is very similar to what the Sway compiler generates by default for all declared types.
|
||||
And this is what is printed:
|
||||
|
||||
```terminal
|
||||
[src/main.sw:12:13] = S2 { field1: 1, field2: "Hello" }
|
||||
[src/main.sw:13:5] = 1
|
||||
```
|
||||
|
|
|
@ -165,12 +165,14 @@ __state_store_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool
|
|||
---
|
||||
|
||||
```sway
|
||||
__log<T>(val: T)
|
||||
__log<T>(val: T) where T: AbiEncode
|
||||
```
|
||||
|
||||
**Description:** Logs value `val`.
|
||||
|
||||
**Constraints:** None.
|
||||
**Constraints:**
|
||||
|
||||
- `T` must implement AbiEncode
|
||||
|
||||
---
|
||||
|
||||
|
@ -335,10 +337,10 @@ __jmp_mem()
|
|||
---
|
||||
|
||||
```sway
|
||||
__slice(item: &[T; N], start: u64, end: u64) -> &[T]
|
||||
__slice(item: &[T], start: u64, end: u64) -> &[T]
|
||||
__slice(item: &mut [T; N], start: u64, end: u64) -> &mut [T]
|
||||
__slice(item: &mut [T], start: u64, end: u64) -> &mut [T]
|
||||
__slice<T>(item: &[T; N], start: u64, end: u64) -> &[T]
|
||||
__slice<T>(item: &[T], start: u64, end: u64) -> &[T]
|
||||
__slice<T>(item: &mut [T; N], start: u64, end: u64) -> &mut [T]
|
||||
__slice<T>(item: &mut [T], start: u64, end: u64) -> &mut [T]
|
||||
```
|
||||
|
||||
**Description:** Slices an array or another slice.
|
||||
|
@ -358,10 +360,10 @@ Runtime bound checks are not generated, and must be done manually when and where
|
|||
---
|
||||
|
||||
```sway
|
||||
__elem_at(item: &[T; N], index: u64) -> &T
|
||||
__elem_at(item: &[T], index: u64) -> &T
|
||||
__elem_at(item: &mut [T; N], index: u64) -> &mut T
|
||||
__elem_at(item: &mut [T], index: u64) -> &mut T
|
||||
__elem_at<T>(item: &[T; N], index: u64) -> &T
|
||||
__elem_at<T>(item: &[T], index: u64) -> &T
|
||||
__elem_at<T>(item: &mut [T; N], index: u64) -> &mut T
|
||||
__elem_at<T>(item: &mut [T], index: u64) -> &mut T
|
||||
```
|
||||
|
||||
**Description:** Returns a reference to the indexed element. The mutability of reference is defined by the first parameter mutability.
|
||||
|
@ -372,3 +374,37 @@ Runtime bound checks are not generated, and must be done manually when and where
|
|||
|
||||
- `item` is a reference to an array or a reference to a slice;
|
||||
- when `index` is a literal, it must be smaller than `item` length;
|
||||
|
||||
---
|
||||
|
||||
```sway
|
||||
__dbg<T>(value: T) -> T where T: Debug
|
||||
```
|
||||
|
||||
**Description:** Automatically calls the `Debug` trait on the passed `value`, with file, line and column information. The passed value is returned without any modification, allowing `__dbg(...)` to be used inside of any expression.
|
||||
|
||||
The code generated by this intrinsic function varies with the compilation mode. For example:
|
||||
|
||||
```terminal
|
||||
forc build <- will print everything as expected
|
||||
forc build --release <- nothing will be printed
|
||||
```
|
||||
|
||||
To enable code generation even on `Release` builds, the flag `force-dbg-in-release` needs to be enabled inside `forc.toml`.
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
authors = ["Fuel Labs <contact@fuel.sh>"]
|
||||
license = "Apache-2.0"
|
||||
entry = "main.sw"
|
||||
name = "some-project"
|
||||
force-dbg-in-release = true
|
||||
```
|
||||
|
||||
It is strongly suggested to always remove this flag before publishing binaries as it will not have any effect when running
|
||||
on real nodes and it only increases gas usage.
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- `T` must implement Debug
|
||||
|
|
|
@ -32,9 +32,3 @@ An analogy for predicates is rather than a traditional 12 or 24 word seed phrase
|
|||
Predicates may introspect the transaction spending their coins (inputs, outputs, script bytecode, etc.) and may take runtime arguments, either or both of which may affect the evaluation of the predicate.
|
||||
|
||||
It is important to note that predicates cannot read or write memory. They may however check the inputs and outputs of a transaction. For example in the [OTC Predicate Swap Example](https://github.com/FuelLabs/sway-applications/tree/master/OTC-swap-predicate), a user may specify they would like to swap `asset1` for `asset2` and with amount of `5`. The user would then send `asset1` to the predicate. Only when the predicate can verify that the outputs include `5` coins of `asset2` being sent to the original user, may `asset1` be transferred out of the predicate.
|
||||
|
||||
## Debugging Predicates
|
||||
|
||||
Because they don't have any side effects (they are _pure_), predicates cannot create receipts. Therefore, they cannot have logging or create a stack backtrace. This means that there is no native way to debug them aside from using a single-stepping debugger.
|
||||
|
||||
As a workaround, the predicate can be written, tested, and debugged first as a `script`, and then changed back into a `predicate`.
|
||||
|
|
|
@ -83,6 +83,10 @@ impl BuildProfile {
|
|||
optimization_level: OptLevel::Opt1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_release(&self) -> bool {
|
||||
self.name == Self::RELEASE
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BuildProfile {
|
||||
|
|
|
@ -213,6 +213,7 @@ pub struct Project {
|
|||
#[serde(default)]
|
||||
pub experimental: HashMap<String, bool>,
|
||||
pub metadata: Option<toml::Value>,
|
||||
pub force_dbg_in_release: Option<bool>,
|
||||
}
|
||||
|
||||
// Validation function for the `name` field
|
||||
|
@ -1449,6 +1450,7 @@ mod tests {
|
|||
forc_version: None,
|
||||
experimental: HashMap::new(),
|
||||
metadata: Some(toml::Value::from(toml::value::Table::new())),
|
||||
force_dbg_in_release: None,
|
||||
};
|
||||
|
||||
let serialized = toml::to_string(&project).unwrap();
|
||||
|
@ -1477,6 +1479,7 @@ mod tests {
|
|||
forc_version: None,
|
||||
experimental: HashMap::new(),
|
||||
metadata: None,
|
||||
force_dbg_in_release: None,
|
||||
};
|
||||
|
||||
let serialized = toml::to_string(&project).unwrap();
|
||||
|
|
|
@ -47,7 +47,7 @@ use sway_core::{
|
|||
source_map::SourceMap,
|
||||
write_dwarf, BuildTarget, Engines, FinalizedEntry, LspConfig,
|
||||
};
|
||||
use sway_core::{set_bytecode_configurables_offset, PrintAsm, PrintIr};
|
||||
use sway_core::{set_bytecode_configurables_offset, DbgGeneration, PrintAsm, PrintIr};
|
||||
use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning};
|
||||
use sway_features::ExperimentalFeatures;
|
||||
use sway_types::{Ident, ProgramId, Span, Spanned};
|
||||
|
@ -1557,6 +1557,7 @@ pub fn sway_build_config(
|
|||
entry_path: &Path,
|
||||
build_target: BuildTarget,
|
||||
build_profile: &BuildProfile,
|
||||
dbg_generation: sway_core::DbgGeneration,
|
||||
) -> Result<sway_core::BuildConfig> {
|
||||
// Prepare the build config to pass through to the compiler.
|
||||
let file_name = find_file_name(manifest_dir, entry_path)?;
|
||||
|
@ -1564,6 +1565,7 @@ pub fn sway_build_config(
|
|||
file_name.to_path_buf(),
|
||||
manifest_dir.to_path_buf(),
|
||||
build_target,
|
||||
dbg_generation,
|
||||
)
|
||||
.with_print_dca_graph(build_profile.print_dca_graph.clone())
|
||||
.with_print_dca_graph_url_format(build_profile.print_dca_graph_url_format.clone())
|
||||
|
@ -1603,6 +1605,7 @@ pub fn dependency_namespace(
|
|||
contract_id_value: Option<ContractIdConst>,
|
||||
program_id: ProgramId,
|
||||
experimental: ExperimentalFeatures,
|
||||
dbg_generation: sway_core::DbgGeneration,
|
||||
) -> Result<namespace::Package, vec1::Vec1<CompileError>> {
|
||||
// TODO: Clean this up when config-time constants v1 are removed.
|
||||
let node_idx = &graph[node];
|
||||
|
@ -1614,6 +1617,7 @@ pub fn dependency_namespace(
|
|||
program_id,
|
||||
contract_id_value,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)?
|
||||
} else {
|
||||
Package::new(name.clone(), None, program_id, false)
|
||||
|
@ -1646,6 +1650,7 @@ pub fn dependency_namespace(
|
|||
program_id,
|
||||
contract_id_value,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
@ -1680,12 +1685,18 @@ pub fn compile(
|
|||
namespace: namespace::Package,
|
||||
source_map: &mut SourceMap,
|
||||
experimental: ExperimentalFeatures,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<CompiledPackage> {
|
||||
let mut metrics = PerformanceData::default();
|
||||
|
||||
let entry_path = pkg.manifest_file.entry_path();
|
||||
let sway_build_config =
|
||||
sway_build_config(pkg.manifest_file.dir(), &entry_path, pkg.target, profile)?;
|
||||
let sway_build_config = sway_build_config(
|
||||
pkg.manifest_file.dir(),
|
||||
&entry_path,
|
||||
pkg.target,
|
||||
profile,
|
||||
dbg_generation,
|
||||
)?;
|
||||
let terse_mode = profile.terse;
|
||||
let reverse_results = profile.reverse_results;
|
||||
let fail = |handler: Handler| {
|
||||
|
@ -2374,6 +2385,10 @@ pub fn build(
|
|||
let pkg = &plan.graph()[node];
|
||||
let manifest = &plan.manifest_map()[&pkg.id()];
|
||||
let program_ty = manifest.program_type().ok();
|
||||
let dbg_generation = match (profile.is_release(), manifest.project.force_dbg_in_release) {
|
||||
(true, Some(true)) | (false, _) => DbgGeneration::Full,
|
||||
(true, _) => DbgGeneration::None,
|
||||
};
|
||||
|
||||
print_compiling(
|
||||
program_ty.as_ref(),
|
||||
|
@ -2439,6 +2454,7 @@ pub fn build(
|
|||
None,
|
||||
program_id,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
) {
|
||||
Ok(o) => o,
|
||||
Err(errs) => return fail(&[], &errs),
|
||||
|
@ -2451,6 +2467,7 @@ pub fn build(
|
|||
dep_namespace,
|
||||
&mut source_map,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)?;
|
||||
|
||||
if let Some(outfile) = profile.metrics_outfile {
|
||||
|
@ -2509,6 +2526,7 @@ pub fn build(
|
|||
contract_id_value.clone(),
|
||||
program_id,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
) {
|
||||
Ok(o) => o,
|
||||
Err(errs) => {
|
||||
|
@ -2530,6 +2548,7 @@ pub fn build(
|
|||
dep_namespace,
|
||||
&mut source_map,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)?;
|
||||
|
||||
if let Some(outfile) = profile.metrics_outfile {
|
||||
|
@ -2577,6 +2596,7 @@ pub fn check(
|
|||
retrigger_compilation: Option<Arc<AtomicBool>>,
|
||||
experimental: &[sway_features::Feature],
|
||||
no_experimental: &[sway_features::Feature],
|
||||
dbg_generation: sway_core::DbgGeneration,
|
||||
) -> anyhow::Result<Vec<(Option<Programs>, Handler)>> {
|
||||
let mut lib_namespace_map = HashMap::default();
|
||||
let mut source_map = SourceMap::new();
|
||||
|
@ -2625,6 +2645,7 @@ pub fn check(
|
|||
contract_id_value,
|
||||
program_id,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)
|
||||
.expect("failed to create dependency namespace");
|
||||
|
||||
|
@ -2638,6 +2659,7 @@ pub fn check(
|
|||
&manifest.entry_path(),
|
||||
build_target,
|
||||
&profile,
|
||||
dbg_generation,
|
||||
)?
|
||||
.with_include_tests(include_tests)
|
||||
.with_lsp_mode(lsp_mode.clone());
|
||||
|
|
|
@ -110,6 +110,7 @@ pub fn compile_html(
|
|||
None,
|
||||
&build_instructions.experimental.experimental,
|
||||
&build_instructions.experimental.no_experimental,
|
||||
sway_core::DbgGeneration::Full,
|
||||
)?;
|
||||
|
||||
let raw_docs = if build_instructions.no_deps {
|
||||
|
|
|
@ -108,6 +108,7 @@ pub(crate) fn compile_package<'a>(
|
|||
None,
|
||||
&build_instructions.experimental.experimental,
|
||||
&build_instructions.experimental.no_experimental,
|
||||
sway_core::DbgGeneration::Full,
|
||||
)?;
|
||||
|
||||
let Some(programs) =
|
||||
|
|
161
forc-test/src/ecal.rs
Normal file
161
forc-test/src/ecal.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use fuel_vm::{
|
||||
interpreter::EcalHandler,
|
||||
prelude::{Interpreter, RegId},
|
||||
};
|
||||
|
||||
// ssize_t write(int fd, const void buf[.count], size_t count);
|
||||
pub const WRITE_SYSCALL: u64 = 1000;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Syscall {
|
||||
Write { fd: u64, bytes: Vec<u8> },
|
||||
Unknown { ra: u64, rb: u64, rc: u64, rd: u64 },
|
||||
}
|
||||
|
||||
impl Syscall {
|
||||
pub fn apply(&self) {
|
||||
match self {
|
||||
Syscall::Write { fd, bytes } => {
|
||||
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
|
||||
|
||||
use std::io::Write;
|
||||
use std::os::fd::FromRawFd;
|
||||
|
||||
let mut f = unsafe { std::fs::File::from_raw_fd(*fd as i32) };
|
||||
write!(&mut f, "{}", s).unwrap();
|
||||
|
||||
// Dont close the fd
|
||||
std::mem::forget(f);
|
||||
}
|
||||
Syscall::Unknown { ra, rb, rc, rd } => {
|
||||
println!("Unknown ecal: {} {} {} {}", ra, rb, rc, rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle VM `ecal` as syscalls.
|
||||
///
|
||||
/// The application of the syscalls can be turned off,
|
||||
/// guaranteeing total isolation from the outside world.
|
||||
///
|
||||
/// Capture of the syscalls can be turned on, allowing
|
||||
/// its application even after the VM is not running anymore.
|
||||
///
|
||||
/// Supported syscalls:
|
||||
/// 1000 - write(fd: u64, buf: raw_ptr, count: u64) -> u64
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EcalSyscallHandler {
|
||||
pub apply: bool,
|
||||
pub capture: bool,
|
||||
pub captured: Vec<Syscall>,
|
||||
}
|
||||
|
||||
impl Default for EcalSyscallHandler {
|
||||
fn default() -> Self {
|
||||
Self::only_capturing()
|
||||
}
|
||||
}
|
||||
|
||||
impl EcalSyscallHandler {
|
||||
pub fn only_capturing() -> Self {
|
||||
Self {
|
||||
apply: false,
|
||||
capture: true,
|
||||
captured: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn only_applying() -> Self {
|
||||
Self {
|
||||
apply: true,
|
||||
capture: false,
|
||||
captured: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.captured.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl EcalHandler for EcalSyscallHandler {
|
||||
fn ecal<M, S, Tx>(
|
||||
vm: &mut Interpreter<M, S, Tx, Self>,
|
||||
a: RegId,
|
||||
b: RegId,
|
||||
c: RegId,
|
||||
d: RegId,
|
||||
) -> fuel_vm::error::SimpleResult<()>
|
||||
where
|
||||
M: fuel_vm::prelude::Memory,
|
||||
{
|
||||
let regs = vm.registers();
|
||||
let syscall = match regs[a.to_u8() as usize] {
|
||||
WRITE_SYSCALL => {
|
||||
let fd = regs[b.to_u8() as usize];
|
||||
let addr = regs[c.to_u8() as usize];
|
||||
let count = regs[d.to_u8() as usize];
|
||||
let bytes = vm.memory().read(addr, count).unwrap().to_vec();
|
||||
Syscall::Write { fd, bytes }
|
||||
}
|
||||
_ => {
|
||||
let ra = regs[a.to_u8() as usize];
|
||||
let rb = regs[b.to_u8() as usize];
|
||||
let rc = regs[c.to_u8() as usize];
|
||||
let rd = regs[d.to_u8() as usize];
|
||||
Syscall::Unknown { ra, rb, rc, rd }
|
||||
}
|
||||
};
|
||||
|
||||
let s = vm.ecal_state_mut();
|
||||
|
||||
if s.apply {
|
||||
syscall.apply();
|
||||
}
|
||||
|
||||
if s.capture {
|
||||
s.captured.push(syscall);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ok_capture_ecals() {
|
||||
use fuel_vm::fuel_asm::op::*;
|
||||
use fuel_vm::prelude::*;
|
||||
let vm: Interpreter<MemoryInstance, MemoryStorage, Script, EcalSyscallHandler> = <_>::default();
|
||||
|
||||
let test_input = "Hello, WriteSyscall!";
|
||||
let script_data: Vec<u8> = test_input.bytes().collect();
|
||||
let script = vec![
|
||||
movi(0x20, WRITE_SYSCALL as u32),
|
||||
gtf_args(0x10, 0x00, GTFArgs::ScriptData),
|
||||
movi(0x21, script_data.len().try_into().unwrap()),
|
||||
ecal(0x20, 0x1, 0x10, 0x21),
|
||||
ret(RegId::ONE),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// Execute transaction
|
||||
let mut client = MemoryClient::from_txtor(vm.into());
|
||||
let tx = TransactionBuilder::script(script, script_data)
|
||||
.script_gas_limit(1_000_000)
|
||||
.add_fee_input()
|
||||
.finalize()
|
||||
.into_checked(Default::default(), &ConsensusParameters::standard())
|
||||
.expect("failed to generate a checked tx");
|
||||
let _ = client.transact(tx);
|
||||
|
||||
// Verify
|
||||
let t: Transactor<MemoryInstance, MemoryStorage, Script, EcalSyscallHandler> = client.into();
|
||||
let syscalls = t.interpreter().ecal_state().captured.clone();
|
||||
|
||||
assert_eq!(syscalls.len(), 1);
|
||||
assert!(
|
||||
matches!(&syscalls[0], Syscall::Write { fd: 1, bytes } if std::str::from_utf8(bytes).unwrap() == test_input)
|
||||
);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::ecal::EcalSyscallHandler;
|
||||
use crate::maxed_consensus_params;
|
||||
use crate::setup::TestSetup;
|
||||
use crate::TestResult;
|
||||
|
@ -9,11 +10,8 @@ use fuel_vm::fuel_asm;
|
|||
use fuel_vm::prelude::Instruction;
|
||||
use fuel_vm::prelude::RegId;
|
||||
use fuel_vm::{
|
||||
self as vm,
|
||||
checked_transaction::builder::TransactionBuilderExt,
|
||||
interpreter::{Interpreter, NotSupportedEcal},
|
||||
prelude::SecretKey,
|
||||
storage::MemoryStorage,
|
||||
self as vm, checked_transaction::builder::TransactionBuilderExt, interpreter::Interpreter,
|
||||
prelude::SecretKey, storage::MemoryStorage,
|
||||
};
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
|
@ -26,7 +24,7 @@ use vm::state::ProgramState;
|
|||
/// An interface for executing a test within a VM [Interpreter] instance.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestExecutor {
|
||||
pub interpreter: Interpreter<MemoryInstance, MemoryStorage, tx::Script, NotSupportedEcal>,
|
||||
pub interpreter: Interpreter<MemoryInstance, MemoryStorage, tx::Script, EcalSyscallHandler>,
|
||||
pub tx: vm::checked_transaction::Ready<tx::Script>,
|
||||
pub test_entry: PkgTestEntry,
|
||||
pub name: String,
|
||||
|
@ -212,6 +210,7 @@ impl TestExecutor {
|
|||
condition,
|
||||
logs,
|
||||
gas_used,
|
||||
ecal: Box::new(self.interpreter.ecal_state().clone()),
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -243,10 +242,13 @@ impl TestExecutor {
|
|||
condition,
|
||||
logs,
|
||||
gas_used,
|
||||
ecal: Box::new(self.interpreter.ecal_state().clone()),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn execute(&mut self) -> anyhow::Result<TestResult> {
|
||||
self.interpreter.ecal_state_mut().clear();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let mut state = Ok(self.single_step_until_test());
|
||||
|
@ -282,6 +284,7 @@ impl TestExecutor {
|
|||
condition,
|
||||
logs,
|
||||
gas_used,
|
||||
ecal: Box::new(self.interpreter.ecal_state().clone()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod ecal;
|
||||
pub mod execute;
|
||||
pub mod setup;
|
||||
|
||||
|
@ -5,6 +6,7 @@ use crate::execute::TestExecutor;
|
|||
use crate::setup::{
|
||||
ContractDeploymentSetup, ContractTestSetup, DeploymentSetup, ScriptTestSetup, TestSetup,
|
||||
};
|
||||
use ecal::EcalSyscallHandler;
|
||||
use forc_pkg::{self as pkg, BuildOpts};
|
||||
use fuel_abi_types::error_codes::ErrorSignal;
|
||||
use fuel_tx as tx;
|
||||
|
@ -74,6 +76,8 @@ pub struct TestResult {
|
|||
pub logs: Vec<fuel_tx::Receipt>,
|
||||
/// Gas used while executing this test.
|
||||
pub gas_used: u64,
|
||||
/// EcalState of the execution
|
||||
pub ecal: Box<EcalSyscallHandler>,
|
||||
}
|
||||
|
||||
const TEST_METADATA_SEED: u64 = 0x7E57u64;
|
||||
|
|
|
@ -165,6 +165,10 @@ fn print_tested_pkg(pkg: &TestedPackage, test_print_opts: &TestPrintOpts) -> For
|
|||
info!("Decoded log value: {}, log rb: {}", var_value, rb);
|
||||
}
|
||||
}
|
||||
|
||||
for captured in test.ecal.captured.iter() {
|
||||
captured.apply();
|
||||
}
|
||||
}
|
||||
|
||||
if test_print_opts.raw_logs {
|
||||
|
|
|
@ -50,6 +50,7 @@ pub fn check(
|
|||
None,
|
||||
&experimental.experimental,
|
||||
&experimental.no_experimental,
|
||||
sway_core::DbgGeneration::None,
|
||||
)?;
|
||||
let (res, handler) = v
|
||||
.pop()
|
||||
|
|
|
@ -45,6 +45,7 @@ pub enum Intrinsic {
|
|||
Slice, // let ref_to_slice = __slice::<T: array or ref_to_slice>(item: T, inclusive_start_index, exclusive_end_index)
|
||||
ElemAt, // let elem: &T = __elem_at::<T: array or ref_to_slice>(item: T, index)
|
||||
Transmute, // let dst: B = __transmute::<A, B>(src)
|
||||
Dbg, // __dbg(value)
|
||||
}
|
||||
|
||||
impl fmt::Display for Intrinsic {
|
||||
|
@ -92,6 +93,7 @@ impl fmt::Display for Intrinsic {
|
|||
Intrinsic::Slice => "slice",
|
||||
Intrinsic::ElemAt => "elem_at",
|
||||
Intrinsic::Transmute => "transmute",
|
||||
Intrinsic::Dbg => "dbg",
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
|
@ -143,6 +145,7 @@ impl Intrinsic {
|
|||
"__slice" => Slice,
|
||||
"__elem_at" => ElemAt,
|
||||
"__transmute" => Transmute,
|
||||
"__dbg" => Dbg,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -38,6 +38,13 @@ impl BuildTarget {
|
|||
pub const CFG: &'static [&'static str] = &["evm", "fuel"];
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum DbgGeneration {
|
||||
Full,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum OptLevel {
|
||||
#[default]
|
||||
|
@ -180,6 +187,7 @@ impl From<&PrintIr> for PrintPassesOpts {
|
|||
pub struct BuildConfig {
|
||||
// Build target for code generation.
|
||||
pub(crate) build_target: BuildTarget,
|
||||
pub(crate) dbg_generation: DbgGeneration,
|
||||
// The canonical file path to the root module.
|
||||
// E.g. `/home/user/project/src/main.sw`.
|
||||
pub(crate) canonical_root_module: Arc<PathBuf>,
|
||||
|
@ -210,6 +218,7 @@ impl BuildConfig {
|
|||
root_module: PathBuf,
|
||||
canonical_manifest_dir: PathBuf,
|
||||
build_target: BuildTarget,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Self {
|
||||
assert!(
|
||||
canonical_manifest_dir.has_root(),
|
||||
|
@ -230,6 +239,7 @@ impl BuildConfig {
|
|||
};
|
||||
Self {
|
||||
build_target,
|
||||
dbg_generation,
|
||||
canonical_root_module: Arc::new(canonical_root_module),
|
||||
print_dca_graph: None,
|
||||
print_dca_graph_url_format: None,
|
||||
|
@ -348,6 +358,7 @@ mod test {
|
|||
root_module,
|
||||
canonical_manifest_dir,
|
||||
BuildTarget::default(),
|
||||
DbgGeneration::Full,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -359,6 +370,7 @@ mod test {
|
|||
root_module,
|
||||
canonical_manifest_dir,
|
||||
BuildTarget::default(),
|
||||
DbgGeneration::Full,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,11 +52,29 @@ impl Engines {
|
|||
|
||||
/// Removes all data associated with `source_id` from the engines.
|
||||
/// It is intended to be used during garbage collection to remove any data that is no longer needed.
|
||||
///
|
||||
/// It will also clear the associated autogenerated file for the `source_id` parameter.
|
||||
pub fn clear_module(&mut self, source_id: &sway_types::SourceId) {
|
||||
self.type_engine.clear_module(source_id);
|
||||
self.decl_engine.clear_module(source_id);
|
||||
self.parsed_decl_engine.clear_module(source_id);
|
||||
self.query_engine.clear_module(source_id);
|
||||
|
||||
// Check if `source_id` has an associated autogenerated file
|
||||
// and clear it
|
||||
if let Some(autogenerated_source_id) =
|
||||
self.se().get_associated_autogenerated_source_id(source_id)
|
||||
{
|
||||
if autogenerated_source_id == *source_id {
|
||||
return;
|
||||
}
|
||||
|
||||
self.type_engine.clear_module(&autogenerated_source_id);
|
||||
self.decl_engine.clear_module(&autogenerated_source_id);
|
||||
self.parsed_decl_engine
|
||||
.clear_module(&autogenerated_source_id);
|
||||
self.query_engine.clear_module(&autogenerated_source_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helps out some `thing: T` by adding `self` as context.
|
||||
|
|
|
@ -1677,6 +1677,9 @@ fn const_eval_intrinsic(
|
|||
let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?;
|
||||
Ok(Some(Constant::unique(lookup.context, c)))
|
||||
}
|
||||
Intrinsic::Dbg => {
|
||||
unreachable!("__dbg should not exist in the typed tree")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2210,6 +2210,9 @@ impl<'eng> FnCompiler<'eng> {
|
|||
Intrinsic::Transmute => {
|
||||
self.compile_intrinsic_transmute(arguments, return_type, context, md_mgr, &span)
|
||||
}
|
||||
Intrinsic::Dbg => {
|
||||
unreachable!("__dbg should not exist in the typed tree")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ use crate::source_map::SourceMap;
|
|||
pub use asm_generation::from_ir::compile_ir_context_to_finalized_asm;
|
||||
use asm_generation::FinalizedAsm;
|
||||
pub use asm_generation::{CompiledBytecode, FinalizedEntry};
|
||||
pub use build_config::DbgGeneration;
|
||||
pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr};
|
||||
use control_flow_analysis::ControlFlowGraph;
|
||||
pub use debug_generation::write_dwarf;
|
||||
|
@ -98,9 +99,9 @@ pub fn parse(
|
|||
experimental: ExperimentalFeatures,
|
||||
) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
|
||||
match config {
|
||||
None => parse_in_memory(handler, engines, src, experimental),
|
||||
None => parse_in_memory(handler, engines, src, experimental, DbgGeneration::None),
|
||||
// When a `BuildConfig` is given,
|
||||
// the module source may declare `dep`s that must be parsed from other files.
|
||||
// the module source may declare `mod`s that must be parsed from other files.
|
||||
Some(config) => parse_module_tree(
|
||||
handler,
|
||||
engines,
|
||||
|
@ -108,6 +109,7 @@ pub fn parse(
|
|||
config.canonical_root_module(),
|
||||
None,
|
||||
config.build_target,
|
||||
config.dbg_generation,
|
||||
config.include_tests,
|
||||
experimental,
|
||||
config.lsp_mode.as_ref(),
|
||||
|
@ -362,6 +364,7 @@ fn parse_in_memory(
|
|||
engines: &Engines,
|
||||
src: Source,
|
||||
experimental: ExperimentalFeatures,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
src.text.hash(&mut hasher);
|
||||
|
@ -376,7 +379,7 @@ fn parse_in_memory(
|
|||
let attributes_error_emitted = handler.append(attributes_handler);
|
||||
|
||||
let (kind, tree) = to_parsed_lang::convert_parse_tree(
|
||||
&mut to_parsed_lang::Context::new(BuildTarget::EVM, experimental),
|
||||
&mut to_parsed_lang::Context::new(BuildTarget::EVM, dbg_generation, experimental),
|
||||
handler,
|
||||
engines,
|
||||
module.value.clone(),
|
||||
|
@ -425,6 +428,7 @@ fn parse_submodules(
|
|||
module: &sway_ast::Module,
|
||||
module_dir: &Path,
|
||||
build_target: BuildTarget,
|
||||
dbg_generation: DbgGeneration,
|
||||
include_tests: bool,
|
||||
experimental: ExperimentalFeatures,
|
||||
lsp_mode: Option<&LspConfig>,
|
||||
|
@ -457,6 +461,7 @@ fn parse_submodules(
|
|||
submod_path.clone(),
|
||||
Some(submod.name.as_str()),
|
||||
build_target,
|
||||
dbg_generation,
|
||||
include_tests,
|
||||
experimental,
|
||||
lsp_mode,
|
||||
|
@ -510,6 +515,7 @@ fn parse_module_tree(
|
|||
path: Arc<PathBuf>,
|
||||
module_name: Option<&str>,
|
||||
build_target: BuildTarget,
|
||||
dbg_generation: DbgGeneration,
|
||||
include_tests: bool,
|
||||
experimental: ExperimentalFeatures,
|
||||
lsp_mode: Option<&LspConfig>,
|
||||
|
@ -530,6 +536,7 @@ fn parse_module_tree(
|
|||
&module.value,
|
||||
module_dir,
|
||||
build_target,
|
||||
dbg_generation,
|
||||
include_tests,
|
||||
experimental,
|
||||
lsp_mode,
|
||||
|
@ -544,7 +551,7 @@ fn parse_module_tree(
|
|||
|
||||
// Convert from the raw parsed module to the `ParseTree` ready for type-check.
|
||||
let (kind, tree) = to_parsed_lang::convert_parse_tree(
|
||||
&mut to_parsed_lang::Context::new(build_target, experimental),
|
||||
&mut to_parsed_lang::Context::new(build_target, dbg_generation, experimental),
|
||||
handler,
|
||||
engines,
|
||||
module.value.clone(),
|
||||
|
|
|
@ -13,7 +13,7 @@ use sway_error::{
|
|||
error::CompileError,
|
||||
handler::{ErrorEmitted, Handler},
|
||||
};
|
||||
use sway_types::{BaseIdent, Named, ProgramId, Span, Spanned};
|
||||
use sway_types::{BaseIdent, Named, SourceId, Span, Spanned};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AbiEncodingAutoImplInfo {}
|
||||
|
@ -190,7 +190,7 @@ where
|
|||
}
|
||||
|
||||
// Auto implements AbiEncode and AbiDecode for structs and returns their `AstNode`s.
|
||||
fn auto_impl_struct(
|
||||
fn auto_impl_abi_encode_and_decode_for_struct(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
decl: &TyDecl,
|
||||
|
@ -208,16 +208,18 @@ where
|
|||
let implementing_for_decl_id = decl.to_struct_decl(&Handler::default(), engines).unwrap();
|
||||
let struct_decl = self.ctx.engines().de().get(&implementing_for_decl_id);
|
||||
|
||||
let program_id = struct_decl.span().source_id().map(|sid| sid.program_id());
|
||||
|
||||
let abi_encode_body = self.generate_abi_encode_struct_body(engines, &struct_decl);
|
||||
let abi_encode_code = self.generate_abi_encode_code(
|
||||
struct_decl.name(),
|
||||
&struct_decl.type_parameters,
|
||||
abi_encode_body,
|
||||
);
|
||||
let abi_encode_node =
|
||||
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_encode_code);
|
||||
let abi_encode_node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
struct_decl.span().source_id(),
|
||||
&abi_encode_code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
let abi_decode_body = self.generate_abi_decode_struct_body(engines, &struct_decl);
|
||||
let abi_decode_code = self.generate_abi_decode_code(
|
||||
|
@ -225,13 +227,17 @@ where
|
|||
&struct_decl.type_parameters,
|
||||
abi_decode_body?,
|
||||
);
|
||||
let abi_decode_node =
|
||||
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_decode_code);
|
||||
let abi_decode_node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
struct_decl.span().source_id(),
|
||||
&abi_decode_code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
Some((abi_encode_node.ok(), abi_decode_node.ok()))
|
||||
}
|
||||
|
||||
fn auto_impl_enum(
|
||||
fn auto_impl_abi_encode_and_decode_for_enum(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
decl: &TyDecl,
|
||||
|
@ -249,16 +255,18 @@ where
|
|||
let enum_decl_id = decl.to_enum_id(&Handler::default(), engines).unwrap();
|
||||
let enum_decl = self.ctx.engines().de().get(&enum_decl_id);
|
||||
|
||||
let program_id = enum_decl.span().source_id().map(|sid| sid.program_id());
|
||||
|
||||
let abi_encode_body = self.generate_abi_encode_enum_body(engines, &enum_decl);
|
||||
let abi_encode_code = self.generate_abi_encode_code(
|
||||
enum_decl.name(),
|
||||
&enum_decl.type_parameters,
|
||||
abi_encode_body,
|
||||
);
|
||||
let abi_encode_node =
|
||||
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_encode_code);
|
||||
let abi_encode_node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
enum_decl.span().source_id(),
|
||||
&abi_encode_code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
let abi_decode_body = self.generate_abi_decode_enum_body(engines, &enum_decl);
|
||||
let abi_decode_code = self.generate_abi_decode_code(
|
||||
|
@ -266,8 +274,12 @@ where
|
|||
&enum_decl.type_parameters,
|
||||
abi_decode_body?,
|
||||
);
|
||||
let abi_decode_node =
|
||||
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_decode_code);
|
||||
let abi_decode_node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
enum_decl.span().source_id(),
|
||||
&abi_decode_code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
Some((abi_encode_node.ok(), abi_decode_node.ok()))
|
||||
}
|
||||
|
@ -278,8 +290,12 @@ where
|
|||
decl: &ty::TyDecl,
|
||||
) -> (Option<TyAstNode>, Option<TyAstNode>) {
|
||||
match decl {
|
||||
TyDecl::StructDecl(_) => self.auto_impl_struct(engines, decl).unwrap_or((None, None)),
|
||||
TyDecl::EnumDecl(_) => self.auto_impl_enum(engines, decl).unwrap_or((None, None)),
|
||||
TyDecl::StructDecl(_) => self
|
||||
.auto_impl_abi_encode_and_decode_for_struct(engines, decl)
|
||||
.unwrap_or((None, None)),
|
||||
TyDecl::EnumDecl(_) => self
|
||||
.auto_impl_abi_encode_and_decode_for_enum(engines, decl)
|
||||
.unwrap_or((None, None)),
|
||||
_ => (None, None),
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +303,7 @@ where
|
|||
pub(crate) fn generate_contract_entry(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
program_id: Option<ProgramId>,
|
||||
original_source_id: Option<&SourceId>,
|
||||
contract_fns: &[DeclId<TyFunctionDecl>],
|
||||
fallback_fn: Option<DeclId<TyFunctionDecl>>,
|
||||
handler: &Handler,
|
||||
|
@ -442,9 +458,10 @@ where
|
|||
|
||||
let entry_fn = self.parse_fn_to_ty_ast_node(
|
||||
engines,
|
||||
program_id,
|
||||
original_source_id,
|
||||
FunctionDeclarationKind::Entry,
|
||||
&code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
match entry_fn {
|
||||
|
@ -465,8 +482,6 @@ where
|
|||
decl: &TyFunctionDecl,
|
||||
handler: &Handler,
|
||||
) -> Result<TyAstNode, ErrorEmitted> {
|
||||
let program_id = decl.span.source_id().map(|sid| sid.program_id());
|
||||
|
||||
let Some(args_types) = decl
|
||||
.parameters
|
||||
.iter()
|
||||
|
@ -503,9 +518,10 @@ where
|
|||
|
||||
let entry_fn = self.parse_fn_to_ty_ast_node(
|
||||
engines,
|
||||
program_id,
|
||||
decl.span.source_id(),
|
||||
FunctionDeclarationKind::Entry,
|
||||
&code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
match entry_fn {
|
||||
|
@ -558,8 +574,6 @@ where
|
|||
decl: &TyFunctionDecl,
|
||||
handler: &Handler,
|
||||
) -> Result<TyAstNode, ErrorEmitted> {
|
||||
let program_id = decl.span.source_id().map(|sid| sid.program_id());
|
||||
|
||||
let Some(args_types) = decl
|
||||
.parameters
|
||||
.iter()
|
||||
|
@ -613,9 +627,10 @@ where
|
|||
|
||||
let entry_fn = self.parse_fn_to_ty_ast_node(
|
||||
engines,
|
||||
program_id,
|
||||
decl.span.source_id(),
|
||||
FunctionDeclarationKind::Entry,
|
||||
&code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
match entry_fn {
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
use sway_error::handler::Handler;
|
||||
use sway_types::{BaseIdent, Named, Spanned};
|
||||
|
||||
use crate::{
|
||||
decl_engine::DeclEngineGet,
|
||||
language::ty::{self, TyAstNode, TyDecl, TyEnumDecl, TyStructDecl},
|
||||
Engines, TypeParameter,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DebugAutoImplInfo {}
|
||||
|
||||
pub type DebugAutoImplContext<'a, 'b> = super::AutoImplContext<'a, 'b, DebugAutoImplInfo>;
|
||||
|
||||
impl<'a, 'b> DebugAutoImplContext<'a, 'b>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
pub fn generate_debug_impl(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
decl: &ty::TyDecl,
|
||||
) -> Option<TyAstNode> {
|
||||
match decl {
|
||||
TyDecl::StructDecl(_) => self.auto_impl_debug_struct(engines, decl),
|
||||
TyDecl::EnumDecl(_) => self.auto_impl_debug_enum(engines, decl),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// checks if the current module is a dependency of the `debug` module.
|
||||
fn is_debug_dependency(&self) -> bool {
|
||||
// Dependencies of the debug library in std cannot have debug implemented for them.
|
||||
self.ctx.namespace.current_package_name().as_str() == "std"
|
||||
&& matches!(
|
||||
self.ctx.namespace.current_module().name().as_str(),
|
||||
"codec"
|
||||
| "raw_slice"
|
||||
| "raw_ptr"
|
||||
| "ops"
|
||||
| "primitives"
|
||||
| "registers"
|
||||
| "flags"
|
||||
| "debug"
|
||||
)
|
||||
}
|
||||
|
||||
// Auto implements Debug for structs and returns their `AstNode`s.
|
||||
fn auto_impl_debug_struct(&mut self, engines: &Engines, decl: &TyDecl) -> Option<TyAstNode> {
|
||||
if self.is_debug_dependency() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let implementing_for_decl_id = decl.to_struct_decl(&Handler::default(), engines).unwrap();
|
||||
let struct_decl = self.ctx.engines().de().get(&implementing_for_decl_id);
|
||||
|
||||
let body = self.generate_fmt_struct_body(engines, &struct_decl);
|
||||
let code = self.generate_fmt_code(struct_decl.name(), &struct_decl.type_parameters, body);
|
||||
let node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
struct_decl.span().source_id(),
|
||||
&code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
node.ok()
|
||||
}
|
||||
|
||||
fn generate_fmt_code(
|
||||
&self,
|
||||
name: &BaseIdent,
|
||||
type_parameters: &[TypeParameter],
|
||||
body: String,
|
||||
) -> String {
|
||||
let type_parameters_declaration =
|
||||
self.generate_type_parameters_declaration_code(type_parameters);
|
||||
let type_parameters_constraints =
|
||||
self.generate_type_parameters_constraints_code(type_parameters, "Debug");
|
||||
|
||||
let name = name.as_str();
|
||||
|
||||
format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{
|
||||
#[allow(dead_code, deprecated)]
|
||||
fn fmt(self, ref mut _f: Formatter) {{
|
||||
{body}
|
||||
}}
|
||||
}}")
|
||||
}
|
||||
|
||||
fn generate_fmt_struct_body(&self, _engines: &Engines, decl: &TyStructDecl) -> String {
|
||||
let mut fields = String::new();
|
||||
|
||||
for field in decl.fields.iter() {
|
||||
fields.push_str(&format!(
|
||||
".field(\"{field_name}\", self.{field_name})\n",
|
||||
field_name = field.name.as_str(),
|
||||
));
|
||||
}
|
||||
|
||||
format!(
|
||||
"_f.debug_struct(\"{}\"){fields}.finish();",
|
||||
decl.name().as_str()
|
||||
)
|
||||
}
|
||||
|
||||
// Auto implements Debug for enums and returns their `AstNode`s.
|
||||
fn auto_impl_debug_enum(&mut self, engines: &Engines, decl: &TyDecl) -> Option<TyAstNode> {
|
||||
if self.is_debug_dependency() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let enum_decl_id = decl.to_enum_id(&Handler::default(), engines).unwrap();
|
||||
let enum_decl = self.ctx.engines().de().get(&enum_decl_id);
|
||||
|
||||
let body = self.generate_fmt_enum_body(engines, &enum_decl);
|
||||
let code = self.generate_fmt_code(enum_decl.name(), &enum_decl.type_parameters, body);
|
||||
let node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
enum_decl.span().source_id(),
|
||||
&code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
node.ok()
|
||||
}
|
||||
|
||||
fn generate_fmt_enum_body(&self, engines: &Engines, decl: &TyEnumDecl) -> String {
|
||||
let enum_name = decl.call_path.suffix.as_str();
|
||||
|
||||
let arms = decl
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let variant_name = variant.name.as_str();
|
||||
if engines.te().get(variant.type_argument.type_id()).is_unit() {
|
||||
format!(
|
||||
"{enum_name}::{variant_name} => {{
|
||||
_f.print_str(\"{variant_name}\");
|
||||
}}, \n",
|
||||
enum_name = enum_name,
|
||||
variant_name = variant_name
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{enum_name}::{variant_name}(value) => {{
|
||||
_f.debug_tuple(\"{enum_name}\").field(value).finish();
|
||||
}}, \n",
|
||||
enum_name = enum_name,
|
||||
variant_name = variant_name,
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
format!("match self {{ {arms} }};")
|
||||
}
|
||||
}
|
|
@ -44,15 +44,17 @@ where
|
|||
return None;
|
||||
}
|
||||
|
||||
let program_id = enum_decl.span().source_id().map(|sid| sid.program_id());
|
||||
|
||||
let impl_enum_code = format!(
|
||||
"#[allow(dead_code, deprecated)] impl {marker_trait_name} for {} {{ }}",
|
||||
enum_decl.name()
|
||||
);
|
||||
|
||||
let impl_enum_node =
|
||||
self.parse_impl_trait_to_ty_ast_node(engines, program_id, &impl_enum_code);
|
||||
let impl_enum_node = self.parse_impl_trait_to_ty_ast_node(
|
||||
engines,
|
||||
enum_decl.span().source_id(),
|
||||
&impl_enum_code,
|
||||
crate::build_config::DbgGeneration::None,
|
||||
);
|
||||
|
||||
impl_enum_node.ok()
|
||||
}
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
//! This module contains common infrastructure for generating and parsing auto-generated code.
|
||||
pub mod abi_encoding;
|
||||
pub mod debug;
|
||||
pub mod marker_traits;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::{
|
||||
build_config::DbgGeneration,
|
||||
engine_threading::SpannedWithEngines,
|
||||
language::{
|
||||
parsed::{self, AstNodeContent, Declaration, FunctionDeclarationKind},
|
||||
ty::{self, TyAstNode, TyDecl},
|
||||
},
|
||||
semantic_analysis::TypeCheckContext,
|
||||
Engines, GenericArgument, TypeInfo, TypeParameter,
|
||||
BuildTarget, Engines, GenericArgument, TypeInfo, TypeParameter,
|
||||
};
|
||||
use sway_error::handler::Handler;
|
||||
use sway_parse::Parse;
|
||||
use sway_types::{Named, ProgramId, Spanned};
|
||||
use sway_types::{Named, SourceId, Spanned};
|
||||
|
||||
/// Contains all information needed to auto-implement code for a certain feature.
|
||||
pub struct AutoImplContext<'a, 'b, I>
|
||||
|
@ -45,19 +47,23 @@ where
|
|||
/// Parses `input` into the expected [Parse] type.
|
||||
/// The resulted [Parse] has source id set to autogenerated source id
|
||||
/// within the program represented by the `program_id`.
|
||||
fn parse<T>(engines: &Engines, program_id: Option<ProgramId>, input: &str) -> T
|
||||
fn parse<T>(engines: &Engines, original_source_id: Option<&SourceId>, src: &str) -> T
|
||||
where
|
||||
T: Parse,
|
||||
{
|
||||
// Uncomment this to see what is being generated
|
||||
// println!("{}", input);
|
||||
// println!("{}", src);
|
||||
|
||||
let handler = <_>::default();
|
||||
let source_id =
|
||||
program_id.map(|program_id| engines.se().get_autogenerated_source_id(program_id));
|
||||
let autogenerated_source_id = original_source_id.as_ref().and_then(|source_id| {
|
||||
engines
|
||||
.se()
|
||||
.get_associated_autogenerated_source_id(source_id)
|
||||
});
|
||||
|
||||
let ts = sway_parse::lex(&handler, input.into(), 0, input.len(), source_id).unwrap();
|
||||
let mut p = sway_parse::Parser::new(&handler, &ts);
|
||||
let token_stream =
|
||||
sway_parse::lex(&handler, src.into(), 0, src.len(), autogenerated_source_id).unwrap();
|
||||
let mut p = sway_parse::Parser::new(&handler, &token_stream);
|
||||
p.check_double_underscore = false;
|
||||
|
||||
let r = p.parse();
|
||||
|
@ -126,18 +132,20 @@ where
|
|||
pub fn parse_fn_to_ty_ast_node(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
program_id: Option<ProgramId>,
|
||||
original_source_id: Option<&SourceId>,
|
||||
kind: FunctionDeclarationKind,
|
||||
code: &str,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<TyAstNode, Handler> {
|
||||
let mut ctx = crate::transform::to_parsed_lang::Context::new(
|
||||
crate::BuildTarget::Fuel,
|
||||
dbg_generation,
|
||||
self.ctx.experimental,
|
||||
);
|
||||
|
||||
let handler = Handler::default();
|
||||
|
||||
let item = Self::parse(engines, program_id, code);
|
||||
let item = Self::parse(engines, original_source_id, code);
|
||||
let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes(
|
||||
&mut ctx,
|
||||
&handler,
|
||||
|
@ -157,12 +165,7 @@ where
|
|||
panic!(
|
||||
"{:?} {:?}",
|
||||
handler,
|
||||
program_id
|
||||
.and_then(|x| engines.se().get_source_ids_from_program_id(x))
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| engines.se().get_file_name(x))
|
||||
.collect::<Vec<_>>()
|
||||
original_source_id.map(|x| engines.se().get_file_name(x))
|
||||
);
|
||||
}
|
||||
assert!(!handler.has_warnings(), "{:?}", handler);
|
||||
|
@ -206,17 +209,19 @@ where
|
|||
fn parse_impl_trait_to_ty_ast_node(
|
||||
&mut self,
|
||||
engines: &Engines,
|
||||
program_id: Option<ProgramId>,
|
||||
original_source_id: Option<&SourceId>,
|
||||
code: &str,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<TyAstNode, Handler> {
|
||||
let mut ctx = crate::transform::to_parsed_lang::Context::new(
|
||||
crate::BuildTarget::Fuel,
|
||||
BuildTarget::Fuel,
|
||||
dbg_generation,
|
||||
self.ctx.experimental,
|
||||
);
|
||||
|
||||
let handler = Handler::default();
|
||||
|
||||
let item = Self::parse(engines, program_id, code);
|
||||
let item = Self::parse(engines, original_source_id, code);
|
||||
let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes(
|
||||
&mut ctx, &handler, engines, item, false, None,
|
||||
)
|
||||
|
|
|
@ -110,6 +110,9 @@ impl ty::TyIntrinsicFunctionKind {
|
|||
Intrinsic::Transmute => {
|
||||
type_check_transmute(arguments, handler, kind, type_arguments, span, ctx)
|
||||
}
|
||||
Intrinsic::Dbg => {
|
||||
unreachable!("__dbg should not exist in the typed tree")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -677,7 +677,8 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet<Effect> {
|
|||
| EncodeBufferAsRawSlice
|
||||
| Slice
|
||||
| ElemAt
|
||||
| Transmute => HashSet::new(),
|
||||
| Transmute
|
||||
| Dbg => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ use crate::{
|
|||
|
||||
use super::{
|
||||
declaration::auto_impl::{
|
||||
abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext,
|
||||
abi_encoding::AbiEncodingAutoImplContext, debug::DebugAutoImplContext,
|
||||
marker_traits::MarkerTraitsAutoImplContext,
|
||||
},
|
||||
symbol_collection_context::SymbolCollectionContext,
|
||||
};
|
||||
|
@ -456,7 +457,7 @@ impl ty::TyModule {
|
|||
let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
|
||||
if let Ok(node) = fn_generator.generate_contract_entry(
|
||||
engines,
|
||||
parsed.span.source_id().map(|x| x.program_id()),
|
||||
parsed.span.source_id(),
|
||||
&contract_fns,
|
||||
fallback_fn,
|
||||
handler,
|
||||
|
@ -553,20 +554,29 @@ impl ty::TyModule {
|
|||
let all_abi_encode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
|
||||
decl.trait_name.suffix.as_str() == "AbiEncode"
|
||||
});
|
||||
let all_debug_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
|
||||
decl.trait_name.suffix.as_str() == "Debug"
|
||||
});
|
||||
|
||||
let mut typed_nodes = vec![];
|
||||
for node in nodes {
|
||||
// Check if the encoding traits are explicitly implemented.
|
||||
let auto_impl_encoding_traits = match &node.content {
|
||||
// Check if the encoding and debug traits are explicitly implemented.
|
||||
let (auto_impl_encoding_traits, auto_impl_debug_traits) = match &node.content {
|
||||
AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => {
|
||||
let decl = ctx.engines().pe().get_struct(decl_id);
|
||||
!all_abi_encode_impls.contains_key(&decl.name)
|
||||
(
|
||||
!all_abi_encode_impls.contains_key(&decl.name),
|
||||
!all_debug_impls.contains_key(&decl.name),
|
||||
)
|
||||
}
|
||||
AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => {
|
||||
let decl = ctx.engines().pe().get_enum(decl_id);
|
||||
!all_abi_encode_impls.contains_key(&decl.name)
|
||||
(
|
||||
!all_abi_encode_impls.contains_key(&decl.name),
|
||||
!all_debug_impls.contains_key(&decl.name),
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
_ => (false, false),
|
||||
};
|
||||
|
||||
let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else {
|
||||
|
@ -593,6 +603,19 @@ impl ty::TyModule {
|
|||
};
|
||||
}
|
||||
|
||||
// Auto impl debug traits only if they are not explicitly implemented
|
||||
if auto_impl_debug_traits {
|
||||
match &node.content {
|
||||
TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
|
||||
| TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
|
||||
let mut ctx = DebugAutoImplContext::new(&mut ctx);
|
||||
let a = ctx.generate_debug_impl(engines, decl);
|
||||
generated.extend(a);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Always auto impl marker traits. If an explicit implementation exists, that will be
|
||||
// reported as an error when type-checking trait impls.
|
||||
if ctx.experimental.error_type {
|
||||
|
|
|
@ -7,6 +7,7 @@ use sway_parse::{lex, Parser};
|
|||
use sway_types::{constants::CONTRACT_ID, ProgramId, Spanned};
|
||||
|
||||
use crate::{
|
||||
build_config::DbgGeneration,
|
||||
language::{
|
||||
parsed::{AstNode, AstNodeContent, Declaration, ExpressionKind},
|
||||
ty::{TyAstNode, TyAstNodeContent},
|
||||
|
@ -25,17 +26,25 @@ pub fn package_with_contract_id(
|
|||
program_id: ProgramId,
|
||||
contract_id_value: String,
|
||||
experimental: crate::ExperimentalFeatures,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<Package, vec1::Vec1<CompileError>> {
|
||||
let package = Package::new(package_name, None, program_id, true);
|
||||
let handler = <_>::default();
|
||||
bind_contract_id_in_root_module(&handler, engines, contract_id_value, package, experimental)
|
||||
.map_err(|_| {
|
||||
let (errors, warnings) = handler.consume();
|
||||
assert!(warnings.is_empty());
|
||||
bind_contract_id_in_root_module(
|
||||
&handler,
|
||||
engines,
|
||||
contract_id_value,
|
||||
package,
|
||||
experimental,
|
||||
dbg_generation,
|
||||
)
|
||||
.map_err(|_| {
|
||||
let (errors, warnings) = handler.consume();
|
||||
assert!(warnings.is_empty());
|
||||
|
||||
// Invariant: `.value == None` => `!errors.is_empty()`.
|
||||
vec1::Vec1::try_from_vec(errors).unwrap()
|
||||
})
|
||||
// Invariant: `.value == None` => `!errors.is_empty()`.
|
||||
vec1::Vec1::try_from_vec(errors).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn bind_contract_id_in_root_module(
|
||||
|
@ -44,6 +53,7 @@ fn bind_contract_id_in_root_module(
|
|||
contract_id_value: String,
|
||||
package: Package,
|
||||
experimental: crate::ExperimentalFeatures,
|
||||
dbg_generation: DbgGeneration,
|
||||
) -> Result<Package, ErrorEmitted> {
|
||||
// this for loop performs a miniature compilation of each const item in the config
|
||||
// FIXME(Centril): Stop parsing. Construct AST directly instead!
|
||||
|
@ -61,7 +71,7 @@ fn bind_contract_id_in_root_module(
|
|||
let attributes = Default::default();
|
||||
// convert to const decl
|
||||
let const_decl_id = to_parsed_lang::item_const_to_constant_declaration(
|
||||
&mut to_parsed_lang::Context::new(crate::BuildTarget::EVM, experimental),
|
||||
&mut to_parsed_lang::Context::new(crate::BuildTarget::EVM, dbg_generation, experimental),
|
||||
handler,
|
||||
engines,
|
||||
const_item,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use sway_features::ExperimentalFeatures;
|
||||
|
||||
use crate::{
|
||||
build_config::DbgGeneration,
|
||||
language::parsed::{Declaration, TreeType},
|
||||
BuildTarget,
|
||||
};
|
||||
|
@ -27,6 +28,9 @@ pub struct Context {
|
|||
/// The build target.
|
||||
build_target: BuildTarget,
|
||||
|
||||
/// Indicates whether the `__dbg` intrinsic generates code or not
|
||||
dbg_generation: DbgGeneration,
|
||||
|
||||
/// The program type.
|
||||
program_type: Option<TreeType>,
|
||||
|
||||
|
@ -36,9 +40,14 @@ pub struct Context {
|
|||
|
||||
impl Context {
|
||||
/// Create a new context.
|
||||
pub fn new(build_target: BuildTarget, experimental: ExperimentalFeatures) -> Self {
|
||||
pub fn new(
|
||||
build_target: BuildTarget,
|
||||
dbg_generation: DbgGeneration,
|
||||
experimental: ExperimentalFeatures,
|
||||
) -> Self {
|
||||
Self {
|
||||
build_target,
|
||||
dbg_generation,
|
||||
experimental,
|
||||
module_has_configurable_block: std::default::Default::default(),
|
||||
destructured_struct_unique_suffix: std::default::Default::default(),
|
||||
|
@ -90,6 +99,10 @@ impl Context {
|
|||
self.build_target
|
||||
}
|
||||
|
||||
pub fn is_dbg_generation_full(&self) -> bool {
|
||||
matches!(self.dbg_generation, DbgGeneration::Full)
|
||||
}
|
||||
|
||||
/// Returns the program type.
|
||||
pub fn program_type(&self) -> Option<TreeType> {
|
||||
self.program_type
|
||||
|
|
|
@ -2009,8 +2009,9 @@ fn expr_func_app_to_expression_kind(
|
|||
|
||||
let (type_arguments, type_arguments_span) = convert_ty_args(context, call_seg.generics_opt)?;
|
||||
|
||||
// Route intrinsic calls to different AST node.
|
||||
// Transform the AST of some intrinsics
|
||||
match Intrinsic::try_from_str(call_seg.name.as_str()) {
|
||||
// "__log(arg)" becomes "__log(encode(arg))"
|
||||
Some(Intrinsic::Log)
|
||||
if context.experimental.new_encoding && last.is_none() && !is_relative_to_root =>
|
||||
{
|
||||
|
@ -2044,6 +2045,214 @@ fn expr_func_app_to_expression_kind(
|
|||
},
|
||||
));
|
||||
}
|
||||
// "__dbg(arg)" in debug becomes "{
|
||||
// let mut f = Formatter { };
|
||||
// f.print_str("[{current_file}:{current_line}:{current_col}] = ");
|
||||
// let arg = arg;
|
||||
// arg.fmt(f);
|
||||
// arg
|
||||
// }"
|
||||
Some(Intrinsic::Dbg)
|
||||
if context.is_dbg_generation_full() && last.is_none() && !is_relative_to_root =>
|
||||
{
|
||||
if arguments.len() != 1 {
|
||||
return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs {
|
||||
name: Intrinsic::Dbg.to_string(),
|
||||
expected: 1,
|
||||
span,
|
||||
}));
|
||||
}
|
||||
|
||||
let f_id: String = format!("f_{}", context.next_for_unique_suffix());
|
||||
let f_ident = BaseIdent::new_no_span(f_id.to_string());
|
||||
|
||||
let f_tid = engines.te().new_unknown();
|
||||
let f_decl_pid = engines.pe().insert(VariableDeclaration {
|
||||
name: f_ident.clone(),
|
||||
type_ascription: GenericArgument::Type(GenericTypeArgument {
|
||||
type_id: f_tid,
|
||||
initial_type_id: f_tid,
|
||||
span: span.clone(),
|
||||
call_path_tree: None,
|
||||
}),
|
||||
body: Expression {
|
||||
kind: ExpressionKind::Struct(Box::new(StructExpression {
|
||||
resolved_call_path_binding: None,
|
||||
call_path_binding: TypeBinding {
|
||||
inner: CallPath {
|
||||
prefixes: vec![],
|
||||
suffix: BaseIdent::new_no_span("Formatter".into()),
|
||||
callpath_type: CallPathType::Ambiguous,
|
||||
},
|
||||
type_arguments: TypeArgs::Regular(vec![]),
|
||||
span: span.clone(),
|
||||
},
|
||||
fields: vec![],
|
||||
})),
|
||||
span: span.clone(),
|
||||
},
|
||||
is_mutable: true,
|
||||
});
|
||||
|
||||
fn get_current_file_from_span(engines: &Engines, span: &Span) -> String {
|
||||
let Some(source_id) = span.source_id() else {
|
||||
return String::new();
|
||||
};
|
||||
let current_file = engines.se().get_path(source_id);
|
||||
|
||||
// find the manifest path of the current span
|
||||
let program_id = engines
|
||||
.se()
|
||||
.get_program_id_from_manifest_path(¤t_file)
|
||||
.unwrap();
|
||||
let manifest_path = engines
|
||||
.se()
|
||||
.get_manifest_path_from_program_id(&program_id)
|
||||
.unwrap();
|
||||
let current_file = current_file
|
||||
.display()
|
||||
.to_string()
|
||||
.replace(&manifest_path.display().to_string(), "");
|
||||
if let Some(current_file) = current_file.strip_prefix("/") {
|
||||
current_file.to_string()
|
||||
} else {
|
||||
current_file
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_node_to_print_str(f_ident: BaseIdent, s: &str, span: &Span) -> AstNode {
|
||||
AstNode {
|
||||
content: AstNodeContent::Expression(Expression {
|
||||
kind: ExpressionKind::MethodApplication(Box::new(
|
||||
MethodApplicationExpression {
|
||||
method_name_binding: TypeBinding {
|
||||
inner: MethodName::FromModule {
|
||||
method_name: BaseIdent::new_no_span("print_str".into()),
|
||||
},
|
||||
type_arguments: TypeArgs::Regular(vec![]),
|
||||
span: span.clone(),
|
||||
},
|
||||
contract_call_params: vec![],
|
||||
arguments: vec![
|
||||
Expression {
|
||||
kind: ExpressionKind::Variable(f_ident.clone()),
|
||||
span: span.clone(),
|
||||
},
|
||||
Expression {
|
||||
kind: ExpressionKind::Literal(Literal::String(
|
||||
Span::from_string(s.to_string()),
|
||||
)),
|
||||
span: span.clone(),
|
||||
},
|
||||
],
|
||||
},
|
||||
)),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
let current_file = get_current_file_from_span(engines, &span);
|
||||
let start_line_col = span.start_line_col_one_index();
|
||||
|
||||
let arg_id: String = format!("arg_{}", context.next_for_unique_suffix());
|
||||
let arg_ident = BaseIdent::new_no_span(arg_id.to_string());
|
||||
|
||||
let block = CodeBlock {
|
||||
contents: vec![
|
||||
// let arg = arguments[0];
|
||||
statement_let_to_ast_nodes_unfold(
|
||||
context,
|
||||
handler,
|
||||
engines,
|
||||
Pattern::AmbiguousSingleIdent(arg_ident.clone()),
|
||||
None,
|
||||
arguments[0].clone(),
|
||||
Span::dummy(),
|
||||
)
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap(),
|
||||
// let mut f = Formatter { };
|
||||
AstNode {
|
||||
content: AstNodeContent::Declaration(Declaration::VariableDeclaration(
|
||||
f_decl_pid,
|
||||
)),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
// f.print_str("[" + <current file> + ":" + <current line> + ":" + <current col> + "] = ");
|
||||
ast_node_to_print_str(
|
||||
f_ident.clone(),
|
||||
&format!(
|
||||
"[{}:{}:{}] = ",
|
||||
current_file, start_line_col.line, start_line_col.col
|
||||
),
|
||||
&span,
|
||||
),
|
||||
// arg.fmt(f);
|
||||
AstNode {
|
||||
content: AstNodeContent::Expression(Expression {
|
||||
kind: ExpressionKind::MethodApplication(Box::new(
|
||||
MethodApplicationExpression {
|
||||
method_name_binding: TypeBinding {
|
||||
inner: MethodName::FromModule {
|
||||
method_name: BaseIdent::new_no_span("fmt".into()),
|
||||
},
|
||||
type_arguments: TypeArgs::Regular(vec![]),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
contract_call_params: vec![],
|
||||
arguments: vec![
|
||||
Expression {
|
||||
kind: ExpressionKind::Variable(arg_ident.clone()),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
Expression {
|
||||
kind: ExpressionKind::Variable(f_ident.clone()),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
],
|
||||
},
|
||||
)),
|
||||
span: Span::dummy(),
|
||||
}),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
// f.print_str(<newline>);
|
||||
ast_node_to_print_str(f_ident.clone(), "\n", &span),
|
||||
// arg
|
||||
AstNode {
|
||||
content: AstNodeContent::Expression(Expression {
|
||||
kind: ExpressionKind::ImplicitReturn(Box::new(Expression {
|
||||
kind: ExpressionKind::AmbiguousVariableExpression(
|
||||
arg_ident.clone(),
|
||||
),
|
||||
span: Span::dummy(),
|
||||
})),
|
||||
span: Span::dummy(),
|
||||
}),
|
||||
span: Span::dummy(),
|
||||
},
|
||||
],
|
||||
whole_block_span: Span::dummy(),
|
||||
};
|
||||
|
||||
return Ok(ExpressionKind::CodeBlock(block));
|
||||
}
|
||||
// ... and in release becomes "arg"
|
||||
Some(Intrinsic::Dbg)
|
||||
if !context.is_dbg_generation_full() && last.is_none() && !is_relative_to_root =>
|
||||
{
|
||||
if arguments.len() != 1 {
|
||||
return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs {
|
||||
name: Intrinsic::Dbg.to_string(),
|
||||
expected: 1,
|
||||
span,
|
||||
}));
|
||||
}
|
||||
return Ok(arguments[0].kind.clone());
|
||||
}
|
||||
Some(intrinsic) if last.is_none() && !is_relative_to_root => {
|
||||
return Ok(ExpressionKind::IntrinsicFunction(
|
||||
IntrinsicFunctionExpression {
|
||||
|
|
|
@ -917,7 +917,7 @@ pub enum CompileError {
|
|||
span: Span,
|
||||
hint: String,
|
||||
},
|
||||
#[error("Call to \"{name}\" expects {expected} arguments")]
|
||||
#[error("Call to \"{name}\" expects {expected} argument(s)")]
|
||||
IntrinsicIncorrectNumArgs {
|
||||
name: String,
|
||||
expected: u64,
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
# Needs to exist at least one line between them
|
||||
remove_generated_code() {
|
||||
START=`grep -n "BEGIN $1" ./src/codec.sw`
|
||||
START=`grep -n "BEGIN $1" ./src/$2`
|
||||
START=${START%:*}
|
||||
END=`grep -n "END $1" ./src/codec.sw`
|
||||
END=`grep -n "END $1" ./src/$2`
|
||||
END=${END%:*}
|
||||
sed -i "$((START+1)),$((END-1))d" ./src/codec.sw
|
||||
sed -i "$((START+1)),$((END-1))d" ./src/$2
|
||||
}
|
||||
|
||||
remove_generated_code "ARRAY_ENCODE"
|
||||
remove_generated_code "ARRAY_ENCODE" "codec.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
|
@ -17,7 +17,7 @@ for ((i=END;i>=START;i--)); do
|
|||
sed -i "s/\/\/ BEGIN ARRAY_ENCODE/\/\/ BEGIN ARRAY_ENCODE\n$CODE/g" ./src/codec.sw
|
||||
done
|
||||
|
||||
remove_generated_code "ARRAY_DECODE"
|
||||
remove_generated_code "ARRAY_DECODE" "codec.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
|
@ -25,7 +25,7 @@ for ((i=END;i>=START;i--)); do
|
|||
sed -i "s/\/\/ BEGIN ARRAY_DECODE/\/\/ BEGIN ARRAY_DECODE\n$CODE/g" ./src/codec.sw
|
||||
done
|
||||
|
||||
remove_generated_code "STRARRAY_ENCODE"
|
||||
remove_generated_code "STRARRAY_ENCODE" "codec.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
|
@ -33,7 +33,7 @@ for ((i=END;i>=START;i--)); do
|
|||
sed -i "s/\/\/ BEGIN STRARRAY_ENCODE/\/\/ BEGIN STRARRAY_ENCODE\n$CODE/g" ./src/codec.sw
|
||||
done
|
||||
|
||||
remove_generated_code "STRARRAY_DECODE"
|
||||
remove_generated_code "STRARRAY_DECODE" "codec.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
|
@ -78,7 +78,7 @@ generate_tuple_encode() {
|
|||
sed -i "s/\/\/ BEGIN TUPLES_ENCODE/\/\/ BEGIN TUPLES_ENCODE\n$CODE/g" ./src/codec.sw
|
||||
}
|
||||
|
||||
remove_generated_code "TUPLES_ENCODE"
|
||||
remove_generated_code "TUPLES_ENCODE" "codec.sw"
|
||||
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
|
||||
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X Y"
|
||||
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X"
|
||||
|
@ -141,7 +141,7 @@ generate_tuple_decode() {
|
|||
sed -i "s/\/\/ BEGIN TUPLES_DECODE/\/\/ BEGIN TUPLES_DECODE\n$CODE/g" ./src/codec.sw
|
||||
}
|
||||
|
||||
remove_generated_code "TUPLES_DECODE"
|
||||
remove_generated_code "TUPLES_DECODE" "codec.sw"
|
||||
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
|
||||
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X Y"
|
||||
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X"
|
||||
|
@ -168,3 +168,86 @@ generate_tuple_decode "A B C D"
|
|||
generate_tuple_decode "A B C"
|
||||
generate_tuple_decode "A B"
|
||||
generate_tuple_decode "A"
|
||||
|
||||
generate_tuple_debug() {
|
||||
local CODE="impl<"
|
||||
|
||||
local elements=("$1")
|
||||
for element in ${elements[@]}
|
||||
do
|
||||
CODE="$CODE $element,"
|
||||
done
|
||||
|
||||
CODE="$CODE> Debug for ("
|
||||
|
||||
for element in ${elements[@]}
|
||||
do
|
||||
CODE="$CODE $element,"
|
||||
done
|
||||
|
||||
CODE="$CODE) where "
|
||||
|
||||
for element in ${elements[@]}
|
||||
do
|
||||
CODE="$CODE $element: Debug, "
|
||||
done
|
||||
|
||||
CODE="$CODE{ fn fmt(self, ref mut f: Formatter) { let mut f = f.debug_tuple(\"\");"
|
||||
|
||||
i=0
|
||||
for element in ${elements[@]}
|
||||
do
|
||||
CODE="$CODE let mut f = f.field(self.$i);"
|
||||
i=$((i+1))
|
||||
done
|
||||
|
||||
CODE="$CODE f.finish(); } }"
|
||||
|
||||
sed -i "s/\/\/ BEGIN TUPLES_DEBUG/\/\/ BEGIN TUPLES_DEBUG\n$CODE/g" ./src/debug.sw
|
||||
}
|
||||
|
||||
remove_generated_code "TUPLES_DEBUG" "debug.sw"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X Y"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q R"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P Q"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O P"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N O"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M N"
|
||||
generate_tuple_debug "A B C D E F G H I J K L M"
|
||||
generate_tuple_debug "A B C D E F G H I J K L"
|
||||
generate_tuple_debug "A B C D E F G H I J K"
|
||||
generate_tuple_debug "A B C D E F G H I J"
|
||||
generate_tuple_debug "A B C D E F G H I"
|
||||
generate_tuple_debug "A B C D E F G H"
|
||||
generate_tuple_debug "A B C D E F G"
|
||||
generate_tuple_debug "A B C D E F"
|
||||
generate_tuple_debug "A B C D E"
|
||||
generate_tuple_debug "A B C D"
|
||||
generate_tuple_debug "A B C"
|
||||
generate_tuple_debug "A B"
|
||||
generate_tuple_debug "A"
|
||||
|
||||
remove_generated_code "STRARRAY_DEBUG" "debug.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
CODE="impl Debug for str[$i] { fn fmt(self, ref mut f: Formatter) { use ::str::*; from_str_array(self).fmt(f); } }"
|
||||
sed -i "s/\/\/ BEGIN STRARRAY_DEBUG/\/\/ BEGIN STRARRAY_DEBUG\n$CODE/g" ./src/debug.sw
|
||||
done
|
||||
|
||||
remove_generated_code "ARRAY_DEBUG" "debug.sw"
|
||||
START=1
|
||||
END=64
|
||||
for ((i=END;i>=START;i--)); do
|
||||
CODE="#[cfg(experimental_const_generics = false)]\nimpl<T> Debug for [T; $i] where T: Debug { fn fmt(self, ref mut f: Formatter) { let mut f = f.debug_list(); let mut i = 0; while i < $i { f = f.entry(self[i]); i += 1; }; f.finish(); } }"
|
||||
sed -i "s/\/\/ BEGIN ARRAY_DEBUG/\/\/ BEGIN ARRAY_DEBUG\n$CODE/g" ./src/debug.sw
|
||||
done
|
||||
|
||||
cargo r -p forc-fmt --release -- -p .
|
2576
sway-lib-std/src/debug.sw
Normal file
2576
sway-lib-std/src/debug.sw
Normal file
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@ pub mod ops;
|
|||
pub mod raw_ptr;
|
||||
pub mod raw_slice;
|
||||
pub mod codec;
|
||||
pub mod debug;
|
||||
pub mod slice;
|
||||
pub mod constants;
|
||||
pub mod error_signals;
|
||||
|
|
|
@ -50,3 +50,4 @@ pub use ::codec::*;
|
|||
pub use ::str::*;
|
||||
#[cfg(experimental_error_type = true)]
|
||||
pub use ::marker::*;
|
||||
pub use ::debug::*;
|
||||
|
|
|
@ -10,6 +10,7 @@ use ::codec::*;
|
|||
use ::ops::*;
|
||||
use ::raw_slice::*;
|
||||
use ::clone::Clone;
|
||||
use ::debug::{Debug, DebugList, Formatter};
|
||||
|
||||
struct RawVec<T> {
|
||||
ptr: raw_ptr,
|
||||
|
@ -905,3 +906,18 @@ where
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Vec<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
fn fmt(self, ref mut f: Formatter) {
|
||||
let mut l = f.debug_list();
|
||||
|
||||
for elem in self.iter() {
|
||||
let _ = l.entry(elem);
|
||||
}
|
||||
|
||||
l.finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ impl Session {
|
|||
let path = uri.to_file_path().unwrap();
|
||||
let source_id = { engines.se().get_source_id(&path) };
|
||||
engines.clear_module(&source_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -326,6 +327,7 @@ pub fn compile(
|
|||
retrigger_compilation,
|
||||
&[],
|
||||
&[sway_features::Feature::NewEncoding],
|
||||
sway_core::DbgGeneration::None,
|
||||
)
|
||||
.map_err(LanguageServerError::FailedToCompile)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ struct MyStruct<T, U> {
|
|||
o: Option<Identity>,
|
||||
}
|
||||
|
||||
impl<T, U> Debug for MyStruct<T, U> {
|
||||
fn fmt(self, f: Formatter) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct Simple {
|
||||
x: u8,
|
||||
}
|
||||
|
|
|
@ -1239,6 +1239,7 @@ fn go_to_definition_for_functions() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "https://github.com/FuelLabs/sway/issues/7025"]
|
||||
fn go_to_definition_for_structs() {
|
||||
run_async!({
|
||||
let server = ServerState::default();
|
||||
|
|
|
@ -3,6 +3,7 @@ use parking_lot::RwLock;
|
|||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
/// The Source Engine manages a relationship between file paths and their corresponding
|
||||
|
@ -47,7 +48,10 @@ impl SourceEngine {
|
|||
}
|
||||
|
||||
pub fn is_source_id_autogenerated(&self, source_id: &SourceId) -> bool {
|
||||
self.get_path(source_id).starts_with("<autogenerated>")
|
||||
self.get_path(source_id)
|
||||
.display()
|
||||
.to_string()
|
||||
.contains("<autogenerated>")
|
||||
}
|
||||
|
||||
/// This function retrieves an integer-based source ID for a provided path buffer.
|
||||
|
@ -91,8 +95,20 @@ impl SourceEngine {
|
|||
source_id
|
||||
}
|
||||
|
||||
pub fn get_autogenerated_source_id(&self, program_id: ProgramId) -> SourceId {
|
||||
self.get_source_id_with_program_id(&Self::AUTOGENERATED_PATH.into(), program_id)
|
||||
/// Return the associated autogenerated pseudo file for the passed `source_id`.
|
||||
/// Example: main.autogenerated.sw for main.sw
|
||||
///
|
||||
/// Returns `None`, if `source_id` does not have a valid path.
|
||||
pub fn get_associated_autogenerated_source_id(&self, source_id: &SourceId) -> Option<SourceId> {
|
||||
let path = self.get_path(source_id);
|
||||
let file_name = PathBuf::from_str(path.file_name()?.to_str()?).ok()?;
|
||||
let path = path.with_file_name(format!(
|
||||
"{}.{}.{}",
|
||||
file_name.file_stem()?.to_str()?,
|
||||
Self::AUTOGENERATED_PATH,
|
||||
file_name.extension()?.to_str()?
|
||||
));
|
||||
Some(self.get_source_id_with_program_id(&path, source_id.program_id()))
|
||||
}
|
||||
|
||||
/// This function provides the file path corresponding to a specified source ID.
|
||||
|
|
|
@ -7,11 +7,12 @@ use forc_client::{
|
|||
NodeTarget,
|
||||
};
|
||||
use forc_pkg::{BuildProfile, Built, BuiltPackage, PrintOpts};
|
||||
use forc_test::ecal::EcalSyscallHandler;
|
||||
use fuel_tx::TransactionBuilder;
|
||||
use fuel_vm::checked_transaction::builder::TransactionBuilderExt;
|
||||
use fuel_vm::fuel_tx::{self, consensus_parameters::ConsensusParametersV1};
|
||||
use fuel_vm::interpreter::Interpreter;
|
||||
use fuel_vm::prelude::*;
|
||||
use fuel_vm::{checked_transaction::builder::TransactionBuilderExt, interpreter::NotSupportedEcal};
|
||||
use futures::Future;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
@ -143,7 +144,7 @@ pub(crate) async fn runs_on_node(
|
|||
}
|
||||
|
||||
pub(crate) enum VMExecutionResult {
|
||||
Fuel(ProgramState, Vec<Receipt>),
|
||||
Fuel(ProgramState, Vec<Receipt>, Box<EcalSyscallHandler>),
|
||||
Evm(revm::primitives::result::ExecutionResult),
|
||||
}
|
||||
|
||||
|
@ -207,13 +208,14 @@ pub(crate) fn runs_in_vm(
|
|||
.map_err(|e| anyhow::anyhow!("{e:?}"))?;
|
||||
|
||||
let mem_instance = MemoryInstance::new();
|
||||
let mut i: Interpreter<_, _, _, NotSupportedEcal> =
|
||||
let mut i: Interpreter<_, _, _, EcalSyscallHandler> =
|
||||
Interpreter::with_storage(mem_instance, storage, Default::default());
|
||||
let transition = i.transact(tx).map_err(anyhow::Error::msg)?;
|
||||
|
||||
Ok(VMExecutionResult::Fuel(
|
||||
*transition.state(),
|
||||
transition.receipts().to_vec(),
|
||||
Box::new(i.ecal_state().clone()),
|
||||
))
|
||||
}
|
||||
BuildTarget::EVM => {
|
||||
|
|
|
@ -11,6 +11,7 @@ use colored::*;
|
|||
use core::fmt;
|
||||
use forc_pkg::manifest::{GenericManifestFile, ManifestFile};
|
||||
use forc_pkg::BuildProfile;
|
||||
use forc_test::ecal::Syscall;
|
||||
use forc_util::tx_utils::decode_log_data;
|
||||
use fuel_vm::fuel_tx;
|
||||
use fuel_vm::fuel_types::canonical::Input;
|
||||
|
@ -404,8 +405,27 @@ impl TestContext {
|
|||
|
||||
let result = harness::runs_in_vm(compiled.clone(), script_data, witness_data)?;
|
||||
let actual_result = match result {
|
||||
harness::VMExecutionResult::Fuel(state, receipts) => {
|
||||
harness::VMExecutionResult::Fuel(state, receipts, ecal) => {
|
||||
print_receipts(output, &receipts);
|
||||
|
||||
use std::fmt::Write;
|
||||
let _ = writeln!(output, " {}", "Captured Output".green().bold());
|
||||
for captured in ecal.captured.iter() {
|
||||
match captured {
|
||||
Syscall::Write { bytes, .. } => {
|
||||
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
|
||||
output.push_str(s);
|
||||
}
|
||||
Syscall::Unknown { ra, rb, rc, rd } => {
|
||||
let _ = writeln!(
|
||||
output,
|
||||
"Unknown ecal: {} {} {} {}",
|
||||
ra, rb, rc, rd
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match state {
|
||||
ProgramState::Return(v) => TestResult::Return(v),
|
||||
ProgramState::ReturnData(digest) => {
|
||||
|
|
|
@ -13,3 +13,4 @@ assert.sw
|
|||
error_signals.sw
|
||||
logging.sw
|
||||
revert.sw
|
||||
debug.sw
|
||||
|
|
|
@ -16,3 +16,4 @@ pub mod logging;
|
|||
pub mod revert;
|
||||
pub mod assert;
|
||||
pub mod prelude;
|
||||
pub mod debug;
|
||||
|
|
|
@ -46,3 +46,4 @@ primitive_conversions/u32.sw
|
|||
primitive_conversions/u64.sw
|
||||
primitive_conversions/u256.sw
|
||||
primitive_conversions/u8.sw
|
||||
debug.sw
|
||||
|
|
|
@ -31,5 +31,6 @@ pub mod math;
|
|||
pub mod array_conversions;
|
||||
pub mod bytes_conversions;
|
||||
pub mod clone;
|
||||
pub mod debug;
|
||||
|
||||
pub mod prelude;
|
||||
|
|
|
@ -9,3 +9,4 @@ raw_slice.sw
|
|||
codec.sw
|
||||
str.sw
|
||||
marker.sw
|
||||
debug.sw
|
||||
|
|
|
@ -11,5 +11,5 @@ pub mod raw_slice;
|
|||
pub mod codec;
|
||||
pub mod r#str;
|
||||
pub mod marker;
|
||||
|
||||
pub mod prelude;
|
||||
pub mod debug;
|
||||
|
|
|
@ -14,3 +14,4 @@ pub use ::codec::*;
|
|||
pub use ::str::*;
|
||||
#[cfg(experimental_error_type = true)]
|
||||
pub use ::marker::*;
|
||||
pub use ::debug::*;
|
||||
|
|
|
@ -15,3 +15,4 @@ logging.sw
|
|||
option.sw
|
||||
result.sw
|
||||
revert.sw
|
||||
debug.sw
|
||||
|
|
|
@ -18,3 +18,4 @@ pub mod result;
|
|||
pub mod option;
|
||||
pub mod assert;
|
||||
pub mod prelude;
|
||||
pub mod debug;
|
||||
|
|
|
@ -20,3 +20,4 @@ vec.sw
|
|||
iterator.sw
|
||||
convert.sw
|
||||
clone.sw
|
||||
debug.sw
|
||||
|
|
|
@ -22,5 +22,6 @@ pub mod alloc;
|
|||
pub mod iterator;
|
||||
pub mod clone;
|
||||
pub mod vec;
|
||||
pub mod debug;
|
||||
|
||||
pub mod prelude;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[[package]]
|
||||
name = "dbg_wrong_args_count"
|
||||
source = "member"
|
||||
dependencies = ["std"]
|
||||
|
||||
[[package]]
|
||||
name = "std"
|
||||
source = "path+from-root-7E04A7BB9FC18A6D"
|
|
@ -0,0 +1,8 @@
|
|||
[project]
|
||||
authors = ["Fuel Labs <contact@fuel.sh>"]
|
||||
entry = "main.sw"
|
||||
license = "Apache-2.0"
|
||||
name = "dbg_wrong_args_count"
|
||||
|
||||
[dependencies]
|
||||
std = { path = "../../../reduced_std_libs/sway-lib-std-core" }
|
|
@ -0,0 +1,6 @@
|
|||
script;
|
||||
|
||||
fn main() {
|
||||
let _ = __dbg();
|
||||
let _ = __dbg(1, 2);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: test/src/snapshot/mod.rs
|
||||
---
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count
|
||||
exit status: 1
|
||||
output:
|
||||
Building test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count
|
||||
Compiling library std (test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core)
|
||||
Compiling script dbg_wrong_args_count (test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count)
|
||||
error
|
||||
--> test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw:4:13
|
||||
|
|
||||
2 |
|
||||
3 | fn main() {
|
||||
4 | let _ = __dbg();
|
||||
| ^^^^^^^ Call to "dbg" expects 1 argument(s)
|
||||
5 | let _ = __dbg(1, 2);
|
||||
6 | }
|
||||
|
|
||||
____
|
||||
|
||||
error
|
||||
--> test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw:5:13
|
||||
|
|
||||
3 | fn main() {
|
||||
4 | let _ = __dbg();
|
||||
5 | let _ = __dbg(1, 2);
|
||||
| ^^^^^^^^^^^ Call to "dbg" expects 1 argument(s)
|
||||
6 | }
|
||||
|
|
||||
____
|
||||
|
||||
Aborting due to 2 errors.
|
||||
error: Failed to compile dbg_wrong_args_count
|
|
@ -4,4 +4,4 @@ category = "fail"
|
|||
# check: $()buffer: __encode_buffer_append(buffer.buffer)
|
||||
|
||||
# check: $()buffer: __encode_buffer_append(buffer.buffer)
|
||||
# nextln: $()Call to "encode_buffer_append" expects 2 arguments
|
||||
# nextln: $()Call to "encode_buffer_append" expects 2 argument(s)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
category = "fail"
|
||||
|
||||
# check: $()buffer: __encode_buffer_empty(self),
|
||||
# nextln: $()Call to "encode_buffer_empty" expects 0 arguments
|
||||
# nextln: $()Call to "encode_buffer_empty" expects 0 argument(s)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: test/src/snapshot/mod.rs
|
||||
assertion_line: 125
|
||||
---
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated --release
|
||||
exit status: 0
|
||||
|
@ -165,7 +164,7 @@ warning: Struct field is deprecated
|
|||
____
|
||||
|
||||
warning: Function is deprecated
|
||||
--> <autogenerated>:5:15
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/src/main.<autogenerated>.sw:5:15
|
||||
|
|
||||
...
|
||||
5 | let _result = __contract_entry_deprecated_to_be_abi_method();
|
||||
|
@ -174,7 +173,7 @@ warning: Function is deprecated
|
|||
____
|
||||
|
||||
warning: Function is deprecated
|
||||
--> <autogenerated>:9:15
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/src/main.<autogenerated>.sw:9:15
|
||||
|
|
||||
...
|
||||
9 | let _result = __contract_entry_deprecated_abi_provided_method();
|
||||
|
|
|
@ -271,7 +271,7 @@ script {
|
|||
!39 = span !3 2804 2817
|
||||
!40 = span !3 2857 2896
|
||||
!41 = (!32 !33 !40)
|
||||
!42 = "<autogenerated>"
|
||||
!42 = "test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/src/main.<autogenerated>.sw"
|
||||
!43 = span !42 0 125
|
||||
!44 = fn_name_span !42 7 14
|
||||
!45 = (!43 !44)
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
---
|
||||
source: test/src/snapshot/mod.rs
|
||||
---
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated
|
||||
exit status: 0
|
||||
output:
|
||||
Building test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated
|
||||
Compiling library std (test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert)
|
||||
Compiling script deprecated (test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated)
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.<autogenerated>.sw:4:39
|
||||
|
|
||||
2 | #[allow(dead_code)]
|
||||
3 | fn abi_encode(self, buffer: Buffer) -> Buffer {
|
||||
4 | let buffer = self.a.abi_encode(buffer);
|
||||
| - deprecated struct field
|
||||
5 | let buffer = self.b.abi_encode(buffer);
|
||||
6 |
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.<autogenerated>.sw:4:21
|
||||
|
|
||||
2 | #[allow(dead_code)]
|
||||
3 | fn abi_decode(ref mut buffer: BufferReader) -> Self {
|
||||
4 | Self { a: buffer.decode::<u64>(),b: buffer.decode::<u64>(), }
|
||||
| ---- deprecated struct
|
||||
5 | }
|
||||
6 | }
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.<autogenerated>.sw:5:25
|
||||
|
|
||||
3 | fn abi_decode(ref mut buffer: BufferReader) -> Self {
|
||||
4 | let variant: u64 = buffer.decode::<u64>();
|
||||
5 | match variant { 0 => B::A,
|
||||
| - deprecated enum
|
||||
6 | 1 => B::B,
|
||||
7 | _ => __revert(0), }
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.<autogenerated>.sw:6:9
|
||||
|
|
||||
4 | let variant: u64 = buffer.decode::<u64>();
|
||||
5 | match variant { 0 => B::A,
|
||||
6 | 1 => B::B,
|
||||
| - deprecated enum
|
||||
7 | _ => __revert(0), }
|
||||
8 |
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.<autogenerated>.sw:6:9
|
||||
|
|
||||
4 | let variant: u64 = buffer.decode::<u64>();
|
||||
5 | match variant { 0 => B::A,
|
||||
6 | 1 => B::B,
|
||||
| - deprecated enum variant
|
||||
7 | _ => __revert(0), }
|
||||
8 |
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:33:17
|
||||
|
|
||||
31 | // TODO: support for traits, abis and their methods
|
||||
32 | pub fn main() {
|
||||
33 | let mut a = A { a: 0, b: 0 };
|
||||
| - deprecated struct
|
||||
34 | let mut a_ref = &a;
|
||||
35 | (*a_ref).a = 1;
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:35:14
|
||||
|
|
||||
33 | let mut a = A { a: 0, b: 0 };
|
||||
34 | let mut a_ref = &a;
|
||||
35 | (*a_ref).a = 1;
|
||||
| - deprecated struct field
|
||||
36 | let b = B::A;
|
||||
37 | depr(a);
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:36:16
|
||||
|
|
||||
34 | let mut a_ref = &a;
|
||||
35 | (*a_ref).a = 1;
|
||||
36 | let b = B::A;
|
||||
| - deprecated enum
|
||||
37 | depr(a);
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:37:5
|
||||
|
|
||||
35 | (*a_ref).a = 1;
|
||||
36 | let b = B::A;
|
||||
37 | depr(a);
|
||||
| ---- deprecated function
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
39 | depr_b(b);
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:38:10
|
||||
|
|
||||
36 | let b = B::A;
|
||||
37 | depr(a);
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
| - deprecated struct
|
||||
39 | depr_b(b);
|
||||
40 | depr_b(B::A);
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:38:5
|
||||
|
|
||||
36 | let b = B::A;
|
||||
37 | depr(a);
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
| ---- deprecated function
|
||||
39 | depr_b(b);
|
||||
40 | depr_b(B::A);
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:39:5
|
||||
|
|
||||
37 | depr(a);
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
39 | depr_b(b);
|
||||
| ------ deprecated function
|
||||
40 | depr_b(B::A);
|
||||
41 | fun(a);
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:40:15
|
||||
|
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
39 | depr_b(b);
|
||||
40 | depr_b(B::A);
|
||||
| - deprecated enum
|
||||
41 | fun(a);
|
||||
42 | fun(A { a: 0, b: 0 });
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:40:5
|
||||
|
|
||||
38 | depr(A { a: 0, b: 0 });
|
||||
39 | depr_b(b);
|
||||
40 | depr_b(B::A);
|
||||
| ------ deprecated function
|
||||
41 | fun(a);
|
||||
42 | fun(A { a: 0, b: 0 });
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:42:9
|
||||
|
|
||||
40 | depr_b(B::A);
|
||||
41 | fun(a);
|
||||
42 | fun(A { a: 0, b: 0 });
|
||||
| - deprecated struct
|
||||
43 | let _ = a.a;
|
||||
44 | let _ = a.b;
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:43:15
|
||||
|
|
||||
41 | fun(a);
|
||||
42 | fun(A { a: 0, b: 0 });
|
||||
43 | let _ = a.a;
|
||||
| - deprecated struct field
|
||||
44 | let _ = a.b;
|
||||
45 | let _ = B::A;
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:45:16
|
||||
|
|
||||
43 | let _ = a.a;
|
||||
44 | let _ = a.b;
|
||||
45 | let _ = B::A;
|
||||
| - deprecated enum
|
||||
46 | let _ = B::B;
|
||||
47 | a.fun();
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:46:16
|
||||
|
|
||||
44 | let _ = a.b;
|
||||
45 | let _ = B::A;
|
||||
46 | let _ = B::B;
|
||||
| - deprecated enum
|
||||
47 | a.fun();
|
||||
48 | }
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:46:16
|
||||
|
|
||||
44 | let _ = a.b;
|
||||
45 | let _ = B::A;
|
||||
46 | let _ = B::B;
|
||||
| - deprecated enum variant
|
||||
47 | a.fun();
|
||||
48 | }
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:47:7
|
||||
|
|
||||
45 | let _ = B::A;
|
||||
46 | let _ = B::B;
|
||||
47 | a.fun();
|
||||
| --- deprecated struct
|
||||
48 | }
|
||||
|
|
||||
____
|
||||
|
||||
warning
|
||||
--> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:47:7
|
||||
|
|
||||
45 | let _ = B::A;
|
||||
46 | let _ = B::B;
|
||||
47 | a.fun();
|
||||
| --- deprecated function
|
||||
48 | }
|
||||
|
|
||||
____
|
||||
|
||||
Compiled script "deprecated" with 21 warnings.
|
||||
Finished debug [unoptimized + fuel] target(s) [312 B] in ???
|
|
@ -0,0 +1,8 @@
|
|||
[[package]]
|
||||
name = "dbg"
|
||||
source = "member"
|
||||
dependencies = ["std"]
|
||||
|
||||
[[package]]
|
||||
name = "std"
|
||||
source = "path+from-root-2AFF83EAEE091763"
|
|
@ -0,0 +1,8 @@
|
|||
[project]
|
||||
authors = ["Fuel Labs <contact@fuel.sh>"]
|
||||
license = "Apache-2.0"
|
||||
entry = "main.sw"
|
||||
name = "dbg"
|
||||
|
||||
[dependencies]
|
||||
std = { path = "../../../../../reduced_std_libs/sway-lib-std-core" }
|
|
@ -0,0 +1,4 @@
|
|||
cmds = [
|
||||
"forc build --path {root} --asm final | grep ecal",
|
||||
"forc build --path {root} --release --asm final | grep ecal"
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
script;
|
||||
|
||||
struct S {}
|
||||
enum E { None: (), Some: S }
|
||||
|
||||
fn main() -> u64 {
|
||||
// Primitive types
|
||||
let _ = __dbg(8u8);
|
||||
let _ = __dbg(16u16);
|
||||
let _ = __dbg(32u32);
|
||||
let _ = __dbg(64u64);
|
||||
let _ = __dbg(0x100u256);
|
||||
|
||||
// strings
|
||||
let _ = __dbg("Hello!");
|
||||
let _ = __dbg(__to_str_array("Hello!"));
|
||||
|
||||
// Aggregates
|
||||
let _ = __dbg((1u64, 2u64));
|
||||
let _ = __dbg([1u64, 2u64]);
|
||||
|
||||
// Strucs and Enum
|
||||
let _ = __dbg(S { });
|
||||
let _ = __dbg(E::None);
|
||||
let _ = __dbg(E::Some(S { }));
|
||||
|
||||
// should return its argument
|
||||
__dbg(11u64)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: test/src/snapshot/mod.rs
|
||||
---
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg --asm final | grep ecal
|
||||
ecal $r2 $r3 $r0 $r1 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r4 $r2 $r3 $r0 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg --release --asm final | grep ecal
|
|
@ -0,0 +1,4 @@
|
|||
category = "run"
|
||||
expected_result = { action = "return", value = 1 }
|
||||
expected_result_new_encoding = { action = "return_data", value = "000000000000000B" }
|
||||
expected_warnings = 10
|
|
@ -0,0 +1,8 @@
|
|||
[[package]]
|
||||
name = "dbg_release"
|
||||
source = "member"
|
||||
dependencies = ["std"]
|
||||
|
||||
[[package]]
|
||||
name = "std"
|
||||
source = "path+from-root-A3D53D8266747D89"
|
|
@ -0,0 +1,9 @@
|
|||
[project]
|
||||
authors = ["Fuel Labs <contact@fuel.sh>"]
|
||||
license = "Apache-2.0"
|
||||
entry = "main.sw"
|
||||
name = "dbg_release"
|
||||
force-dbg-in-release = true
|
||||
|
||||
[dependencies]
|
||||
std = { path = "../../../../../reduced_std_libs/sway-lib-std-core" }
|
|
@ -0,0 +1,4 @@
|
|||
cmds = [
|
||||
"forc build --path {root} --asm final | grep ecal",
|
||||
"forc build --path {root} --release --asm final | grep ecal"
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
script;
|
||||
|
||||
struct S {}
|
||||
enum E { None: (), Some: S }
|
||||
|
||||
fn main() -> u64 {
|
||||
// Primitive types
|
||||
let _ = __dbg(8u8);
|
||||
let _ = __dbg(16u16);
|
||||
let _ = __dbg(32u32);
|
||||
let _ = __dbg(64u64);
|
||||
let _ = __dbg(0x100u256);
|
||||
|
||||
// strings
|
||||
let _ = __dbg("Hello!");
|
||||
let _ = __dbg(__to_str_array("Hello!"));
|
||||
|
||||
// Aggregates
|
||||
let _ = __dbg((1u64, 2u64));
|
||||
let _ = __dbg([1u64, 2u64]);
|
||||
|
||||
// Strucs and Enum
|
||||
let _ = __dbg(S { });
|
||||
let _ = __dbg(E::None);
|
||||
let _ = __dbg(E::Some(S { }));
|
||||
|
||||
// should return its argument
|
||||
__dbg(11u64)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: test/src/snapshot/mod.rs
|
||||
---
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release --asm final | grep ecal
|
||||
ecal $r2 $r3 $r0 $r1 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
ecal $r4 $r2 $r3 $r0 ; ecal id fd buf count
|
||||
ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count
|
||||
|
||||
> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release --release --asm final | grep ecal
|
||||
ecal $r2 $r6 $r0 $r1 ; ecal id fd buf count
|
||||
ecal $r7 $r8 $r3 $r6 ; ecal id fd buf count
|
||||
ecal $r3 $r4 $r2 $r0 ; ecal id fd buf count
|
||||
ecal $r3 $r4 $r1 $r2 ; ecal id fd buf count
|
|
@ -0,0 +1,4 @@
|
|||
category = "run"
|
||||
expected_result = { action = "return", value = 1 }
|
||||
expected_result_new_encoding = { action = "return_data", value = "000000000000000B" }
|
||||
expected_warnings = 10
|
|
@ -236,6 +236,7 @@ pub(super) async fn run(
|
|||
path.clone(),
|
||||
PathBuf::from("/"),
|
||||
build_target,
|
||||
sway_core::DbgGeneration::Full,
|
||||
);
|
||||
|
||||
// Include unit tests in the build.
|
||||
|
|
|
@ -13,7 +13,7 @@ const REDUCED_LIB_CONFIG_FILE_NAME: &str = "reduced_lib.config";
|
|||
/// Creates the reduced versions of `std` libraries based on the list of
|
||||
/// modules defined in [REDUCED_LIB_CONFIG_FILE_NAME] file for each reduced library
|
||||
/// available in the [REDUCED_STD_LIBS_DIR_NAME].
|
||||
pub(crate) fn create() -> Result<()> {
|
||||
pub fn create() -> Result<()> {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let reduced_libs_dir = format!("{manifest_dir}/src/e2e_vm_tests/{REDUCED_STD_LIBS_DIR_NAME}");
|
||||
let std_lib_src_dir = format!("{manifest_dir}/../sway-lib-std/src");
|
||||
|
|
|
@ -77,40 +77,60 @@ pub(super) async fn run(filter_regex: Option<®ex::Regex>) -> Result<()> {
|
|||
|
||||
let _ = writeln!(&mut snapshot, "> {}", cmd);
|
||||
|
||||
// known commands
|
||||
let cmd = if let Some(cmd) = cmd.strip_prefix("forc doc ") {
|
||||
FORC_DOC_COMPILATION.call_once(|| {
|
||||
compile_forc_doc();
|
||||
});
|
||||
format!("target/release/forc-doc {cmd} 1>&2")
|
||||
} else if let Some(cmd) = cmd.strip_prefix("forc ") {
|
||||
FORC_COMPILATION.call_once(|| {
|
||||
compile_forc();
|
||||
});
|
||||
format!("target/release/forc {cmd} 1>&2")
|
||||
} else {
|
||||
panic!("Not supported. Possible commands: forc")
|
||||
};
|
||||
let mut last_output: Option<String> = None;
|
||||
|
||||
let o = duct::cmd!("bash", "-c", cmd.clone())
|
||||
.dir(repo_root.clone())
|
||||
.stderr_to_stdout()
|
||||
.stdout_capture()
|
||||
.env("COLUMNS", "10")
|
||||
.unchecked()
|
||||
.start()
|
||||
.unwrap();
|
||||
let o = o.wait().unwrap();
|
||||
for cmd in cmd.split("|") {
|
||||
let cmd = cmd.trim();
|
||||
|
||||
let _ = writeln!(
|
||||
&mut snapshot,
|
||||
"{}",
|
||||
clean_output(&format!(
|
||||
// known commands
|
||||
let cmd = if let Some(cmd) = cmd.strip_prefix("forc doc ") {
|
||||
FORC_DOC_COMPILATION.call_once(|| {
|
||||
compile_forc_doc();
|
||||
});
|
||||
format!("target/release/forc-doc {cmd} 1>&2")
|
||||
} else if let Some(cmd) = cmd.strip_prefix("forc ") {
|
||||
FORC_COMPILATION.call_once(|| {
|
||||
compile_forc();
|
||||
});
|
||||
format!("target/release/forc {cmd} 1>&2")
|
||||
} else if let Some(cmd) = cmd.strip_prefix("grep ") {
|
||||
let arg = cmd.trim();
|
||||
if let Some(l) = last_output.take() {
|
||||
let mut new_output = String::new();
|
||||
for line in l.lines() {
|
||||
if line.contains(arg) {
|
||||
new_output.push_str(line);
|
||||
new_output.push('\n');
|
||||
}
|
||||
}
|
||||
last_output = Some(new_output);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
panic!("Not supported. Possible commands: forc")
|
||||
};
|
||||
|
||||
let o = duct::cmd!("bash", "-c", cmd.clone())
|
||||
.dir(repo_root.clone())
|
||||
.stderr_to_stdout()
|
||||
.stdout_capture();
|
||||
|
||||
let o = if let Some(last_output) = last_output.as_ref() {
|
||||
o.stdin_bytes(last_output.as_bytes())
|
||||
} else {
|
||||
o
|
||||
};
|
||||
|
||||
let o = o.env("COLUMNS", "10").unchecked().start().unwrap();
|
||||
let o = o.wait().unwrap();
|
||||
last_output = Some(clean_output(&format!(
|
||||
"exit status: {}\noutput:\n{}",
|
||||
o.status.code().unwrap(),
|
||||
std::str::from_utf8(&o.stdout).unwrap(),
|
||||
))
|
||||
);
|
||||
)));
|
||||
}
|
||||
|
||||
let _ = writeln!(&mut snapshot, "{}", last_output.unwrap_or_default());
|
||||
}
|
||||
|
||||
fn stdout(root: &str, snapshot: &str) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue