sway/sway-lib-std/generate.sh
Daniel Frederico Lins Leite f736fce7ac
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.
2025-04-22 07:49:31 -03:00

253 lines
No EOL
8.7 KiB
Bash
Executable file

#! /bin/bash
# Needs to exist at least one line between them
remove_generated_code() {
START=`grep -n "BEGIN $1" ./src/$2`
START=${START%:*}
END=`grep -n "END $1" ./src/$2`
END=${END%:*}
sed -i "$((START+1)),$((END-1))d" ./src/$2
}
remove_generated_code "ARRAY_ENCODE" "codec.sw"
START=1
END=64
for ((i=END;i>=START;i--)); do
CODE="#[cfg(experimental_const_generics = false)]\nimpl<T> AbiEncode for [T; $i] where T: AbiEncode { fn abi_encode(self, buffer: Buffer) -> Buffer { let mut buffer = buffer; let mut i = 0; while i < $i { buffer = self[i].abi_encode(buffer); i += 1; }; buffer } }"
sed -i "s/\/\/ BEGIN ARRAY_ENCODE/\/\/ BEGIN ARRAY_ENCODE\n$CODE/g" ./src/codec.sw
done
remove_generated_code "ARRAY_DECODE" "codec.sw"
START=1
END=64
for ((i=END;i>=START;i--)); do
CODE="#[cfg(experimental_const_generics = false)]\nimpl<T> AbiDecode for [T; $i] where T: AbiDecode { fn abi_decode(ref mut buffer: BufferReader) -> [T; $i] { let first: T = buffer.decode::<T>(); let mut array = [first; $i]; let mut i = 1; while i < $i { array[i] = buffer.decode::<T>(); i += 1; }; array } }"
sed -i "s/\/\/ BEGIN ARRAY_DECODE/\/\/ BEGIN ARRAY_DECODE\n$CODE/g" ./src/codec.sw
done
remove_generated_code "STRARRAY_ENCODE" "codec.sw"
START=1
END=64
for ((i=END;i>=START;i--)); do
CODE="impl AbiEncode for str[$i] { fn abi_encode(self, buffer: Buffer) -> Buffer { Buffer { buffer: __encode_buffer_append(buffer.buffer, self) } } }"
sed -i "s/\/\/ BEGIN STRARRAY_ENCODE/\/\/ BEGIN STRARRAY_ENCODE\n$CODE/g" ./src/codec.sw
done
remove_generated_code "STRARRAY_DECODE" "codec.sw"
START=1
END=64
for ((i=END;i>=START;i--)); do
CODE="impl AbiDecode for str[$i] { fn abi_decode(ref mut buffer: BufferReader) -> str[$i] { let data = buffer.read_bytes($i); asm(s: data.ptr()) { s: str[$i] } } }"
sed -i "s/\/\/ BEGIN STRARRAY_DECODE/\/\/ BEGIN STRARRAY_DECODE\n$CODE/g" ./src/codec.sw
done
generate_tuple_encode() {
local CODE="impl<"
local elements=("$1")
for element in ${elements[@]}
do
CODE="$CODE $element,"
done
CODE="$CODE> AbiEncode for ("
for element in ${elements[@]}
do
CODE="$CODE $element,"
done
CODE="$CODE) where "
for element in ${elements[@]}
do
CODE="$CODE $element: AbiEncode, "
done
CODE="$CODE{ fn abi_encode(self, buffer: Buffer) -> Buffer { "
i=0
for element in ${elements[@]}
do
CODE="$CODE let buffer = self.$i.abi_encode(buffer);"
i=$((i+1))
done
CODE="$CODE buffer } }"
sed -i "s/\/\/ BEGIN TUPLES_ENCODE/\/\/ BEGIN TUPLES_ENCODE\n$CODE/g" ./src/codec.sw
}
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"
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"
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"
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U"
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T"
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S"
generate_tuple_encode "A B C D E F G H I J K L M N O P Q R"
generate_tuple_encode "A B C D E F G H I J K L M N O P Q"
generate_tuple_encode "A B C D E F G H I J K L M N O P"
generate_tuple_encode "A B C D E F G H I J K L M N O"
generate_tuple_encode "A B C D E F G H I J K L M N"
generate_tuple_encode "A B C D E F G H I J K L M"
generate_tuple_encode "A B C D E F G H I J K L"
generate_tuple_encode "A B C D E F G H I J K"
generate_tuple_encode "A B C D E F G H I J"
generate_tuple_encode "A B C D E F G H I"
generate_tuple_encode "A B C D E F G H"
generate_tuple_encode "A B C D E F G"
generate_tuple_encode "A B C D E F"
generate_tuple_encode "A B C D E"
generate_tuple_encode "A B C D"
generate_tuple_encode "A B C"
generate_tuple_encode "A B"
generate_tuple_encode "A"
generate_tuple_decode() {
local CODE="impl<"
local elements=("$1")
for element in ${elements[@]}
do
CODE="$CODE $element,"
done
CODE="$CODE> AbiDecode for ("
for element in ${elements[@]}
do
CODE="$CODE $element,"
done
CODE="$CODE) where "
for element in ${elements[@]}
do
CODE="$CODE $element: AbiDecode, "
done
CODE="$CODE{ fn abi_decode(ref mut buffer: BufferReader) -> Self { ("
for element in ${elements[@]}
do
CODE="$CODE $element::abi_decode(buffer),"
done
CODE="$CODE) } }"
sed -i "s/\/\/ BEGIN TUPLES_DECODE/\/\/ BEGIN TUPLES_DECODE\n$CODE/g" ./src/codec.sw
}
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"
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"
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"
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U"
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T"
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S"
generate_tuple_decode "A B C D E F G H I J K L M N O P Q R"
generate_tuple_decode "A B C D E F G H I J K L M N O P Q"
generate_tuple_decode "A B C D E F G H I J K L M N O P"
generate_tuple_decode "A B C D E F G H I J K L M N O"
generate_tuple_decode "A B C D E F G H I J K L M N"
generate_tuple_decode "A B C D E F G H I J K L M"
generate_tuple_decode "A B C D E F G H I J K L"
generate_tuple_decode "A B C D E F G H I J K"
generate_tuple_decode "A B C D E F G H I J"
generate_tuple_decode "A B C D E F G H I"
generate_tuple_decode "A B C D E F G H"
generate_tuple_decode "A B C D E F G"
generate_tuple_decode "A B C D E F"
generate_tuple_decode "A B C D E"
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 .