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
|
@ -244,4 +244,6 @@ StringArray
|
|||
StringSlice
|
||||
calldata
|
||||
Cfg
|
||||
evm
|
||||
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`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue