From dfa3fe544771f7f86b7748f6827d53dd1a4a2f1a Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Fri, 20 Oct 2023 02:53:42 +0400 Subject: [PATCH] Use a copy of standard libraries for LSP tests (#5203) ## Description Use a standalone copy of std and core for LSP tests so that they don't get disrupted by standard library changes. ## Checklist - [x] 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). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] 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. Co-authored-by: Joshua Batty --- sway-lsp/tests/fixtures/benchmark/Forc.toml | 2 +- sway-lsp/tests/fixtures/completion/Forc.lock | 13 + sway-lsp/tests/fixtures/completion/Forc.toml | 2 +- .../fixtures/diagnostics/dead_code/Forc.toml | 2 +- .../fixtures/diagnostics/multi_file/Forc.toml | 2 +- .../tests/fixtures/fixtures-core/Forc.lock | 3 + .../tests/fixtures/fixtures-core/Forc.toml | 5 + .../tests/fixtures/fixtures-core/src/lib.sw | 11 + .../tests/fixtures/fixtures-core/src/never.sw | 78 + .../tests/fixtures/fixtures-core/src/ops.sw | 1218 ++++++++++++++++ .../fixtures/fixtures-core/src/prelude.sw | 13 + .../src/primitive_conversions.sw | 361 +++++ .../fixtures/fixtures-core/src/primitives.sw | 288 ++++ .../fixtures/fixtures-core/src/raw_ptr.sw | 289 ++++ .../fixtures/fixtures-core/src/raw_slice.sw | 158 ++ .../fixtures/fixtures-core/src/storage.sw | 20 + .../tests/fixtures/fixtures-core/src/str.sw | 30 + .../tests/fixtures/fixtures-std/Forc.lock | 8 + .../tests/fixtures/fixtures-std/Forc.toml | 8 + .../fixtures/fixtures-std/src/address.sw | 154 ++ .../tests/fixtures/fixtures-std/src/alias.sw | 4 + .../tests/fixtures/fixtures-std/src/alloc.sw | 156 ++ .../fixtures-std/src/array_conversions.sw | 7 + .../src/array_conversions/b256.sw | 201 +++ .../fixtures-std/src/array_conversions/u16.sw | 158 ++ .../src/array_conversions/u256.sw | 201 +++ .../fixtures-std/src/array_conversions/u32.sw | 191 +++ .../fixtures-std/src/array_conversions/u64.sw | 275 ++++ .../tests/fixtures/fixtures-std/src/assert.sw | 65 + .../tests/fixtures/fixtures-std/src/auth.sw | 179 +++ .../tests/fixtures/fixtures-std/src/b512.sw | 56 + .../tests/fixtures/fixtures-std/src/block.sw | 158 ++ .../tests/fixtures/fixtures-std/src/bytes.sw | 1277 +++++++++++++++++ .../fixtures-std/src/bytes_conversions.sw | 7 + .../src/bytes_conversions/b256.sw | 200 +++ .../fixtures-std/src/bytes_conversions/u16.sw | 199 +++ .../src/bytes_conversions/u256.sw | 204 +++ .../fixtures-std/src/bytes_conversions/u32.sw | 246 ++++ .../fixtures-std/src/bytes_conversions/u64.sw | 336 +++++ .../fixtures/fixtures-std/src/call_frames.sw | 215 +++ .../fixtures/fixtures-std/src/constants.sw | 34 + .../fixtures/fixtures-std/src/context.sw | 77 + .../fixtures/fixtures-std/src/contract_id.sw | 359 +++++ .../fixtures/fixtures-std/src/convert.sw | 20 + .../tests/fixtures/fixtures-std/src/ecr.sw | 298 ++++ .../fixtures-std/src/error_signals.sw | 30 + .../fixtures/fixtures-std/src/external.sw | 34 + .../tests/fixtures/fixtures-std/src/flags.sw | 267 ++++ .../tests/fixtures/fixtures-std/src/hash.sw | 618 ++++++++ .../fixtures/fixtures-std/src/identity.sw | 236 +++ .../tests/fixtures/fixtures-std/src/inputs.sw | 624 ++++++++ .../fixtures/fixtures-std/src/intrinsics.sw | 92 ++ .../tests/fixtures/fixtures-std/src/lib.sw | 46 + .../fixtures/fixtures-std/src/logging.sw | 24 + .../fixtures-std/src/low_level_call.sw | 273 ++++ .../tests/fixtures/fixtures-std/src/math.sw | 165 +++ .../fixtures/fixtures-std/src/message.sw | 73 + .../tests/fixtures/fixtures-std/src/option.sw | 251 ++++ .../fixtures/fixtures-std/src/outputs.sw | 258 ++++ .../fixtures/fixtures-std/src/prelude.sw | 35 + .../fixtures-std/src/primitive_conversions.sw | 8 + .../src/primitive_conversions/b256.sw | 49 + .../src/primitive_conversions/str.sw | 38 + .../src/primitive_conversions/u16.sw | 96 ++ .../src/primitive_conversions/u32.sw | 81 ++ .../src/primitive_conversions/u64.sw | 64 + .../src/primitive_conversions/u8.sw | 110 ++ .../fixtures/fixtures-std/src/registers.sw | 317 ++++ .../tests/fixtures/fixtures-std/src/result.sw | 210 +++ .../tests/fixtures/fixtures-std/src/revert.sw | 61 + .../fixtures/fixtures-std/src/storage.sw | 10 + .../src/storage/storable_slice.sw | 144 ++ .../fixtures-std/src/storage/storage_api.sw | 183 +++ .../fixtures-std/src/storage/storage_bytes.sw | 159 ++ .../fixtures-std/src/storage/storage_key.sw | 139 ++ .../fixtures-std/src/storage/storage_map.sw | 113 ++ .../src/storage/storage_string.sw | 164 +++ .../fixtures-std/src/storage/storage_vec.sw | 914 ++++++++++++ .../tests/fixtures/fixtures-std/src/string.sw | 510 +++++++ .../tests/fixtures/fixtures-std/src/token.sw | 271 ++++ .../tests/fixtures/fixtures-std/src/tx.sw | 511 +++++++ .../tests/fixtures/fixtures-std/src/u128.sw | 555 +++++++ .../tests/fixtures/fixtures-std/src/u256.sw | 640 +++++++++ .../tests/fixtures/fixtures-std/src/vec.sw | 623 ++++++++ .../tests/fixtures/fixtures-std/src/vm.sw | 4 + .../tests/fixtures/fixtures-std/src/vm/evm.sw | 5 + .../fixtures/fixtures-std/src/vm/evm/ecr.sw | 54 + .../fixtures-std/src/vm/evm/evm_address.sw | 37 + sway-lsp/tests/fixtures/renaming/Forc.toml | 2 +- sway-lsp/tests/fixtures/runnables/Forc.toml | 2 +- sway-lsp/tests/fixtures/tokens/abi/Forc.toml | 2 +- .../tests/fixtures/tokens/consts/Forc.toml | 2 +- .../tests/fixtures/tokens/enums/Forc.toml | 2 +- .../tests/fixtures/tokens/fields/Forc.toml | 2 +- .../tests/fixtures/tokens/functions/Forc.toml | 2 +- .../tests/fixtures/tokens/impls/Forc.toml | 2 +- .../tests/fixtures/tokens/matches/Forc.toml | 2 +- .../tests/fixtures/tokens/modules/Forc.toml | 2 +- .../tests/fixtures/tokens/paths/Forc.toml | 2 +- .../tests/fixtures/tokens/storage/Forc.toml | 2 +- .../tests/fixtures/tokens/structs/Forc.toml | 2 +- .../tests/fixtures/tokens/traits/Forc.toml | 2 +- .../tests/fixtures/tokens/turbofish/Forc.toml | 2 +- .../tests/fixtures/tokens/variables/Forc.toml | 2 +- .../fixtures/tokens/where_clause/Forc.toml | 2 +- sway-lsp/tests/lib.rs | 50 +- 106 files changed, 16922 insertions(+), 46 deletions(-) create mode 100644 sway-lsp/tests/fixtures/completion/Forc.lock create mode 100644 sway-lsp/tests/fixtures/fixtures-core/Forc.lock create mode 100644 sway-lsp/tests/fixtures/fixtures-core/Forc.toml create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/lib.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/never.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/ops.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/prelude.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/primitive_conversions.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/primitives.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/raw_ptr.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/raw_slice.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/storage.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-core/src/str.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/Forc.lock create mode 100644 sway-lsp/tests/fixtures/fixtures-std/Forc.toml create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/address.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/alias.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/alloc.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/b256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u16.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u32.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u64.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/assert.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/auth.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/b512.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/block.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/b256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u16.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u32.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u64.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/call_frames.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/constants.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/context.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/contract_id.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/convert.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/ecr.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/error_signals.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/external.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/flags.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/hash.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/identity.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/inputs.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/intrinsics.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/lib.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/logging.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/low_level_call.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/math.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/message.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/option.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/outputs.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/prelude.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/b256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/str.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u16.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u32.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u64.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u8.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/registers.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/result.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/revert.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storable_slice.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_api.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_bytes.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_key.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_map.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_string.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_vec.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/string.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/token.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/tx.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/u128.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/u256.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/vec.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/vm.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/vm/evm.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/ecr.sw create mode 100644 sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/evm_address.sw diff --git a/sway-lsp/tests/fixtures/benchmark/Forc.toml b/sway-lsp/tests/fixtures/benchmark/Forc.toml index ffc25415e49..0041a70602f 100644 --- a/sway-lsp/tests/fixtures/benchmark/Forc.toml +++ b/sway-lsp/tests/fixtures/benchmark/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "sway_project" [dependencies] -std = { path = "../../../../sway-lib-std" } \ No newline at end of file +std = { path = "../fixtures-std" } \ No newline at end of file diff --git a/sway-lsp/tests/fixtures/completion/Forc.lock b/sway-lsp/tests/fixtures/completion/Forc.lock new file mode 100644 index 00000000000..e8a73623cfb --- /dev/null +++ b/sway-lsp/tests/fixtures/completion/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "completion" +source = "member" +dependencies = ["std"] + +[[package]] +name = "core" +source = "path+from-root-A8024ACE89756498" + +[[package]] +name = "std" +source = "path+from-root-A8024ACE89756498" +dependencies = ["core"] diff --git a/sway-lsp/tests/fixtures/completion/Forc.toml b/sway-lsp/tests/fixtures/completion/Forc.toml index 75a606a7a96..237fb8922d0 100644 --- a/sway-lsp/tests/fixtures/completion/Forc.toml +++ b/sway-lsp/tests/fixtures/completion/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "completion" [dependencies] -std = { path = "../../../../sway-lib-std" } +std = { path = "../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/diagnostics/dead_code/Forc.toml b/sway-lsp/tests/fixtures/diagnostics/dead_code/Forc.toml index d5efe82823c..87f5dbd2de0 100644 --- a/sway-lsp/tests/fixtures/diagnostics/dead_code/Forc.toml +++ b/sway-lsp/tests/fixtures/diagnostics/dead_code/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "dead_code" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/diagnostics/multi_file/Forc.toml b/sway-lsp/tests/fixtures/diagnostics/multi_file/Forc.toml index 0e287f6f283..e3781d9477a 100644 --- a/sway-lsp/tests/fixtures/diagnostics/multi_file/Forc.toml +++ b/sway-lsp/tests/fixtures/diagnostics/multi_file/Forc.toml @@ -6,4 +6,4 @@ entry = "main.sw" implicit-std = false [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/fixtures-core/Forc.lock b/sway-lsp/tests/fixtures/fixtures-core/Forc.lock new file mode 100644 index 00000000000..9c621870bcd --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = 'core' +source = 'member' diff --git a/sway-lsp/tests/fixtures/fixtures-core/Forc.toml b/sway-lsp/tests/fixtures/fixtures-core/Forc.toml new file mode 100644 index 00000000000..29ea1385066 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/Forc.toml @@ -0,0 +1,5 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "core" diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/lib.sw b/sway-lsp/tests/fixtures/fixtures-core/src/lib.sw new file mode 100644 index 00000000000..3ecb58bf880 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/lib.sw @@ -0,0 +1,11 @@ +library; + +pub mod primitives; +pub mod raw_ptr; +pub mod raw_slice; +pub mod r#str; +pub mod ops; +pub mod primitive_conversions; +pub mod never; +pub mod r#storage; +pub mod prelude; diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/never.sw b/sway-lsp/tests/fixtures/fixtures-core/src/never.sw new file mode 100644 index 00000000000..5c9760b6e1d --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/never.sw @@ -0,0 +1,78 @@ +library; + +use ::ops::{Eq, Not, Ord}; + +/// `Never` represents the type of computations which never resolve to any value at all. +/// +/// # Additional Information +/// +/// `break`, `continue` and `return` expressions also have type `Never`. For example we are allowed to +/// write: +/// +/// ```sway +/// let x: Never = { +/// return 123 +/// }; +/// ``` +/// +/// Although the `let` is pointless here, it illustrates the meaning of `Never`. Since `x` is never +/// assigned a value (because `return` returns from the entire function), `x` can be given type +/// `Never`. We could also replace `return 123` with a `revert()` or a never-ending `loop` and this code +/// would still be valid. +/// +/// A more realistic usage of `Never` is in this code: +/// +/// ```sway +/// let num: u32 = match get_a_number() { +/// Some(num) => num, +/// None => break, +/// }; +/// ``` +/// +/// Both match arms must produce values of type [`u32`], but since `break` never produces a value +/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another +/// behaviour of the `Never` type - expressions with type `Never` will coerce into any other type. +/// +/// Note that `Never` type coerces into any other type, another example of this would be: +/// +/// ```sway +/// let x: u32 = { +/// return 123 +/// }; +/// ``` +/// +/// Regardless of the type of `x`, the return block of type `Never` will always coerce into `x` type. +/// +/// # Examples +/// +/// ```sway +/// fn foo() { +/// let num: u64 = match Option::None:: { +/// Some(num) => num, +/// None => return, +/// }; +/// } +/// ``` +pub enum Never {} + +impl Not for Never { + fn not(self) -> Self { + match self { + } + } +} + +impl Eq for Never { + fn eq(self, _other: Self) -> bool { + self + } +} + +impl Ord for Never { + fn gt(self, _other: Self) -> bool { + self + } + fn lt(self, _other: Self) -> bool { + self + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/ops.sw b/sway-lsp/tests/fixtures/fixtures-core/src/ops.sw new file mode 100644 index 00000000000..7ba7e5b4676 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/ops.sw @@ -0,0 +1,1218 @@ +library; + +use ::primitives::*; + +/// Trait for the addition of two values. +pub trait Add { + /// Add two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value to add to self. + /// + /// # Returns + /// + /// * [Self] - The result of the two values added. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Add for MyStruct { + /// fn add(self, other: Self) -> Self { + /// let val = self.val + other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 1 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result_struct = struct1 + struct2; + /// assert(result_struct.val == 3); + /// } + /// ``` + fn add(self, other: Self) -> Self; +} + +impl Add for u256 { + fn add(self, other: Self) -> Self { + __add(self, other) + } +} + +impl Add for u64 { + fn add(self, other: Self) -> Self { + __add(self, other) + } +} + +// Emulate overflowing arithmetic for non-64-bit integer types +impl Add for u32 { + fn add(self, other: Self) -> Self { + // any non-64-bit value is compiled to a u64 value under-the-hood + // constants (like Self::max() below) are also automatically promoted to u64 + let res = __add(self, other); + if __gt(res, Self::max()) { + // integer overflow + __revert(0) + } else { + // no overflow + res + } + } +} + +impl Add for u16 { + fn add(self, other: Self) -> Self { + let res = __add(self, other); + if __gt(res, Self::max()) { + __revert(0) + } else { + res + } + } +} + +impl Add for u8 { + fn add(self, other: Self) -> Self { + let res = __add(self, other); + if __gt(res, Self::max()) { + __revert(0) + } else { + res + } + } +} + +/// Trait for the subtraction of two values. +pub trait Subtract { + /// Subtract two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value to subtract from self. + /// + /// # Returns + /// + /// * [Self] - The result of the two values subtracted. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Subtract for MyStruct { + /// fn subtract(self, other: Self) -> Self { + /// let val = self.val - other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 3 }; + /// let struct2 = MyStruct { val: 1 }; + /// let result_struct = struct1 - struct2; + /// assert(result_struct.val == 2); + /// } + /// ``` + fn subtract(self, other: Self) -> Self; +} + +impl Subtract for u256 { + fn subtract(self, other: Self) -> Self { + __sub(self, other) + } +} + +impl Subtract for u64 { + fn subtract(self, other: Self) -> Self { + __sub(self, other) + } +} + +// unlike addition, underflowing subtraction does not need special treatment +// because VM handles underflow +impl Subtract for u32 { + fn subtract(self, other: Self) -> Self { + __sub(self, other) + } +} + +impl Subtract for u16 { + fn subtract(self, other: Self) -> Self { + __sub(self, other) + } +} + +impl Subtract for u8 { + fn subtract(self, other: Self) -> Self { + __sub(self, other) + } +} + +/// Trait for the multiplication of two values. +pub trait Multiply { + /// Multiply two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value to multiply with self. + /// + /// # Returns + /// + /// * [Self] - The result of the two values multiplied. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Multiply for MyStruct { + /// fn multiply(self, other: Self) -> Self { + /// let val = self.val * other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 3 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result_struct = struct1 * struct2; + /// assert(result_struct.val == 6); + /// } + /// ``` + fn multiply(self, other: Self) -> Self; +} + +impl Multiply for u256 { + fn multiply(self, other: Self) -> Self { + __mul(self, other) + } +} + +impl Multiply for u64 { + fn multiply(self, other: Self) -> Self { + __mul(self, other) + } +} + +// Emulate overflowing arithmetic for non-64-bit integer types +impl Multiply for u32 { + fn multiply(self, other: Self) -> Self { + // any non-64-bit value is compiled to a u64 value under-the-hood + // constants (like Self::max() below) are also automatically promoted to u64 + let res = __mul(self, other); + if __gt(res, Self::max()) { + // integer overflow + __revert(0) + } else { + // no overflow + res + } + } +} + +impl Multiply for u16 { + fn multiply(self, other: Self) -> Self { + let res = __mul(self, other); + if __gt(res, Self::max()) { + __revert(0) + } else { + res + } + } +} + +impl Multiply for u8 { + fn multiply(self, other: Self) -> Self { + let res = __mul(self, other); + if __gt(res, Self::max()) { + __revert(0) + } else { + res + } + } +} + +/// Trait for the division of two values. +pub trait Divide { + /// Divide two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value to divide with self. + /// + /// # Returns + /// + /// * [Self] - The result of the two values divided. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Divide for MyStruct { + /// fn divide(self, other: Self) -> Self { + /// let val = self.val / other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result_struct = struct1 / struct2; + /// assert(result_struct.val == 5); + /// } + /// ``` + fn divide(self, other: Self) -> Self; +} + +impl Divide for u256 { + fn divide(self, other: Self) -> Self { + __div(self, other) + } +} + +impl Divide for u64 { + fn divide(self, other: Self) -> Self { + __div(self, other) + } +} + +// division for unsigned integers cannot overflow, +// but if signed integers are ever introduced, +// overflow needs to be handled, since +// Self::max() / -1 overflows +impl Divide for u32 { + fn divide(self, other: Self) -> Self { + __div(self, other) + } +} + +impl Divide for u16 { + fn divide(self, other: Self) -> Self { + __div(self, other) + } +} + +impl Divide for u8 { + fn divide(self, other: Self) -> Self { + __div(self, other) + } +} + +/// Trait for the modulo of two values. +pub trait Mod { + /// Modulo two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value to mod with self. + /// + /// # Returns + /// + /// * [Self] - The modulo of the two values. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Mod for MyStruct { + /// fn modulo(self, other: Self) -> Self { + /// let val = self.val % other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result_struct = struct1 % struct2; + /// assert(result_struct.val == 0); + /// } + /// ``` + fn modulo(self, other: Self) -> Self; +} + +impl Mod for u256 { + fn modulo(self, other: Self) -> Self { + __mod(self, other) + } +} + +impl Mod for u64 { + fn modulo(self, other: Self) -> Self { + __mod(self, other) + } +} + +impl Mod for u32 { + fn modulo(self, other: Self) -> Self { + __mod(self, other) + } +} + +impl Mod for u16 { + fn modulo(self, other: Self) -> Self { + __mod(self, other) + } +} + +impl Mod for u8 { + fn modulo(self, other: Self) -> Self { + __mod(self, other) + } +} + +/// Trait to invert a type. +pub trait Not { + /// Inverts the value of the type. + /// + /// # Returns + /// + /// * [Self] - The result of the inverse. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: bool, + /// } + /// + /// impl Not for MyStruct { + /// fn not(self) -> Self { + /// Self { + /// val: !self.val, + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct = MyStruct { val: true }; + /// let result_struct = !struct; + /// assert(!result_struct.val); + /// } + /// ``` + fn not(self) -> Self; +} + +impl Not for bool { + fn not(self) -> Self { + __eq(self, false) + } +} + +impl Not for u256 { + fn not(self) -> Self { + __not(self) + } +} + +impl Not for b256 { + fn not(self) -> Self { + __not(self) + } +} + +impl Not for u64 { + fn not(self) -> Self { + __not(self) + } +} + +impl Not for u32 { + fn not(self) -> Self { + let v = __not(self); + __and(v, u32::max()) + } +} + +impl Not for u16 { + fn not(self) -> Self { + let v = __not(self); + __and(v, u16::max()) + } +} + +impl Not for u8 { + fn not(self) -> Self { + let v = __not(self); + __and(v, u8::max()) + } +} + +/// Trait to evaluate if two types are equal. +pub trait Eq { + /// Evaluates if two values of the same type are equal. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if the values are equal, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 2 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 == struct2; + /// assert(result); + /// } + /// ``` + fn eq(self, other: Self) -> bool; +} { + /// Evaluates if two values of the same type are not equal. + /// + /// # Additional Information + /// + /// This function is inherited when `eq()` is implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if the two values are not equal, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 != struct2; + /// assert(result); + /// } + /// ``` + fn neq(self, other: Self) -> bool { + (self.eq(other)).not() + } +} + +impl Eq for bool { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for u256 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for b256 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for u64 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for u32 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for u16 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for u8 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +impl Eq for raw_ptr { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +/// Trait to evaluate if one value is greater or less than another of the same type. +pub trait Ord { + /// Evaluates if one value of the same type is greater than another. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is greater than `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Ord for MyStruct { + /// fn gt(self, other: Self) -> bool { + /// self.val > other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 > struct2; + /// assert(result); + /// } + /// ``` + fn gt(self, other: Self) -> bool; + + /// Evaluates if one value of the same type is less than another. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is less than `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Ord for MyStruct { + /// fn lt(self, other: Self) -> bool { + /// self.val < other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 < struct2; + /// assert(!result); + /// } + /// ``` + fn lt(self, other: Self) -> bool; +} + +impl Ord for u256 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +impl Ord for b256 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +impl Ord for u64 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +impl Ord for u32 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +impl Ord for u16 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +impl Ord for u8 { + fn gt(self, other: Self) -> bool { + __gt(self, other) + } + fn lt(self, other: Self) -> bool { + __lt(self, other) + } +} + +/// Trait to bitwise AND two values of the same type. +pub trait BitwiseAnd { + /// Bitwise AND two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [Self] - The result of the bitwise AND of the two values. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl BitwiseAnd for MyStruct { + /// fn binary_and(self, other: Self) -> Self { + /// let val = self.val & other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 11 }; + /// let result_struct = struct1 & struct2; + /// assert(result_struct.val == 10); + /// } + /// ``` + fn binary_and(self, other: Self) -> Self; +} + +impl BitwiseAnd for u256 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +impl BitwiseAnd for b256 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +impl BitwiseAnd for u64 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +impl BitwiseAnd for u32 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +impl BitwiseAnd for u16 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +impl BitwiseAnd for u8 { + fn binary_and(self, other: Self) -> Self { + __and(self, other) + } +} + +/// Trait to bitwise OR two values of the same type. +pub trait BitwiseOr { + /// Bitwise OR two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [Self] - The result of the bitwise OR of the two values. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl BitwiseOr for MyStruct { + /// fn binary_or(self, other: Self) -> Self { + /// let val = self.val | other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 11 }; + /// let result_struct = struct1 | struct2; + /// assert(result_struct.val == 11); + /// } + /// ``` + fn binary_or(self, other: Self) -> Self; +} + +impl BitwiseOr for u256 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +impl BitwiseOr for b256 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +impl BitwiseOr for u64 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +impl BitwiseOr for u32 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +impl BitwiseOr for u16 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +impl BitwiseOr for u8 { + fn binary_or(self, other: Self) -> Self { + __or(self, other) + } +} + +/// Trait to bitwise XOR two values of the same type. +pub trait BitwiseXor { + /// Bitwise XOR two values of the same type. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [Self] - The result of the bitwise XOR of the two values. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl BitwiseXOr for MyStruct { + /// fn binary_xor(self, other: Self) -> Self { + /// let val = self.val ^ other.val; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 11 }; + /// let result_struct = struct1 ^ struct2; + /// assert(result_struct.val == 1); + /// } + /// ``` + fn binary_xor(self, other: Self) -> Self; +} + +impl BitwiseXor for u256 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +impl BitwiseXor for b256 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +impl BitwiseXor for u64 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +impl BitwiseXor for u32 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +impl BitwiseXor for u16 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +impl BitwiseXor for u8 { + fn binary_xor(self, other: Self) -> Self { + __xor(self, other) + } +} + +/// Trait to evaluate if one value is greater than or equal, or less than or equal to another of the same type. +trait OrdEq: Ord + Eq { +} { + /// Evaluates if one value of the same type is greater or equal to than another. + /// + /// # Additional Information + /// + /// This trait requires that the `Ord` and `Eq` traits are implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is greater than or equal to `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// impl Ord for MyStruct { + /// fn gt(self, other: Self) -> bool { + /// self.val > other.val + /// } + /// } + /// + /// impl OrdEq for MyStruct {} + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 10 }; + /// let result = struct1 >= struct2; + /// assert(result); + /// } + /// ``` + fn ge(self, other: Self) -> bool { + self.gt(other) || self.eq(other) + } + + /// Evaluates if one value of the same type is less or equal to than another. + /// + /// # Additional Information + /// + /// This trait requires that the `Ord` and `Eq` traits are implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is less than or equal to `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// impl Ord for MyStruct { + /// fn lt(self, other: Self) -> bool { + /// self.val < other.val + /// } + /// } + /// + /// impl OrdEq for MyStruct {} + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 10 }; + /// let result = struct1 <= struct2; + /// assert(result); + /// } + /// ``` + fn le(self, other: Self) -> bool { + self.lt(other) || self.eq(other) + } +} + +impl OrdEq for u256 {} +impl OrdEq for u64 {} +impl OrdEq for u32 {} +impl OrdEq for u16 {} +impl OrdEq for u8 {} +impl OrdEq for b256 {} + +/// Trait to bit shift a value. +pub trait Shift { + /// Bit shift left by an amount. + /// + /// # Arguments + /// + /// * `other`: [u64] - The amount to bit shift by. + /// + /// # Returns + /// + /// * [Self] - The result of the value bit shifted to the left. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Shift for MyStruct { + /// fn lsh(self, other: u64) -> Self { + /// let val = self.val << other; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let result_struct = struct1 << 3; + /// assert(result_struct.val == 80); + /// } + /// ``` + fn lsh(self, other: u64) -> Self; + + /// Bit shift right by an amount. + /// + /// # Arguments + /// + /// * `other`: [u64] - The amount to bit shift by. + /// + /// # Returns + /// + /// * [Self] - The result of the value bit shifted to the right. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Shift for MyStruct { + /// fn rsh(self, other: u64) -> Self { + /// let val = self.val >> other; + /// Self { + /// val + /// } + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let result_struct = struct1 >> 1; + /// assert(result_struct.val == 5); + /// } + /// ``` + fn rsh(self, other: u64) -> Self; +} + +impl Shift for u256 { + fn lsh(self, other: u64) -> Self { + __lsh(self, other) + } + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +impl Shift for b256 { + fn lsh(self, other: u64) -> Self { + __lsh(self, other) + } + + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +impl Shift for u64 { + fn lsh(self, other: u64) -> Self { + __lsh(self, other) + } + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +impl Shift for u32 { + fn lsh(self, other: u64) -> Self { + // any non-64-bit value is compiled to a u64 value under-the-hood + // so we need to clear upper bits here + __and(__lsh(self, other), Self::max()) + } + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +impl Shift for u16 { + fn lsh(self, other: u64) -> Self { + __and(__lsh(self, other), Self::max()) + } + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +impl Shift for u8 { + fn lsh(self, other: u64) -> Self { + __and(__lsh(self, other), Self::max()) + } + fn rsh(self, other: u64) -> Self { + __rsh(self, other) + } +} + +///////////////////////////////////////////////// +// Internal Helpers +///////////////////////////////////////////////// + +/// Build a single b256 value from a tuple of 4 u64 values. +fn compose(words: (u64, u64, u64, u64)) -> b256 { + asm(r1: words) { r1: b256 } +} + +/// Get a tuple of 4 u64 values from a single b256 value. +fn decompose(val: b256) -> (u64, u64, u64, u64) { + asm(r1: val) { r1: (u64, u64, u64, u64) } +} + +#[test] +fn test_compose() { + let expected: b256 = 0x0000000000000001_0000000000000002_0000000000000003_0000000000000004; + let composed = compose((1, 2, 3, 4)); + if composed.neq(expected) { + __revert(0) + } +} + +#[test] +fn test_decompose() { + let initial: b256 = 0x0000000000000001_0000000000000002_0000000000000003_0000000000000004; + let expected = (1, 2, 3, 4); + let decomposed = decompose(initial); + if decomposed.0.neq(expected.0) + && decomposed.1.neq(expected.1) + && decomposed.2.neq(expected.2) + && decomposed.3.neq(expected.3) + { + __revert(0) + } +} + +use ::str::*; + +impl Eq for str { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + false + } else { + let self_ptr = self.as_ptr(); + let other_ptr = other.as_ptr(); + let l = self.len(); + asm(r1: self_ptr, r2: other_ptr, r3: l, r4) { + meq r4 r1 r2 r3; + r4: bool + } + } + } +} \ No newline at end of file diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/prelude.sw b/sway-lsp/tests/fixtures/fixtures-core/src/prelude.sw new file mode 100644 index 00000000000..00fb4e4c420 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/prelude.sw @@ -0,0 +1,13 @@ +library; + +//! Defines the Sway core library prelude. +//! The prelude consists of implicitly available items, +//! for which `use` is not required. +use ::primitives::*; +use ::primitive_conversions::*; +use ::raw_ptr::*; +use ::raw_slice::*; +use ::never::*; +use ::ops::*; +use ::storage::*; +use ::str::*; \ No newline at end of file diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/primitive_conversions.sw b/sway-lsp/tests/fixtures/fixtures-core/src/primitive_conversions.sw new file mode 100644 index 00000000000..6058beae19c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/primitive_conversions.sw @@ -0,0 +1,361 @@ +library; + +impl u64 { + /// Extends a `u64` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self); + asm(input: input) { + input: u256 + } + } +} + +impl u32 { + /// Extends a `u32` to a `u64`. + /// + /// # Returns + /// + /// * [u64] - The converted `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 10u32; + /// let result = val.as_u64(); + /// assert(result == 10); + /// } + /// ``` + pub fn as_u64(self) -> u64 { + asm(input: self) { + input: u64 + } + } +} + +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u32 { + /// Extends a `u32` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u32; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + +impl u16 { + /// Extends a `u16` to a `u32`. + /// + /// # Returns + /// + /// * [u32] - The converted `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 10u16; + /// let result = val.as_u32(); + /// assert(result == 10u32); + /// } + /// ``` + pub fn as_u32(self) -> u32 { + asm(input: self) { + input: u32 + } + } + + /// Extends a `u16` to a `u64`. + /// + /// # Returns + /// + /// * [u64] - The converted `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 10u16; + /// let result = val.as_u64(); + /// assert(result == 10); + /// } + /// ``` + pub fn as_u64(self) -> u64 { + asm(input: self) { + input: u64 + } + } +} + +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u16 { + /// Extends a `u16` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u16; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + +impl u8 { + /// Extends a `u8` to a `u16`. + /// + /// # Returns + /// + /// * [u16] - The converted `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u8; + /// let result = val.as_u16(); + /// assert(result == 2u16); + /// } + /// ``` + pub fn as_u16(self) -> u16 { + asm(input: self) { + input: u16 + } + } + + /// Extends a `u8` to a `u32`. + /// + /// # Returns + /// + /// * [u32] - The converted `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u8; + /// let result = val.as_u32(); + /// assert(result == 2u32); + /// } + /// ``` + pub fn as_u32(self) -> u32 { + asm(input: self) { + input: u32 + } + } + + /// Extends a `u8` to a `u64`. + /// + /// # Returns + /// + /// * [u64] - The converted `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u8; + /// let result = val.as_u64(); + /// assert(result == 2); + /// } + /// ``` + pub fn as_u64(self) -> u64 { + asm(input: self) { + input: u64 + } + } +} + +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u8 { + /// Extends a `u8` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u8; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + +impl b256 { + /// Converts a `b256` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + /// let result = val.as_u256(); + /// assert(result == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + asm(input: self) { + input: u256 + } + } +} + +impl u256 { + /// Converts a `u256` to a `b256`. + /// + /// # Returns + /// + /// * [b256] - The converted `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// let result = val.as_b256(); + /// assert(result == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn as_b256(self) -> b256 { + asm(input: self) { + input: b256 + } + } +} + +fn assert(condition: bool) { + if !condition { + __revert(0) + } +} + +#[test] +fn test_u64_as_u256() { + let val = 2; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + +#[test] +fn test_u32_as_u64() { + let val = 2u32; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u32_as_u256() { + let val = 2u32; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + +#[test] +fn test_u16_as_u64() { + let val = 2u16; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u16_as_u32() { + let val = 2u16; + let result = val.as_u32(); + assert(result == 2u32); +} + +#[test] +fn test_u16_as_u256() { + let val = 2u16; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + +#[test] +fn test_u8_as_u64() { + let val = 2u8; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u8_as_u32() { + let val = 2u8; + let result = val.as_u32(); + assert(result == 2u32); +} + +#[test] +fn test_u8_as_u16() { + let val = 2u8; + let result = val.as_u16(); + assert(result == 2u16); +} + +#[test] +fn test_u8_as_u256() { + let val = 2u8; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + +#[test] +fn test_b256_as_u256() { + let val = 0x0000000000000000000000000000000000000000000000000000000000000002; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/primitives.sw b/sway-lsp/tests/fixtures/fixtures-core/src/primitives.sw new file mode 100644 index 00000000000..e89d4fc9fb4 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/primitives.sw @@ -0,0 +1,288 @@ +library; + +impl u64 { + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [u64] - The smallest `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u64::min(); + /// assert(val == 0); + // } + /// ``` + pub fn min() -> Self { + 0 + } + + /// The largest value that can be represented by this integer type, + /// 264 - 1. + /// + /// # Returns + /// + /// * [u64] - The largest `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u64::max(); + /// assert(val == 18446744073709551615); + /// } + /// ``` + pub fn max() -> Self { + 18446744073709551615 + } + + /// The size of this integer type in bits. + /// + /// # Returns + /// + /// * [u32] - The number of bits for a `u64`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bits = u64::bits(); + /// assert(bits == 64); + /// } + /// ``` + pub fn bits() -> u64 { + 64 + } +} + +impl u32 { + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [u32] - The smallest `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val == u32::min(); + /// assert(val == 0u32); + /// } + /// ``` + pub fn min() -> Self { + 0 + } + + /// The largest value that can be represented by this integer type, + /// 232 - 1. + /// + /// # Returns + /// + /// * [u32] - The largest `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val == u32:max(); + /// assert(val == 4294967295u32); + /// } + /// ``` + pub fn max() -> Self { + 4294967295 + } + + /// The size of this integer type in bits. + /// + /// # Returns + /// + /// * [u32] - The number of bits for a `u32`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bits = u32::bits(); + /// assert(bits == 32); + /// } + /// ``` + pub fn bits() -> u64 { + 32 + } +} + +impl u16 { + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [u16] - The smallest `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u16::min(); + /// assert(val == 0u16); + /// } + /// ``` + pub fn min() -> Self { + 0 + } + + /// The largest value that can be represented by this integer type, + /// 216 - 1. + /// + /// # Returns + /// + /// * [u16] - The largest `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u16::max(); + /// assert(val == 65535u16); + /// } + /// ``` + pub fn max() -> Self { + 65535 + } + + /// The size of this integer type in bits. + /// + /// # Returns + /// + /// * [u32] - The number of bits for a `u16`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bits = u16::bits(); + /// assert(bits == 16); + /// } + /// ``` + pub fn bits() -> u64 { + 16 + } +} + +impl u8 { + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [u8] - The smallest `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u8::min(); + /// assert(val == 0u8); + /// } + /// ``` + pub fn min() -> Self { + 0 + } + + /// The largest value that can be represented by this integer type, + /// 28 - 1. + /// + /// # Returns + /// + /// * [u8] - The largest `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = u8::max(); + /// assert(val == 255u8); + /// } + /// ``` + pub fn max() -> Self { + 255 + } + + /// The size of this integer type in bits. + /// + /// # Returns + /// + /// * [u64] - The number of bits for a `u8`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bits = u8::bits(); + /// assert(bits == 8); + /// } + /// ``` + pub fn bits() -> u64 { + 8 + } +} + +impl b256 { + /// The smallest value that can be represented by this type. + /// + /// # Returns + /// + /// * [b256] - The smallest `b256` value. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let val = b256::min(); + /// assert(val == ZERO_B256); + /// } + /// ``` + pub fn min() -> Self { + 0x0000000000000000000000000000000000000000000000000000000000000000 + } + + /// The largest value that can be represented by this type, + /// 2256 - 1. + /// + /// # Returns + /// + /// * [b256] - The largest `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val == b256::max(); + /// assert(val == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + /// } + /// ``` + pub fn max() -> Self { + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + } + + /// The size of this type in bits. + /// + /// # Returns + /// + /// * [u64] - The number of bits for a `b256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bits == b256::bits(); + /// assert(bits == 256); + /// } + /// ``` + pub fn bits() -> u64 { + 256 + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/raw_ptr.sw b/sway-lsp/tests/fixtures/fixtures-core/src/raw_ptr.sw new file mode 100644 index 00000000000..a79f52d85b0 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/raw_ptr.sw @@ -0,0 +1,289 @@ +library; + +impl raw_ptr { + /// Returns `true` if the pointer is null. + /// + /// # Returns + /// + /// * [bool] - `true` if the pointer is null, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(2); + /// assert(!ptr.is_null()); + /// } + /// ``` + pub fn is_null(self) -> bool { + let null_ptr = asm() { zero: raw_ptr }; + __eq(self, null_ptr) + } + + /// Calculates the offset from the pointer. + /// + /// # Arguments + /// + /// * `count`: [u64] - The number of `size_of` bytes to increase by. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer to the offset memory location. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(2); + /// let offset_ptr = ptr.add::(1); + /// assert(ptr != offset_ptr); + /// } + /// ``` + pub fn add(self, count: u64) -> Self { + __ptr_add::(self, count) + } + + /// Calculates the offset from the pointer. + /// + /// # Arguments + /// + /// * `count`: [u64] - The number of `size_of` bytes to decrease by. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer to the offset memory location. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(2); + /// let offset_ptr = ptr.add::(1); + /// let subbed_offset = offset_ptr.sub::(1); + /// assert(ptr == subbed_offset); + /// } + /// ``` + pub fn sub(self, count: u64) -> Self { + __ptr_sub::(self, count) + } + + /// Reads the given type of value from the address. + /// + /// # Returns + /// + /// * [T] - The value in memory at the location of the pointer. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// ptr.write(5); + /// assert(ptr.read::() == 5); + /// } + /// ``` + pub fn read(self) -> T { + if __is_reference_type::() { + asm(ptr: self) { ptr: T } + } else { + asm(ptr: self, val) { + lw val ptr i0; + val: T + } + } + } + + /// Copies `count * size_of` bytes from `self` to `dst`. + /// + /// # Arguments + /// + /// * `dst`: [raw_ptr] - Pointer to the location in memory to copy the bytes to. + /// * `count`: [u64] - The number of `size_of` bytes to copy. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr_1 = alloc::(1); + /// let ptr_2 = alloc::(1); + /// ptr_1.write(5); + /// ptr_1.copy_to::(ptr_2, 1); + /// assert(ptr_2.read::() == 5); + /// } + /// ``` + pub fn copy_to(self, dst: Self, count: u64) { + let len = __mul(count, __size_of::()); + asm(dst: dst, src: self, len: len) { + mcp dst src len; + }; + } + + /// Writes the given value to the address. + /// + /// # Arguments + /// + /// * `val`: [T] - The value to write to memory. + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// ptr.write(5); + /// assert(ptr.read::() == 5); + /// } + /// ``` + pub fn write(self, val: T) { + if __is_reference_type::() { + asm(dst: self, src: val, count: __size_of_val(val)) { + mcp dst src count; + }; + } else { + asm(ptr: self, val: val) { + sw ptr val i0; + }; + } + } + + /// Writes the given byte to the address. + /// + /// # Arguments + /// + /// * `val`: [u8] - The bytes to write. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// ptr.write_byte(5u8); + /// assert(ptr.read::() == 5u8); + /// } + /// ``` + pub fn write_byte(self, val: u8) { + let val_ptr = asm(r1: val) { r1: raw_ptr }; + asm(ptr: self, val: val_ptr) { + sb ptr val i0; + }; + } + + /// Reads a byte from the given address. + /// + /// # Returns + /// + /// * [u8] - The byte in memory. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// ptr.write_byte(5u8); + /// assert(ptr.read_byte() == 5u8); + /// } + /// ``` + pub fn read_byte(self) -> u8 { + asm(r1: self, r2) { + lb r2 r1 i0; + r2: u8 + } + } + + /// Copies `count` bytes from `self` to `dst`. + /// + /// # Arguments + /// + /// * `dst`: [raw_ptr] - Pointer to the location in memory to copy the bytes to. + /// * `count`: [u64] - The number of bytes to copy. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr_1 = alloc::(1); + /// let ptr_2 = alloc::(1); + /// ptr_1.write_byte(5u8); + /// ptr_1.copy_bytes_to(ptr_2, 1); + /// assert(ptr_2.read_byte() == 5u8); + /// } + /// ``` + pub fn copy_bytes_to(self, dst: Self, count: u64) { + asm(dst: dst, src: self, len: count) { + mcp dst src len; + }; + } + + /// Add a `u64` offset to a `raw_ptr`. + /// + /// # Arguments + /// + /// * `count`: [u64] - The number of `u64` bytes to increase by. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer to the offset memory location. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(2); + /// let offset_ptr_1 = ptr.add::(1); + /// let offset_ptr_2 = ptr.add_uint_offset(1); + /// assert(offset_ptr_1 == offset_ptr_1); + /// } + /// ``` + pub fn add_uint_offset(self, offset: u64) -> Self { + asm(ptr: self, offset: offset, new) { + add new ptr offset; + new: raw_ptr + } + } + + /// Subtract a `u64` offset from a `raw_ptr`. + /// + /// # Arguments + /// + /// * `count`: [u64] - The number of `u64` bytes to decrease by. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer to the offset memory location. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(2); + /// let offset_ptr = ptr.add::(1); + /// let subbed_offset = offset_ptr.sub_uint_offset(1); + /// assert(ptr == subbed_offset); + /// } + /// ``` + pub fn sub_uint_offset(self, offset: u64) -> Self { + asm(ptr: self, offset: offset, new) { + sub new ptr offset; + new: raw_ptr + } + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/raw_slice.sw b/sway-lsp/tests/fixtures/fixtures-core/src/raw_slice.sw new file mode 100644 index 00000000000..eddd6906f19 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/raw_slice.sw @@ -0,0 +1,158 @@ +library; + +use ::raw_ptr::*; + +/// Trait to return a type as a `raw_slice`. +pub trait AsRawSlice { + /// Converts self into a `raw_slice`. + /// + /// # Returns + /// + /// * [raw_slice] - The newly created `raw_slice` from self. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc_bytes; + /// + /// struct MyType { + /// ptr: raw_ptr, + /// len: u64 + /// } + /// + /// impl AsRawSlice for MyType { + /// fn as_raw_slice(self) -> raw_slice { + /// from_parts(self.ptr, self.len) + /// } + /// } + /// + /// fn foo() { + /// let my_type = MyType { + /// ptr: alloc_bytes(0), + /// len: 0 + /// } + /// let slice = my_type.as_raw_slice(); + /// assert(slice.ptr() == my_type.ptr); + /// assert(slice.number_of_bytes() == my_type.len); + /// } + /// ``` + fn as_raw_slice(self) -> raw_slice; +} + +/// Returns a `raw_slice` from a pointer and length. +/// +/// # Arguments +/// +/// * `parts`: [(raw_ptr, u64)] - A location in memory and a length to become a `raw_slice`. +/// +/// # Returns +/// +/// * [raw_slice] - The newly created `raw_slice`. +fn from_parts(parts: (raw_ptr, u64)) -> raw_slice { + asm(ptr: parts) { ptr: raw_slice } +} + +/// Returns a pointer and length from a `raw_slice`. +/// +/// # Arguments +/// +/// * `slice`: [raw_slice] - The slice to be broken into its parts. +/// +/// # Returns +/// +/// * [(raw_ptr, u64)] - A tuple of the location in memory of the original `raw_slice` and its length. +fn into_parts(slice: raw_slice) -> (raw_ptr, u64) { + asm(ptr: slice) { ptr: (raw_ptr, u64) } +} + +impl raw_slice { + /// Forms a slice from a pointer and a length. + /// + /// # Arguments + /// + /// * `ptr`: [raw_ptr] - The pointer to the location in memory. + /// * `count`: [u64] - The number of `__size_of::` bytes. + /// + /// # Returns + /// + /// * [raw_slice] - The newly created `raw_slice`. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// let slice = raw_slice::from_parts::(ptr, 1); + /// assert(slice.len::() == 1); + /// } + /// ``` + pub fn from_parts(ptr: raw_ptr, count: u64) -> Self { + from_parts((ptr, __mul(count, __size_of::()))) + } + + /// Returns the pointer to the slice. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer to the location in memory of the `raw_slice`. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// let slice = raw_slice::from_parts::(ptr, 1); + /// let slice_ptr = slice.ptr(); + /// assert(slice_ptr == ptr); + /// } + /// ``` + pub fn ptr(self) -> raw_ptr { + into_parts(self).0 + } + + /// Returns the number of elements in the slice. + /// + /// # Returns + /// + /// * [u64] - The length of the slice based on `size_of::`. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// let slice = raw_slice::from_parts::(ptr, 1); + /// assert(slice.len::() == 1); + /// } + /// ``` + pub fn len(self) -> u64 { + __div(into_parts(self).1, __size_of::()) + } + + /// Returns the number of elements in the slice when the elements are bytes. + /// + /// # Returns + /// + /// * [u64] - The number of bytes in the `raw_slice`. + /// + /// # Examples + /// + /// ```sway + /// use std::alloc::alloc; + /// + /// fn foo() { + /// let ptr = alloc::(1); + /// let slice = raw_slice::from_parts::(ptr, 1); + /// assert(slice.number_of_bytes() == 8); + /// } + /// ``` + pub fn number_of_bytes(self) -> u64 { + into_parts(self).1 + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/storage.sw b/sway-lsp/tests/fixtures/fixtures-core/src/storage.sw new file mode 100644 index 00000000000..959a7f1dfa6 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/storage.sw @@ -0,0 +1,20 @@ +library; + +/// Describes a location in storage. +/// +/// # Additional Information +/// +/// The location in storage is specified by the `b256` key of a particular storage slot and an +/// offset, in words, from the start of the storage slot at `key`. The parameter `T` is the type of +/// the data to be read from or written to at `offset`. +/// `field_id` is a unique identifier for the storage field being referred to, it is different even +/// for multiple zero sized fields that might live at the same location but +/// represent different storage constructs. +pub struct StorageKey { + /// The assigned location in storage. + slot: b256, + /// The assigned offset based on the data structure `T`. + offset: u64, + /// A unique identifier. + field_id: b256, +} diff --git a/sway-lsp/tests/fixtures/fixtures-core/src/str.sw b/sway-lsp/tests/fixtures/fixtures-core/src/str.sw new file mode 100644 index 00000000000..c6b80f7542d --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-core/src/str.sw @@ -0,0 +1,30 @@ +library; + +impl str { + /// Return a `raw_ptr` to the begining of the string slice on the heap + pub fn as_ptr(self) -> raw_ptr { + let (ptr, _) = asm(s: self) { s: (raw_ptr, u64) }; + ptr + } + + /// Return the length of the string slice in bytes + pub fn len(self) -> u64 { + let (_, len) = asm(s: self) { s: (raw_ptr, u64) }; + len + } +} + +pub fn from_str_array(s: S) -> str { + __assert_is_str_array::(); + let str_size = __size_of_str_array::(); + let src = __addr_of(s); + + let ptr = asm(size: __size_of::(), dest, src: src) { + aloc size; + move dest hp; + mcp dest src size; + dest: raw_ptr + }; + + asm(s: (ptr, str_size)) { s: str } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/Forc.lock b/sway-lsp/tests/fixtures/fixtures-std/Forc.lock new file mode 100644 index 00000000000..834567f83a6 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = 'core' +source = 'path+from-root-5863B5D56901589E' + +[[package]] +name = 'std' +source = 'member' +dependencies = ['core'] diff --git a/sway-lsp/tests/fixtures/fixtures-std/Forc.toml b/sway-lsp/tests/fixtures/fixtures-std/Forc.toml new file mode 100644 index 00000000000..f5283443d76 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "std" + +[dependencies] +core = { path = "../fixtures-core" } diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/address.sw b/sway-lsp/tests/fixtures/fixtures-std/src/address.sw new file mode 100644 index 00000000000..6f5cc388b92 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/address.sw @@ -0,0 +1,154 @@ +//! A wrapper around the `b256` type to help enhance type-safety. +library; + +use ::alias::SubId; +use ::call_frames::contract_id; +use ::contract_id::AssetId; +use ::convert::From; +use ::hash::*; +use ::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL; +use ::hash::sha256; +use ::revert::revert; +use ::outputs::{Output, output_amount, output_count, output_type}; + +/// The `Address` type, a struct wrapper around the inner `b256` value. +pub struct Address { + /// The underlying raw `b256` data of the address. + value: b256, +} + +impl core::ops::Eq for Address { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} + +/// Functions for casting between the `b256` and `Address` types. +impl From for Address { + /// Casts raw `b256` data to an `Address`. + /// + /// # Arguments + /// + /// * `bits`: [b256] - The raw `b256` data to be casted. + /// + /// # Returns + /// + /// * [Address] - The newly created `Address` from the raw `b256`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let address = Address::from(ZERO_B256); + /// } + /// ``` + fn from(bits: b256) -> Self { + Self { value: bits } + } + + /// Casts an `Address` to raw `b256` data. + /// + /// # Returns + /// + /// * [b256] - The underlying raw `b256` data of the `Address`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let address = Address::from(ZERO_B256); + /// let b256_data = address.into(); + /// assert(b256_data == ZERO_B256); + /// } + /// ``` + fn into(self) -> b256 { + self.value + } +} + +impl Address { + /// Transfer `amount` coins of type `asset_id` and send them to + /// the Address. + /// + /// # Arguments + /// + /// * `asset_id`: [AssetId] - The `AssetId` of the token to transfer. + /// * `amount`: [u64] - The amount of tokens to transfer. + /// + /// # Reverts + /// + /// * When `amount` is greater than the contract balance for `asset_id`. + /// * When `amount` is equal to zero. + /// * When there are no free variable outputs. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::{BASE_ASSET_ID, ZERO_B256}; + /// + /// fn foo() { + /// let address = Address::from(ZERO_B256); + /// address.transfer(BASE_ASSET_ID, 500); + /// } + /// ``` + pub fn transfer(self, asset_id: AssetId, amount: u64) { + // maintain a manual index as we only have `while` loops in sway atm: + let mut index = 0; + + // If an output of type `OutputVariable` is found, check if its `amount` is + // zero. As one cannot transfer zero coins to an output without a panic, a + // variable output with a value of zero is by definition unused. + let number_of_outputs = output_count(); + while index < number_of_outputs { + if let Output::Variable = output_type(index) { + if output_amount(index) == 0 { + asm(r1: self.value, r2: index, r3: amount, r4: asset_id.value) { + tro r1 r2 r3 r4; + }; + return; + } + } + index += 1; + } + + revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL); + } +} + +impl Address { + /// Mint `amount` coins of the current contract's `asset_id` and send them to + /// the Address. + /// + /// # Arguments + /// + /// * `sub_id`: [SubId] - The sub id of the token to mint. + /// * `amount`: [u64] - The amount of tokens to mint. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let address = Address::from(ZERO_B256); + /// address.mint_to(ZERO_B256, 500); + /// } + /// ``` + pub fn mint_to(self, sub_id: SubId, amount: u64) { + asm(r1: amount, r2: sub_id) { + mint r1 r2; + }; + self.transfer(AssetId::new(contract_id(), sub_id), amount); + } +} + +impl Hash for Address { + fn hash(self, ref mut state: Hasher) { + let Address { value } = self; + value.hash(state); + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/alias.sw b/sway-lsp/tests/fixtures/fixtures-std/src/alias.sw new file mode 100644 index 00000000000..53066566465 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/alias.sw @@ -0,0 +1,4 @@ +library; + +/// The `SubId` type is simply an alias for `b256` that represents the sub identifier of a native asset. +pub type SubId = b256; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/alloc.sw b/sway-lsp/tests/fixtures/fixtures-std/src/alloc.sw new file mode 100644 index 00000000000..5d3790badb9 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/alloc.sw @@ -0,0 +1,156 @@ +//! A library for allocating memory inspired by [Rust's std::alloc](https://doc.rust-lang.org/std/alloc/index.html). +library; + +/// Allocates zeroed memory on the heap. +/// +/// # Additional Information +/// +/// In the FuelVM, the heap begins at `VM_MAX_RAM` and grows downward. +/// The heap pointer(`$hp`) always points to the first allocated byte. +/// +/// Initially the heap will look like this: +/// ``` +/// â–¾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// â–´VM_MAX_RAM +/// ``` +/// After allocating with `let ptr = alloc::(1)`: +/// ``` +/// â–¾$hp +/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +/// â–´ptr â–´VM_MAX_RAM +/// ``` +/// After writing with `sw(ptr, u64::max())`: +/// ``` +/// â–¾$hp +/// ... 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF | +/// â–´ptr â–´VM_MAX_RAM +/// ``` +/// For more information, see the Fuel Spec for [VM Initialization](https://fuellabs.github.io/fuel-specs/master/vm#vm-initialization) +/// and the VM Instruction Set for [Memory Allocation](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set.html#aloc-allocate-memory). +/// +/// # Arguments +/// +/// * `count`: [u64] - The number of `size_of` bytes to allocate onto the heap. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly allocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::alloc; +/// +/// fn foo() { +/// let ptr = alloc::(2); +/// assert(!ptr.is_null()); +/// } +/// ``` +pub fn alloc(count: u64) -> raw_ptr { + asm(size: __size_of::() * count, ptr) { + aloc size; + move ptr hp; + ptr: raw_ptr + } +} + +/// Reallocates the given area of memory. +/// +/// # Arguments +/// +/// * `ptr`: [raw_ptr] - The pointer to the area of memory to reallocate. +/// * `count`: [u64] - The number of `size_of` bytes kept when reallocating. These are not set to 0. +/// * `new_count`: [u64] - The number of new `size_of` bytes to allocate. These are set to 0. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly reallocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::{alloc, realloc}; +/// +/// fn foo() { +/// let ptr = alloc::(1); +/// ptr.write(5); +/// let reallocated_ptr = realloc::(ptr, 1, 2); +/// assert(reallocated_ptr.read::() == 5); +/// } +/// ``` +pub fn realloc(ptr: raw_ptr, count: u64, new_count: u64) -> raw_ptr { + if new_count > count { + let new_ptr = alloc::(new_count); + if count > 0 { + ptr.copy_to::(new_ptr, count); + } + new_ptr + } else { + ptr + } +} + +/// Allocates zeroed memory on the heap in individual bytes. +/// +/// # Arguments +/// +/// * `count`: [u64] - The number of bytes to allocate onto the heap. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly allocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::alloc_bytes; +/// +/// fn foo() { +/// let ptr = alloc_bytes(2); +/// assert(!ptr.is_null()); +/// } +/// ``` +pub fn alloc_bytes(count: u64) -> raw_ptr { + asm(size: count, ptr) { + aloc size; + move ptr hp; + ptr: raw_ptr + } +} + +/// Reallocates the given area of memory in individual bytes. +/// +/// # Arguments +/// +/// * `ptr`: [raw_ptr] - The pointer to the area of memory to reallocate. +/// * `count`: [u64] - The number of bytes kept when reallocating. These are not set to 0. +/// * `new_count`: [u64] - The number of new bytes to allocate. These are set to 0. +/// +/// # Returns +/// +/// * [raw_ptr] - The pointer to the newly reallocated memory. +/// +/// # Examples +/// +/// ```sway +/// use std::alloc::{alloc_bytes, realloc_bytes}; +/// +/// fn foo() { +/// let ptr = alloc_bytes(8); +/// ptr.write(5); +/// let reallocated_ptr = realloc_bytes(ptr, 8, 16); +/// assert(reallocated_ptr.read::() == 5); +/// } +/// ``` +pub fn realloc_bytes(ptr: raw_ptr, count: u64, new_count: u64) -> raw_ptr { + if new_count > count { + let new_ptr = alloc_bytes(new_count); + if count > 0 { + ptr.copy_bytes_to(new_ptr, count); + } + new_ptr + } else { + ptr + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions.sw new file mode 100644 index 00000000000..1b470fcc586 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions.sw @@ -0,0 +1,7 @@ +library; + +pub mod u16; +pub mod u32; +pub mod u64; +pub mod b256; +pub mod u256; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/b256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/b256.sw new file mode 100644 index 00000000000..1d65b0ed044 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/b256.sw @@ -0,0 +1,201 @@ +library; + +use ::array_conversions::u64::*; +use ::assert::assert; + +impl b256 { + /// Converts the `b256` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `b256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = b256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_le_bytes(); + let b = b.to_le_bytes(); + let c = c.to_le_bytes(); + let d = d.to_le_bytes(); + + let (a,b,c,d) = (d,c,b,a); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of little-endian bytes to a `b256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `b256`. + /// + /// # Returns + /// + /// * [b256] - The resulting `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = b256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// ``` + pub fn from_le_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (d, c, b, a); + + asm(r1: result) { + r1: b256 + } + } + + /// Converts the `b256` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `b256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + /// let bytes = x.to_be_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes[i.as_u64()] == i + 1_u8); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_be_bytes(); + let b = b.to_be_bytes(); + let c = c.to_be_bytes(); + let d = d.to_be_bytes(); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of big-endian bytes to a `b256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `b256`. + /// + /// # Returns + /// + /// * [b256] - The resulting `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + /// 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + /// 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + /// 31_u8, 32_u8]; + /// let x = b256::from_be_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (a, b, c, d); + + asm(r1: result) { + r1: b256 + } + } +} + +#[test] +fn test_b256_from_le_bytes() { + let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + 2_u8, 1_u8]; + + let x = b256::from_le_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); +} + +#[test] +fn test_b256_to_le_bytes() { + let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + + let bytes = x.to_le_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == 32_u8 - i); + i += 1_u8; + } + +} + +#[test] +fn test_b256_from_be_bytes() { + let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + 31_u8, 32_u8]; + + let x = b256::from_be_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); +} + +#[test] +fn test_b256_to_be_bytes() { + let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + + let bytes = x.to_be_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == i + 1_u8); + i += 1_u8; + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u16.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u16.sw new file mode 100644 index 00000000000..d285975b2ea --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u16.sw @@ -0,0 +1,158 @@ +library; + +use ::assert::assert; + +impl u16 { + /// Converts the `u16` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 2]] - An array of 2 `u8` bytes that compose the `u16`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u16 = 513; + /// let result = x.to_le_bytes(); + /// + /// assert(result[0] == 1_u8); + /// assert(result[1] == 2_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 2] { + let output = [0_u8, 0_u8]; + + asm(input: self, off: 0xFF, i: 0x8, output: output, r1) { + and r1 input off; + sw output r1 i0; + + srl r1 input i; + and r1 r1 off; + sw output r1 i1; + + output: [u8; 2] + } + } + + /// Converts a sequence of little-endian bytes to a `u16`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 2]] - A sequence of 2 `u8` bytes that represent a `u16`. + /// + /// # Returns + /// + /// * [u16] - The resulting `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8]; + /// let result = u16::from_le_bytes(bytes); + /// + /// assert(result == 513_u16); + /// } + /// ``` + pub fn from_le_bytes(bytes: [u8; 2]) -> Self { + asm(a: bytes[0], b: bytes[1], i: 0x8, r1) { + sll r1 b i; + or r1 a r1; + r1: u16 + } + } + + /// Converts the `u16` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 2]] - An array of 2 `u8` bytes that compose the `u16`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u16 = 513; + /// let result = x.to_be_bytes(); + /// + /// assert(result[0] == 2_u8); + /// assert(result[1] == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 2] { + let output = [0_u8, 0_u8]; + + asm(input: self, off: 0xFF, i: 0x8, output: output, r1) { + srl r1 input i; + sw output r1 i0; + + and r1 input off; + sw output r1 i1; + + output: [u8; 2] + } + } + + /// Converts a sequence of big-endian bytes to a `u16`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 2]] - A sequence of 2 `u8` bytes that represent a `u16`. + /// + /// # Returns + /// + /// * [u32] - The resulting `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [2_u8, 1_u8]; + /// let result = u16::from_be_bytes(bytes); + /// + /// assert(result == 513_u16); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 2]) -> Self { + asm(a: bytes[0], b: bytes[1], i: 0x8, r1) { + sll r1 a i; + or r1 r1 b; + r1: u16 + } + } +} + +#[test] +fn test_u16_to_le_bytes() { + let x: u16 = 513; + let result = x.to_le_bytes(); + + assert(result[0] == 1_u8); + assert(result[1] == 2_u8); +} + +#[test] +fn test_u16_from_le_bytes() { + let bytes = [1_u8, 2_u8]; + let result = u16::from_le_bytes(bytes); + + assert(result == 513_u16); +} + +#[test] +fn test_u16_to_be_bytes() { + let x: u16 = 513; + let result = x.to_be_bytes(); + + assert(result[0] == 2_u8); + assert(result[1] == 1_u8); +} + +#[test] +fn test_u16_from_be_bytes() { + let bytes = [2_u8, 1_u8]; + let result = u16::from_be_bytes(bytes); + + assert(result == 513_u16); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u256.sw new file mode 100644 index 00000000000..6947d0e3416 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u256.sw @@ -0,0 +1,201 @@ +library; + +use ::array_conversions::u64::*; +use ::assert::assert; + +impl u256 { + /// Converts the `u256` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = u256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_le_bytes(); + let b = b.to_le_bytes(); + let c = c.to_le_bytes(); + let d = d.to_le_bytes(); + + let (a,b,c,d) = (d,c,b,a); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of little-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = u256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// ``` + pub fn from_le_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (d, c, b, a); + + asm(r1: result) { + r1: u256 + } + } + + /// Converts the `u256` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// let bytes = x.to_be_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes[i.as_u64()] == i + 1_u8); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_be_bytes(); + let b = b.to_be_bytes(); + let c = c.to_be_bytes(); + let d = d.to_be_bytes(); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of big-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + /// 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + /// 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + /// 31_u8, 32_u8]; + /// let x = u256::from_be_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (a, b, c, d); + + asm(r1: result) { + r1: u256 + } + } +} + +#[test] +fn test_u256_from_le_bytes() { + let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + 2_u8, 1_u8]; + + let x = u256::from_le_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); +} + +#[test] +fn test_u256_to_le_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + + let bytes = x.to_le_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == 32_u8 - i); + i += 1_u8; + } + +} + +#[test] +fn test_u256_from_be_bytes() { + let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + 31_u8, 32_u8]; + + let x = u256::from_be_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); +} + +#[test] +fn test_u256_to_be_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + + let bytes = x.to_be_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == i + 1_u8); + i += 1_u8; + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u32.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u32.sw new file mode 100644 index 00000000000..aeab723beaa --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u32.sw @@ -0,0 +1,191 @@ +library; + +use ::assert::assert; + +impl u32 { + /// Converts the `u32` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 4]] - An array of 4 `u8` bytes that compose the `u32`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u32 = 67305985; + /// let result = x.to_le_bytes(); + /// + /// assert(result[0] == 1_u8); + /// assert(result[1] == 2_u8); + /// assert(result[2] == 3_u8); + /// assert(result[3] == 4_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 4] { + let output = [0_u8, 0_u8, 0_u8, 0_u8]; + + asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, output: output, r1) { + and r1 input off; + sw output r1 i0; + + srl r1 input i; + and r1 r1 off; + sw output r1 i1; + + srl r1 input j; + and r1 r1 off; + sw output r1 i2; + + srl r1 input k; + and r1 r1 off; + sw output r1 i3; + + output: [u8; 4] + } + } + + /// Converts a sequence of little-endian bytes to a `u32`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 4]] - A sequence of 4 `u8` bytes that represent a `u32`. + /// + /// # Returns + /// + /// * [u32] - The resulting `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8, 3_u8, 4_u8]; + /// let result = u32::from_le_bytes(bytes); + /// + /// assert(result == 67305985_u32); + /// } + /// ``` + pub fn from_le_bytes(bytes: [u8; 4]) -> Self { + asm(a: bytes[0], b: bytes[1], c: bytes[2], d: bytes[3], i: 0x8, j: 0x10, k: 0x18, r1, r2, r3) { + sll r1 c j; + sll r2 d k; + or r3 r1 r2; + sll r1 b i; + or r2 a r1; + or r1 r2 r3; + r1: u32 + } + } + + /// Converts the `u32` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 4]] - An array of 4 `u8` bytes that compose the `u32`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u32 = 67305985; + /// let result = x.to_be_bytes(); + /// + /// assert(result[0] == 4_u8); + /// assert(result[1] == 3_u8); + /// assert(result[2] == 2_u8); + /// assert(result[3] == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 4] { + let output = [0_u8, 0_u8, 0_u8, 0_u8]; + + asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, output: output, r1) { + srl r1 input k; + and r1 r1 off; + sw output r1 i0; + + srl r1 input j; + and r1 r1 off; + sw output r1 i1; + + srl r1 input i; + and r1 r1 off; + sw output r1 i2; + + and r1 input off; + sw output r1 i3; + + output: [u8; 4] + } + } + + /// Converts a sequence of big-endian bytes to a `u32`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 4]] - A sequence of 4 `u8` bytes that represent a `u32`. + /// + /// # Returns + /// + /// * [u32] - The resulting `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [4_u8, 3_u8, 2_u8, 1_u8]; + /// let result = u32::from_be_bytes(bytes); + /// + /// assert(result == 67305985_u32); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 4]) -> Self { + asm(a: bytes[0], b: bytes[1], c: bytes[2], d: bytes[3], i: 0x8, j: 0x10, k: 0x18, r1, r2, r3) { + sll r1 a k; + sll r2 b j; + or r3 r1 r2; + sll r1 c i; + or r2 r3 r1; + or r1 r2 d; + r1: u32 + } + } +} + +#[test] +fn test_u32_to_le_bytes() { + let x: u32 = 67305985; + let result = x.to_le_bytes(); + + assert(result[0] == 1_u8); + assert(result[1] == 2_u8); + assert(result[2] == 3_u8); + assert(result[3] == 4_u8); +} + +#[test] +fn test_u32_from_le_bytes() { + let bytes = [1_u8, 2_u8, 3_u8, 4_u8]; + let result = u32::from_le_bytes(bytes); + + assert(result == 67305985_u32); +} + +#[test] +fn test_u32_to_be_bytes() { + let x: u32 = 67305985; + let result = x.to_be_bytes(); + + assert(result[0] == 4_u8); + assert(result[1] == 3_u8); + assert(result[2] == 2_u8); + assert(result[3] == 1_u8); +} + +#[test] +fn test_u32_from_be_bytes() { + let bytes = [4_u8, 3_u8, 2_u8, 1_u8]; + let result = u32::from_be_bytes(bytes); + + assert(result == 67305985_u32); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u64.sw b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u64.sw new file mode 100644 index 00000000000..597254a6834 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/array_conversions/u64.sw @@ -0,0 +1,275 @@ +library; + +use ::assert::assert; + +impl u64 { + /// Converts the `u64` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 8]] - An array of 8 `u8` bytes that compose the `u64`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u64 = 578437695752307201; + /// let result = x.to_le_bytes(); + /// + /// assert(result[0] == 1_u8); + /// assert(result[1] == 2_u8); + /// assert(result[2] == 3_u8); + /// assert(result[3] == 4_u8); + /// assert(result[4] == 5_u8); + /// assert(result[5] == 6_u8); + /// assert(result[6] == 7_u8); + /// assert(result[7] == 8_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 8] { + let output = [0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8]; + + asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, output: output, r1) { + and r1 input off; + sw output r1 i0; + + srl r1 input i; + and r1 r1 off; + sw output r1 i1; + + srl r1 input j; + and r1 r1 off; + sw output r1 i2; + + srl r1 input k; + and r1 r1 off; + sw output r1 i3; + + srl r1 input l; + and r1 r1 off; + sw output r1 i4; + + srl r1 input m; + and r1 r1 off; + sw output r1 i5; + + srl r1 input n; + and r1 r1 off; + sw output r1 i6; + + srl r1 input o; + and r1 r1 off; + sw output r1 i7; + + output: [u8; 8] + } + } + + /// Converts a sequence of little-endian bytes to a `u64`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 8]] - A sequence of 8 `u8` bytes that represent a `u64`. + /// + /// # Returns + /// + /// * [u64] - The resulting `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8]; + /// let result = u64::from_le_bytes(bytes); + /// + /// assert(result == 578437695752307201); + /// } + /// ``` + pub fn from_le_bytes(bytes: [u8; 8]) -> Self { + let a = bytes[0]; + let b = bytes[1]; + let c = bytes[2]; + let d = bytes[3]; + let e = bytes[4]; + let f = bytes[5]; + let g = bytes[6]; + let h = bytes[7]; + + asm(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, r1, r2, r3) { + sll r1 h o; + sll r2 g n; + or r3 r1 r2; + sll r1 f m; + or r2 r3 r1; + sll r3 e l; + or r1 r2 r3; + sll r2 d k; + or r3 r1 r2; + sll r1 c j; + or r2 r3 r1; + sll r3 b i; + or r1 r2 r3; + or r2 r1 a; + + r2: u64 + } + } + + /// Converts the `u64` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 8]] - An array of 8 `u8` bytes that compose the `u64`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u64 = 578437695752307201; + /// let result = x.to_be_bytes(); + /// + /// assert(result[0] == 8_u8); + /// assert(result[1] == 7_u8); + /// assert(result[2] == 6_u8); + /// assert(result[3] == 5_u8); + /// assert(result[4] == 4_u8); + /// assert(result[5] == 3_u8); + /// assert(result[6] == 2_u8); + /// assert(result[7] == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 8] { + let output = [0; 8]; + + asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, output: output, r1) { + and r1 input off; + sw output r1 i7; + + srl r1 input i; + and r1 r1 off; + sw output r1 i6; + + srl r1 input j; + and r1 r1 off; + sw output r1 i5; + + srl r1 input k; + and r1 r1 off; + sw output r1 i4; + + srl r1 input l; + and r1 r1 off; + sw output r1 i3; + + srl r1 input m; + and r1 r1 off; + sw output r1 i2; + + srl r1 input n; + and r1 r1 off; + sw output r1 i1; + + srl r1 input o; + and r1 r1 off; + sw output r1 i0; + + output: [u8; 8] + } + } + + /// Converts a sequence of big-endian bytes to a `u64`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 8]] - A sequence of 8 `u8` bytes that represent a `u64`. + /// + /// # Returns + /// + /// * [u64] - The resulting `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8]; + /// let result = u64::from_be_bytes(bytes); + /// + /// assert(result == 578437695752307201); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 8]) -> Self { + let a = bytes[0]; + let b = bytes[1]; + let c = bytes[2]; + let d = bytes[3]; + let e = bytes[4]; + let f = bytes[5]; + let g = bytes[6]; + let h = bytes[7]; + + asm(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, r1, r2, r3) { + sll r1 a o; + sll r2 b n; + or r3 r1 r2; + sll r1 c m; + or r2 r3 r1; + sll r3 d l; + or r1 r2 r3; + sll r2 e k; + or r3 r1 r2; + sll r1 f j; + or r2 r3 r1; + sll r3 g i; + or r1 r2 r3; + or r2 r1 h; + + r2: u64 + } + } +} + +#[test] +fn test_u64_to_le_bytes() { + let x: u64 = 578437695752307201; + let result = x.to_le_bytes(); + + assert(result[0] == 1_u8); + assert(result[1] == 2_u8); + assert(result[2] == 3_u8); + assert(result[3] == 4_u8); + assert(result[4] == 5_u8); + assert(result[5] == 6_u8); + assert(result[6] == 7_u8); + assert(result[7] == 8_u8); +} + +#[test] +fn test_u64_from_le_bytes() { + let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8]; + let result = u64::from_le_bytes(bytes); + + assert(result == 578437695752307201); +} + +#[test] +fn test_u64_to_be_bytes() { + let x: u64 = 578437695752307201; + let result = x.to_be_bytes(); + + assert(result[0] == 8_u8); + assert(result[1] == 7_u8); + assert(result[2] == 6_u8); + assert(result[3] == 5_u8); + assert(result[4] == 4_u8); + assert(result[5] == 3_u8); + assert(result[6] == 2_u8); + assert(result[7] == 1_u8); +} + +#[test] +fn test_u64_from_be_bytes() { + let bytes = [8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, 2_u8, 1_u8]; + let result = u64::from_be_bytes(bytes); + + assert(result == 578437695752307201); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/assert.sw b/sway-lsp/tests/fixtures/fixtures-std/src/assert.sw new file mode 100644 index 00000000000..d345e303bc7 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/assert.sw @@ -0,0 +1,65 @@ +//! Functions to assert a given condition. +library; + +use ::logging::log; +use ::revert::revert; +use ::error_signals::{FAILED_ASSERT_SIGNAL, FAILED_ASSERT_EQ_SIGNAL}; + + +/// Asserts that the given `condition` will always be `true` during runtime. +/// +/// # Additional Information +/// +/// To check for conditions that may not be `true`, use `std::revert::require` instead. +/// For more information, see the Wiki article on [Assertion](https://en.wikipedia.org/wiki/Assertion_(software_development)#Comparison_with_error_handling). +/// +/// # Arguments +/// +/// * `condition`: [bool] - The condition which will be asserted to be `true`. +/// +/// # Reverts +/// +/// * Reverts when `condition` is `false`. +/// +/// # Examples +/// +/// ```sway +/// fn foo(a: u64, b: u64) { +/// assert(a == b); +/// // if code execution continues, that means a was equal to b +/// log("a is equal to b"); +/// } +/// ``` +pub fn assert(condition: bool) { + if !condition { + revert(FAILED_ASSERT_SIGNAL); + } +} + +/// Asserts that the given values `v1` & `v2` will always be equal during runtime. +/// +/// # Arguments +/// +/// * `v1`: [T] - The first value to compare. +/// * `v2`: [T] - The second value to compare. +/// +/// # Reverts +/// +/// * Reverts when `v1` != `v2`. +/// +/// # Examples +/// +/// ```sway +/// fn foo(a: u64, b: u64) { +/// assert_eq(a, b); +/// // if code execution continues, that means `a` is equal to `b` +/// log("a is equal to b"); +/// } +/// ``` +pub fn assert_eq(v1: T, v2: T) where T: Eq { + if (v1 != v2) { + log(v1); + log(v2); + revert(FAILED_ASSERT_EQ_SIGNAL); + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/auth.sw b/sway-lsp/tests/fixtures/fixtures-std/src/auth.sw new file mode 100644 index 00000000000..19b88ceb97d --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/auth.sw @@ -0,0 +1,179 @@ +//! Functionality for determining who is calling a contract. +library; + +use ::address::Address; +use ::contract_id::ContractId; +use ::identity::Identity; +use ::option::Option::{self, *}; +use ::result::Result::{self, *}; +use ::inputs::{Input, input_count, input_owner, input_type}; + +/// The error type used when an `Identity` cannot be determined. +pub enum AuthError { + /// The caller is external, but the inputs to the transaction are not all owned by the same address. + InputsNotAllOwnedBySameAddress: (), + /// The caller is internal, but the `caller_address` function was called. + CallerIsInternal: (), +} + +/// Returns `true` if the caller is external (i.e. a `script`). +/// Otherwise, if the caller is a contract, returns `false`. +/// +/// # Additional Information +/// +/// For more information refer to the [VM Instruction Set](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set#gm-get-metadata). +/// +/// # Returns +/// +/// * [bool] - `true` if the caller is external, `false` otherwise. +/// +/// # Examples +/// +/// ```sway +/// use std::auth::caller_is_external; +/// +/// fn foo() { +/// if caller_is_external() { +/// log("Caller is external.") +/// } else { +/// log("Caller is a contract.") +/// } +/// } +/// ``` +pub fn caller_is_external() -> bool { + asm(r1) { + gm r1 i1; + r1: bool + } +} + +/// If the caller is internal, returns the contract ID of the caller. +/// +/// # Additional Information +/// +/// External calls result in undefined behaviour. +/// +/// # Returns +/// +/// * [ContractId] - The contract ID of the caller. +/// +/// # Examples +/// +/// ```sway +/// use std::auth::{caller_is_external, caller_contract_id}; +/// +/// fn foo() { +/// if !caller_is_external() { +/// let caller_contract_id = caller_contract_id(); +/// log(caller_contract_id); +/// } +/// } +/// ``` +pub fn caller_contract_id() -> ContractId { + ContractId::from(asm(r1) { + gm r1 i2; + r1: b256 + }) +} + +/// Get the `Identity` (i.e. `Address` or `ContractId`) from which a call was made. +/// Returns a `Ok(Identity)`, or `Err(AuthError)` if an identity cannot be determined. +/// +/// # Additional Information +/// +/// Returns an `AuthError::InputsNotAllOwnedBySameAddress` if the caller is external and the inputs to the transaction are not all owned by the same address. +/// Should not return an `AuthError::CallerIsInternal` under any circumstances. +/// +/// # Returns +/// +/// * [Result] - `Ok(Identity)` if the identity can be determined, `Err(AuthError)` otherwise. +/// +/// # Examples +/// +/// ```sway +/// fn foo() { +/// match msg_sender() { +/// Ok(Identity::Address(address)) => log(address), +/// Ok(Identity::ContractId(contract_id)) => log(contract_id), +/// Err(AuthError::InputsNotAllOwnedBySameAddress) => log("Inputs not all owned by same address."), +/// Err(AuthError::CallerIsInternal) => log("Hell froze over."), +/// } +/// } +/// ``` +pub fn msg_sender() -> Result { + if caller_is_external() { + match caller_address() { + Err(err) => Err(err), + Ok(owner) => Ok(Identity::Address(owner)), + } + } else { + // Get caller's `ContractId`. + Ok(Identity::ContractId(caller_contract_id())) + } +} + +/// Get the owner of the inputs (of type `Input::Coin` or `Input::Message`) to a +/// `TransactionScript` if they all share the same owner. +/// +/// # Returns +/// +/// * [Result] - `Ok(Address)` if the owner can be determined, `Err(AuthError)` otherwise. +/// +/// # Examples +/// +/// ```sway +/// use std::auth::inputs_owner; +/// +/// fn foo() { +/// match inputs_owner() { +/// Ok(address) => log(address), +/// Err(AuthError::InputsNotAllOwnedBySameAddress) => log("Inputs not all owned by same address."), +/// } +/// } +/// ``` +pub fn caller_address() -> Result { + let inputs = input_count(); + let mut candidate = None; + let mut i = 0u8; + + // Note: `inputs_count` is guaranteed to be at least 1 for any valid tx. + while i < inputs { + let type_of_input = input_type(i.as_u64()); + match type_of_input { + Input::Coin => (), + Input::Message => (), + _ => { + // type != InputCoin or InputMessage, continue looping. + i += 1u8; + continue; + } + } + + // type == InputCoin or InputMessage. + let owner_of_input = input_owner(i.as_u64()); + if candidate.is_none() { + // This is the first input seen of the correct type. + candidate = owner_of_input; + i += 1u8; + continue; + } + + // Compare current input owner to candidate. + // `candidate` and `input_owner` must be `Some`. + // at this point, so we can unwrap safely. + if owner_of_input.unwrap() == candidate.unwrap() { + // Owners are a match, continue looping. + i += 1u8; + continue; + } + + // Owners don't match. Return Err. + return Err(AuthError::InputsNotAllOwnedBySameAddress); + } + + // `candidate` must be `Some` if the caller is an address, otherwise it's a contract. + match candidate { + Some(address) => Ok(address), + None => Err(AuthError::CallerIsInternal), + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/b512.sw b/sway-lsp/tests/fixtures/fixtures-std/src/b512.sw new file mode 100644 index 00000000000..63ec5b0baad --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/b512.sw @@ -0,0 +1,56 @@ +//! A wrapper around two `b256` types to support the usage of 64-byte values in Sway, +//! which are needed when working with public keys and signatures. +library; + +use ::constants::ZERO_B256; +use ::convert::From; + +/// Stores two `b256`s in contiguous memory. +/// Guaranteed to be contiguous for use with ec-recover: `std::ecr::ec_recover`. +pub struct B512 { + /// The two `b256`s that make up the `B512`. + bytes: [b256; 2], +} + +impl core::ops::Eq for B512 { + fn eq(self, other: Self) -> bool { + (self.bytes)[0] == (other.bytes)[0] && (self.bytes)[1] == (other.bytes)[1] + } +} + +/// Functions for casting between `B512` and an array of two `b256`s. +impl From<(b256, b256)> for B512 { + fn from(components: (b256, b256)) -> Self { + Self { + bytes: [components.0, components.1], + } + } + + fn into(self) -> (b256, b256) { + ((self.bytes)[0], (self.bytes)[1]) + } +} + +/// Methods on the `B512` type. +impl B512 { + /// Initializes a new, zeroed `B512`. + /// + /// # Returns + /// + /// * [B512] - A zero value B512. + /// + /// # Examples + /// + /// ```sway + /// use std::b512::B512; + /// + /// fn foo() { + /// let zero = B512::new(); + /// } + /// ``` + pub fn new() -> Self { + Self { + bytes: [ZERO_B256, ZERO_B256], + } + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/block.sw b/sway-lsp/tests/fixtures/fixtures-std/src/block.sw new file mode 100644 index 00000000000..bcf3ea33ed0 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/block.sw @@ -0,0 +1,158 @@ +//! Functionality for accessing block-related data. +library; + +use ::assert::assert; +use ::constants::ZERO_B256; +use ::result::Result::{self, *}; +use ::logging::log; + +/// Error type for when the block hash cannot be found. +enum BlockHashError { + /// Error returned when the block hash cannot be found. + BlockHeightTooHigh: (), +} + +/// Get the current block height. +/// +/// # Returns +/// +/// * [u32] - The current block height. +/// +/// # Examples +/// +/// ```sway +/// use std::block::height; +/// +/// fn foo() { +/// let current_height = height(); +/// log(current_height); +/// } +/// ``` +pub fn height() -> u32 { + asm(height) { + bhei height; + height: u32 + } +} + +/// Get the TAI64 timestamp of the current block. +/// +/// # Additional Information +/// +/// The TAI64 timestamp begins at 2^62 seconds before 1970, and ends at 2^62 seconds after 1970, +/// with a TAI second defined as the duration of 9192631770 periods of the radiation corresponding +/// to the transition between the two hyperfine levels of the ground state of the cesium atom. +/// +/// # Returns +/// +/// * [u64] - The TAI64 timestamp of the current block. +/// +/// # Examples +/// +/// ```sway +/// use std::block::timestamp; +/// +/// fn foo() { +/// let current_timestamp = timestamp(); +/// log(current_timestamp); +/// } +/// ``` +pub fn timestamp() -> u64 { + timestamp_of_block(height()) +} + +/// Get the TAI64 timestamp of a block at a given `block_height`. +/// +/// # Additional Information +/// +/// The TAI64 timestamp begins at 2^62 seconds before 1970, and ends at 2^62 seconds after 1970, +/// with a TAI second defined as the duration of 9192631770 periods of the radiation corresponding +/// to the transition between the two hyperfine levels of the ground state of the cesium atom. +/// +/// # Arguments +/// +/// * `block_height`: [u32] - The height of the block to get the timestamp of. +/// +/// # Returns +/// +/// * [u64] - The TAI64 timestamp of the block at `block_height`. +/// +/// # Examples +/// +/// ```sway +/// use std::block::timestamp_of_block; +/// +/// fn foo() { +/// let timestamp_of_block_100 = timestamp_of_block(100u32); +/// log(timestamp_of_block_100); +/// } +/// ``` +pub fn timestamp_of_block(block_height: u32) -> u64 { + asm(timestamp, height: block_height) { + time timestamp height; + timestamp: u64 + } +} + +/// Get the header hash of the block at height `block_height` +/// +/// # Returns +/// +/// * [Result] - The header hash of the block at `block_height`, or a [BlockHashError] if the block is not found. +/// +/// # Examples +/// +/// ```sway +/// use std::block::block_header_hash; +/// +/// fn foo() { +/// let header_hash_of_block_100 = block_header_hash(100u32); +/// log(header_hash_of_block_100); +/// } +/// ``` +pub fn block_header_hash(block_height: u32) -> Result { + + let mut header_hash = ZERO_B256; + + asm(r1: __addr_of(header_hash), r2: block_height) { + bhsh r1 r2; + }; + + // `bhsh` returns b256(0) if the block is not found, so catch this and return an error + if header_hash == ZERO_B256 { + Err(BlockHashError::BlockHeightTooHigh) + } else { + Ok(header_hash) + } +} + +//////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////// + +#[test(should_revert)] +fn test_block_header_hash_err_current_height() { + // Get the header hash of the current block. Each time this test runs, the block height will be 1. calling BHSH with a height >= current height will fail. + let mut hash = block_header_hash(height()); + let correct_error = match hash { + Ok(_) => false, + Err(BlockHashError::BlockHeightTooHigh) => true, + }; + + assert(correct_error); +} + +#[test(should_revert)] +fn test_block_header_hash_err_future_height() { + + // Try to get header hash of a block in the future + // The function should return a BlockHashError + let hash = block_header_hash(height() + 1u32); + let correct_error = match hash { + Ok(_) => false, + Err(BlockHashError::BlockHeightTooHigh) => true, + }; + + assert(correct_error); + +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes.sw new file mode 100644 index 00000000000..d6441211b10 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes.sw @@ -0,0 +1,1277 @@ +//! The `Bytes` type is used when a collection of tightly-packed arbitrary bytes is needed. +library; + +use ::{alloc::{alloc_bytes, realloc_bytes}, vec::Vec}; +use ::assert::assert; +use ::intrinsics::size_of_val; +use ::option::Option::{self, *}; +use ::convert::From; + +struct RawBytes { + ptr: raw_ptr, + cap: u64, +} + +impl RawBytes { + /// Create a new `RawBytes` with zero capacity. + pub fn new() -> Self { + Self { + ptr: alloc_bytes(0), + cap: 0, + } + } + + /// Creates a `RawBytes` (on the heap) with exactly the capacity (in bytes) specified. + /// This is equivalent to calling `RawBytes::new` when `capacity` is zero. + pub fn with_capacity(capacity: u64) -> Self { + Self { + ptr: alloc_bytes(capacity), + cap: capacity, + } + } + + /// Gets the pointer of the allocation. + pub fn ptr(self) -> raw_ptr { + self.ptr + } + + /// Gets the capacity of the allocation. + pub fn capacity(self) -> u64 { + self.cap + } + + /// Grow the capacity of `Bytes` by doubling its current capacity. The + /// `realloc_bytes` function allocates memory on the heap and copies + /// the data from the old allocation to the new allocation. + pub fn grow(ref mut self) { + let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap }; + self.ptr = realloc_bytes(self.ptr, self.cap, new_cap); + self.cap = new_cap; + } +} + +/// A type used to represent raw bytes. +pub struct Bytes { + /// A barebones struct for the bytes. + buf: RawBytes, + /// The number of bytes being stored. + len: u64, +} + +impl Bytes { + /// Constructs a new, empty `Bytes`. + /// + /// # Additional Information + /// + /// The struct will not allocate until elements are pushed onto it. + /// + /// # Returns + /// + /// * [Bytes] - A new, empty `Bytes`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// assert(bytes.len() == 0); + /// assert(bytes.capacity() == 0); + /// } + /// ``` + pub fn new() -> Self { + Self { + buf: RawBytes::new(), + len: 0, + } + } + + /// Constructs a new, empty `Bytes` with the specified capacity. + /// + /// # Additional Information + /// + /// The `Bytes` will be able to hold exactly `capacity` bytes without + /// reallocating. If `capacity` is zero, the `Bytes` will not allocate. + /// + /// It is important to note that although the returned `Bytes` has the + /// capacity specified, the type will have a zero length. + /// + /// # Arguments + /// + /// * `capacity`: [u64] - The capacity with which to initialize the `Bytes`. + /// + /// # Returns + /// + /// * [Bytes] - A new, empty `Bytes` with the specified capacity. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::with_capacity(2); + /// // does not allocate + /// bytes.push(5); + /// // does not re-allocate + /// bytes.push(10); + /// } + /// ``` + pub fn with_capacity(capacity: u64) -> Self { + Self { + buf: RawBytes::with_capacity(capacity), + len: 0, + } + } + + /// Appends an element to the back of a `Bytes` collection. + /// + /// # Arguments + /// + /// * `byte`: [u8] - The element to be pushed onto the `Bytes`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// let a = 5u8; + /// let b = 7u8; + /// bytes.push(a); + /// bytes.push(b); + /// assert(bytes.len() == 2); + /// } + /// ``` + pub fn push(ref mut self, byte: u8) { + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.capacity() { + self.buf.grow(); + }; + + // Get a pointer to the end of the buffer, where the new element will + // be inserted. + let end = self.buf.ptr().add_uint_offset(self.len); + + // Write `byte` at pointer `end` + end.write_byte(byte); + + // Increment length. + self.len += 1; + } + + /// Removes the last element from a `Bytes` and returns it, or `None` if it + /// is empty. + /// + /// # Returns + /// + /// * [Option] - The last element of the `Bytes`, or `None` if it is empty. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// + /// let res = bytes.pop(); + /// assert(res.is_none()); + /// + /// bytes.push(5); + /// let res = bytes.pop(); + /// assert(res.unwrap() == 5); + /// assert(bytes.is_empty()); + /// } + /// ``` + pub fn pop(ref mut self) -> Option { + if self.len == 0 { + return None; + }; + // Decrement length. + self.len -= 1; + let target = self.buf.ptr().add_uint_offset(self.len); + + Some(target.read_byte()) + } + + /// Returns `Some(byte)` at `index`, or `None` if `index` is out of + /// bounds. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be returned. + /// + /// # Returns + /// + /// * [Option] - The element at the specified index, or `None` if the index is out of bounds. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Byte; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// bytes.push(5u8); + /// bytes.push(10u8); + /// bytes.push(15u8); + /// let item = bytes.get(1).unwrap(); + /// assert(item == 10u8); + /// let opt = bytes.get(10); + /// assert(opt.is_none()); // index out of bounds + /// } + /// ``` + pub fn get(self, index: u64) -> Option { + // First check that index is within bounds. + if self.len <= index { + return None; + }; + + let item_ptr = self.buf.ptr().add_uint_offset(index); + + Some(item_ptr.read_byte()) + } + + /// Updates an element at position `index` with a new element `value`. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be set. + /// * `value`: [u8] - The value of the element to be set. + /// + /// # Reverts + /// + /// * When `index` is greater than or equal to the length of Bytes. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8; + /// bytes.push(a); + /// bytes.push(b); + /// bytes.push(c); + /// + /// let d = 11u8; + /// + /// bytes.set(1, d); + /// + /// assert(bytes.len() == 3); + /// assert(bytes.get(0).unwrap() == a); + /// assert(bytes.get(1).unwrap() == d); + /// assert(bytes.get(2).unwrap() == c); + /// } + /// ``` + pub fn set(ref mut self, index: u64, value: u8) { + assert(index < self.len); + + let index_ptr = self.buf.ptr().add_uint_offset(index); + + index_ptr.write_byte(value); + } + + /// Inserts an element at position `index` within the Bytes, shifting all + /// elements after it to the right. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index at which to insert the element. + /// * `element`: [u8] - The element to be inserted. + /// + /// # Reverts + /// + /// * When `index > len`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Byte; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// let a = 11u8; + /// let b = 11u8; + /// let c = 11u8; + /// let d = 11u8; + /// bytes.push(a); + /// bytes.push(b); + /// bytes.push(c); + /// bytes.insert(1, d); + /// + /// assert(bytes.get(0).unwrap() == a); + /// assert(bytes.get(1).unwrap() == d); + /// assert(bytes.get(2).unwrap() == b); + /// assert(bytes.get(3).unwrap() == c); + /// } + /// ``` + pub fn insert(ref mut self, index: u64, element: u8) { + assert(index <= self.len); + + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.cap { + self.buf.grow(); + } + + let start = self.buf.ptr(); + + // The spot to put the new value. + let index_ptr = start.add_uint_offset(index); + + // Shift everything over to make space. + let mut i = self.len; + while i > index { + let idx_ptr = start.add_uint_offset(i); + let previous = idx_ptr.sub_uint_offset(1); + previous.copy_bytes_to(idx_ptr, 1); + i -= 1; + } + + // Write `element` at pointer `index`. + index_ptr.write_byte(element); + + // Increment length. + self.len += 1; + } + + /// Removes and returns the element at position `index` within the Bytes, + /// shifting all elements after it to the left. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be removed. + /// + /// # Returns + /// + /// * [u8] - The element at the specified index. + /// + /// # Reverts + /// + /// * When `index >= self.len`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Byte; + /// + /// fn foo() { + /// let bytes = Byte::new(); + /// bytes.push(5); + /// bytes.push(10); + /// bytes.push(15); + /// let item = bytes.remove(1); + /// assert(item == 10); + /// assert(bytes.get(0).unwrap() == 5); + /// assert(bytes.get(1).unwrap() == 15); + /// assert(bytes.get(2).is_none()); + /// } + /// ``` + pub fn remove(ref mut self, index: u64) -> u8 { + // Panic if index >= length. + assert(index < self.len); + let start = self.buf.ptr(); + + let item_ptr = start.add_uint_offset(index); + // Read the value at `index` + let ret = item_ptr.read_byte(); + + // Shift everything down to fill in that spot. + let mut i = index; + while i < self.len { + let idx_ptr = start.add_uint_offset(i); + let next = idx_ptr.add_uint_offset(1); + next.copy_bytes_to(idx_ptr, 1); + i += 1; + } + + // Decrease length. + self.len -= 1; + ret + } + + /// Swaps two elements. + /// + /// # Arguments + /// + /// * `element1_index`: [u64] - The index of the first element. + /// * `element2_index`: [u64] - The index of the second element. + /// + /// # Reverts + /// + /// * When `element1_index` or `element2_index` is greater than or equal to the length of `Bytes`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8; + /// bytes.push(a); + /// bytes.push(b); + /// bytes.push(c); + /// + /// bytes.swap(0, 1); + /// + /// assert(bytes.get(0).unwrap() == b); + /// assert(bytes.get(1).unwrap() == a); + /// assert(bytes.get(2).unwrap() == c); + /// } + /// ``` + pub fn swap(ref mut self, element1_index: u64, element2_index: u64) { + assert(element1_index < self.len); + assert(element2_index < self.len); + + if element1_index == element2_index { + return; + } + + let start = self.buf.ptr(); + + let element1_ptr = start.add_uint_offset(element1_index); + let element2_ptr = start.add_uint_offset(element2_index); + + let element1_val = element1_ptr.read_byte(); + element2_ptr.copy_bytes_to(element1_ptr, 1); + element2_ptr.write_byte(element1_val); + } + + /// Gets the capacity of the allocation. + /// + /// # Returns + /// + /// * [u64] - The capacity of the allocation. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::with_capacity(5); + /// let cap = bytes.capacity(); + /// assert(cap == 5); + /// } + /// ``` + pub fn capacity(self) -> u64 { + self.buf.cap + } + + /// Gets the length of the `Bytes`. + /// + /// # Returns + /// + /// * [u64] - The length of the `Bytes`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// assert(bytes.len() == 0); + /// bytes.push(5); + /// assert(bytes.len() == 1); + /// } + /// ``` + pub fn len(self) -> u64 { + self.len + } + + /// Clears the `Bytes`, removing all values. + /// + /// # Additional Information + /// + /// Note that this method has no effect on the allocated capacity + /// of the Bytes. + /// + /// # Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// bytes.push(5); + /// bytes.clear() + /// assert(bytes.is_empty()); + /// } + /// ``` + pub fn clear(ref mut self) { + self.buf.ptr = alloc_bytes(0); + self.len = 0; + self.buf.cap = 0; + } + + /// Returns `true` if the type contains no elements. + /// + /// # Returns + /// + /// * [bool] - `true` if the type contains no elements, `false` otherwise. + /// + /// # Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// fn foo() { + /// let bytes = Bytes::new(); + /// assert(bytes.is_empty()); + /// bytes.push(5); + /// assert(!bytes.is_empty()); + /// bytes.clear() + /// assert(bytes.is_empty()); + /// } + /// ``` + pub fn is_empty(self) -> bool { + self.len == 0 + } +} + +// Need to use seperate impl blocks for now: https://github.com/FuelLabs/sway/issues/1548 +impl Bytes { + /// Divides one Bytes into two at an index. + /// + /// # Additional Information + /// + /// The first will contain all indices from `[0, mid)` (excluding the index + /// `mid` itself) and the second will contain all indices from `[mid, len)` + /// (excluding the index `len` itself). + /// + /// # Arguments + /// + /// * `mid`: [u64] - Index at which the Bytes is to be split. + /// + /// # Reverts + /// + /// * When `mid > self.len`. + /// + /// # Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5u8); + /// bytes.push(7u8); + /// bytes.push(9u8); + /// assert(bytes.len() == 3); + /// let mid = 1; + /// let (left, right) = bytes.split_at(mid); + /// assert(left.capacity() == mid); + /// assert(right.capacity() == bytes.len() - mid); + /// assert(left.len() == 1); + /// assert(right.len() == 2); + /// } + /// ``` + pub fn split_at(self, mid: u64) -> (Self, Self) { + assert(self.len >= mid); + + let left_len = mid; + let right_len = self.len - mid; + + let mut left_bytes = Self { buf: RawBytes::with_capacity(left_len), len: left_len }; + let mut right_bytes = Self { buf: RawBytes::with_capacity(right_len), len: right_len }; + + if mid > 0 { + self.buf.ptr().copy_bytes_to(left_bytes.buf.ptr(), left_len); + }; + if mid != self.len { + self.buf.ptr().add_uint_offset(mid).copy_bytes_to(right_bytes.buf.ptr(), right_len); + }; + + left_bytes.len = left_len; + right_bytes.len = right_len; + + (left_bytes, right_bytes) + } + + /// Moves all elements of `other` into `self`, leaving `other` empty. + /// + /// # Arguments + /// + /// * `other`: [Bytes] - The Bytes to append to self. + /// + /// # Examples + /// + /// ```sway + /// + /// use std:bytes::Bytes; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5u8); + /// bytes.push(7u8); + /// bytes.push(9u8); + /// assert(bytes.len() == 3); + /// + /// let mut bytes2 = Bytes::new(); + /// bytes2.push(5u8); + /// bytes2.push(7u8); + /// bytes2.push(9u8); + /// assert(bytes2.len() == 3); + /// + /// let first_length = bytes.len(); + /// let second_length = bytes2.len(); + /// let first_cap = bytes.capacity(); + /// let second_cap = bytes2.capacity(); + /// bytes.append(bytes2); + /// assert(bytes.len() == first_length + second_length); + /// assert(bytes.capacity() == first_cap + second_cap); + /// } + /// ``` + pub fn append(ref mut self, ref mut other: self) { + if other.len == 0 { + return + }; + + // optimization for when starting with empty bytes and appending to it + if self.len == 0 { + self = other; + other.clear(); + return; + }; + + let both_len = self.len + other.len; + let other_start = self.len; + + // reallocate with combined capacity, write `other`, set buffer capacity + self.buf.ptr = realloc_bytes(self.buf.ptr(), self.buf.capacity(), both_len); + + let mut i = 0; + while i < other.len { + let new_ptr = self.buf.ptr().add_uint_offset(other_start); + new_ptr.add_uint_offset(i).write_byte(other.buf.ptr.add_uint_offset(i).read_byte()); + i += 1; + } + + // set capacity and length + self.buf.cap = both_len; + self.len = both_len; + + // clear `other` + other.clear(); + } +} + +impl core::ops::Eq for Bytes { + fn eq(self, other: Self) -> bool { + if self.len != other.len { + return false; + } + + asm(result, r2: self.buf.ptr, r3: other.buf.ptr, r4: self.len) { + meq result r2 r3 r4; + result: bool + } + } +} + +impl AsRawSlice for Bytes { + /// Returns a raw slice of all of the elements in the type. + fn as_raw_slice(self) -> raw_slice { + asm(ptr: (self.buf.ptr(), self.len)) { ptr: raw_slice } + } +} + +/// Methods for converting between the `Bytes` and the `b256` types. +impl From for Bytes { + fn from(b: b256) -> Self { + // Artificially create bytes with capacity and len + let mut bytes = Self::with_capacity(32); + bytes.len = 32; + // Copy bytes from contract_id into the buffer of the target bytes + __addr_of(b).copy_bytes_to(bytes.buf.ptr, 32); + + bytes + } + + // NOTE: this cas be lossy! Added here as the From trait currently requires it, + // but the conversion from `Bytes` ->`b256` should be implemented as + // `impl TryFrom for b256` when the `TryFrom` trait lands: + // https://github.com/FuelLabs/sway/pull/3881 + fn into(self) -> b256 { + let mut value = 0x0000000000000000000000000000000000000000000000000000000000000000; + let ptr = __addr_of(value); + self.buf.ptr().copy_to::(ptr, 1); + + value + } +} + +impl From for Bytes { + /// Creates a `Bytes` from a `raw_slice`. + /// + /// ### Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// let mut vec = Vec::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8 + /// + /// vec.push(a); + /// vec.push(b); + /// vec.push(c); + /// + /// let vec_as_raw_slice = vec.as_raw_slice(); + /// let bytes = Bytes::from(vec_as_raw_slice); + /// + /// assert(bytes.len == 3); + /// assert(bytes.get(0).unwrap() == a); + /// assert(bytes.get(1).unwrap() == b); + /// assert(bytes.get(2).unwrap() == c); + /// ``` + fn from(slice: raw_slice) -> Self { + let number_of_bytes = slice.number_of_bytes(); + Self { + buf: RawBytes { + ptr: slice.ptr(), + cap: number_of_bytes, + }, + len: number_of_bytes, + } + } + + /// Creates a `raw_slice` from a `Bytes`. + /// + /// ### Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// let mut bytes = Bytes::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8 + /// bytes.push(a); + /// bytes.push(b); + /// bytes.push(c); + /// + /// assert(bytes.len() == 3); + /// + /// let slice: raw_slice = bytes.into(); + /// + /// assert(slice.number_of_bytes() == 3); + /// ``` + fn into(self) -> raw_slice { + asm(ptr: (self.buf.ptr(), self.len)) { ptr: raw_slice } + } +} + +impl From> for Bytes { + /// Creates a `Bytes` from a `Vec`. + /// + /// ### Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// let mut vec = Vec::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8 + /// + /// vec.push(a); + /// vec.push(b); + /// vec.push(c); + /// + /// let bytes = Bytes::from(vec); + /// + /// assert(bytes.len == 3); + /// assert(bytes.get(0).unwrap() == a); + /// assert(bytes.get(1).unwrap() == b); + /// assert(bytes.get(2).unwrap() == c); + /// ``` + fn from(vec: Vec) -> Self { + let mut bytes = Self::with_capacity(vec.len()); + let mut i = 0; + while i < vec.len() { + bytes.push(vec.get(i).unwrap()); + i += 1; + }; + bytes + } + + /// Creates a `Vec` from a `Bytes`. + /// + /// ### Examples + /// + /// ```sway + /// use std:bytes::Bytes; + /// + /// let mut bytes = Bytes::new(); + /// let a = 5u8; + /// let b = 7u8; + /// let c = 9u8 + /// bytes.push(a); + /// bytes.push(b); + /// bytes.push(c); + /// + /// assert(bytes.len() == 3); + /// + /// let vec: Vec = bytes.into(); + /// + /// assert(vec.len() == 3); + /// assert(vec.get(0).unwrap() == a); + /// assert(vec.get(1).unwrap() == b); + /// assert(vec.get(2).unwrap() == c); + /// ``` + fn into(self) -> Vec { + let mut vec = Vec::with_capacity(self.len); + let mut i = 0; + while i < self.len { + vec.push(self.get(i).unwrap()); + i += 1; + }; + vec + } +} + +// Tests +// +fn setup() -> (Bytes, u8, u8, u8) { + let mut bytes = Bytes::new(); + let a = 5u8; + let b = 7u8; + let c = 9u8; + bytes.push(a); + bytes.push(b); + bytes.push(c); + (bytes, a, b, c) +} + +#[test()] +fn test_new_bytes() { + let bytes = Bytes::new(); + assert(bytes.len() == 0); +} +#[test()] +fn test_push() { + let (_, a, b, c) = setup(); + let mut bytes = Bytes::new(); + bytes.push(a); + assert(bytes.len() == 1); + bytes.push(b); + assert(bytes.len() == 2); + bytes.push(c); + assert(bytes.len() == 3); +} +#[test()] +fn test_pop() { + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + bytes.push(42u8); + bytes.push(11u8); + bytes.push(69u8); + bytes.push(100u8); + bytes.push(200u8); + bytes.push(255u8); + bytes.push(180u8); + bytes.push(17u8); + bytes.push(19u8); + assert(bytes.len() == 12); + + let first = bytes.pop(); + assert(first.unwrap() == 19u8); + assert(bytes.len() == 11); + + let second = bytes.pop(); + assert(second.unwrap() == 17u8); + assert(bytes.len() == 10); + + let third = bytes.pop(); + assert(third.unwrap() == 180u8); + assert(bytes.len() == 9); + let _ = bytes.pop(); + let _ = bytes.pop(); + let _ = bytes.pop(); + let _ = bytes.pop(); + let _ = bytes.pop(); + let _ = bytes.pop(); + assert(bytes.len() == 3); + assert(bytes.pop().unwrap() == c); + assert(bytes.pop().unwrap() == b); + assert(bytes.pop().unwrap() == a); + assert(bytes.pop().is_none() == true); + assert(bytes.len() == 0); +} +#[test()] +fn test_len() { + let (mut bytes, _, _, _) = setup(); + assert(bytes.len() == 3); +} +#[test()] +fn test_clear() { + let (mut bytes, _, _, _) = setup(); + assert(bytes.len() == 3); + + bytes.clear(); + + assert(bytes.len() == 0); +} +#[test()] +fn test_packing() { + let mut bytes = Bytes::new(); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + bytes.push(5u8); + assert(bytes.len() == 11); + assert(bytes.capacity() == 16); + assert(size_of_val(bytes.buf) == 16); +} + +#[test()] +fn test_capacity() { + let mut bytes = Bytes::new(); + assert(bytes.capacity() == 0); + bytes.push(5u8); + assert(bytes.capacity() == 1); + bytes.push(7u8); + assert(bytes.capacity() == 2); + bytes.push(9u8); + assert(bytes.capacity() == 4); + bytes.push(11u8); + assert(bytes.capacity() == 4); + assert(bytes.len() == 4); + bytes.push(3u8); + assert(bytes.capacity() == 8); + assert(bytes.len() == 5); +} + +#[test()] +fn test_get() { + let (bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == b); + assert(bytes.get(2).unwrap() == c); + // get is non-modifying + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == b); + assert(bytes.get(2).unwrap() == c); + assert(bytes.len() == 3); +} + +#[test()] +fn test_remove() { + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + + let item = bytes.remove(1); + + assert(bytes.len() == 2); + assert(item == b); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == c); + assert(bytes.get(2).is_none()); +} + +#[test()] +fn test_insert() { + let (mut bytes, a, b, c) = setup(); + let d = 11u8; + assert(bytes.len() == 3); + + bytes.insert(1, d); + + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == d); + assert(bytes.get(2).unwrap() == b); + assert(bytes.get(3).unwrap() == c); + assert(bytes.len() == 4); +} + +#[test()] +fn test_swap() { + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + + bytes.swap(0, 1); + + assert(bytes.len() == 3); + assert(bytes.get(0).unwrap() == b); + assert(bytes.get(1).unwrap() == a); + assert(bytes.get(2).unwrap() == c); +} + +#[test()] +fn test_set() { + let (mut bytes, a, _b, c) = setup(); + assert(bytes.len() == 3); + let d = 11u8; + + bytes.set(1, d); + + assert(bytes.len() == 3); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == d); + assert(bytes.get(2).unwrap() == c); +} + +#[test()] +fn test_from_vec_u8() { + let mut vec = Vec::new(); + let (_, a, b, c) = setup(); + vec.push(a); + vec.push(b); + vec.push(c); + + let bytes = Bytes::from(vec); + + assert(bytes.len == 3); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == b); + assert(bytes.get(2).unwrap() == c); +} + +#[test()] +fn test_into_vec_u8() { + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + + let vec: Vec = bytes.into(); + + assert(vec.len() == 3); + assert(vec.get(0).unwrap() == a); + assert(vec.get(1).unwrap() == b); + assert(vec.get(2).unwrap() == c); +} + +#[test()] +fn test_bytes_limits() { + let mut bytes = Bytes::new(); + let max = 255u8; + let min = 0u8; + bytes.push(max); + bytes.push(min); + bytes.push(max); + bytes.push(min); + bytes.push(max); + bytes.push(min); + + assert(bytes.len() == 6); + assert(bytes.capacity() == 8); + assert(bytes.get(0).unwrap() == max); + assert(bytes.get(1).unwrap() == min); + assert(bytes.get(2).unwrap() == max); + assert(bytes.get(3).unwrap() == min); + assert(bytes.get(4).unwrap() == max); + assert(bytes.get(5).unwrap() == min); +} + +#[test()] +fn test_split_at() { + let (mut original, _a, _b, _c) = setup(); + assert(original.len() == 3); + let index = 1; + let (left, right) = original.split_at(index); + assert(original.capacity() == 4); + assert(right.capacity() == 2); + assert(left.len() == 1); + assert(right.len() == 2); +} + +#[test()] +fn test_split_at_0() { + let (mut original, _a, _b, _c) = setup(); + assert(original.len() == 3); + let index = 0; + let (left, right) = original.split_at(index); + assert(original.capacity() == 4); + assert(right.capacity() == 3); + assert(left.len() == 0); + assert(right.len() == 3); +} + +#[test()] +fn test_split_at_len() { + let (mut original, _a, _b, _c) = setup(); + assert(original.len() == 3); + let index = 3; + let (left, right) = original.split_at(index); + assert(original.capacity() == 4); + assert(right.capacity() == 0); + assert(left.len() == 3); + assert(right.len() == 0); +} + +#[test()] +fn test_append() { + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == b); + assert(bytes.get(2).unwrap() == c); + + let mut bytes2 = Bytes::new(); + let d = 5u8; + let e = 7u8; + let f = 9u8; + bytes2.push(d); + bytes2.push(e); + bytes2.push(f); + assert(bytes2.len() == 3); + assert(bytes2.get(0).unwrap() == d); + assert(bytes2.get(1).unwrap() == e); + assert(bytes2.get(2).unwrap() == f); + + let first_length = bytes.len(); + let second_length = bytes2.len(); + let _first_cap = bytes.capacity(); + let _second_cap = bytes2.capacity(); + bytes.append(bytes2); + assert(bytes.len() == first_length + second_length); + assert(bytes.capacity() == first_length + first_length); + let values = [a, b, c, d, e, f]; + let mut i = 0; + while i < 6 { + assert(bytes.get(i).unwrap() == values[i]); + i += 1; + }; +} + +#[test()] +fn test_append_empty_bytes() { + // nothing is appended or modified when appending an empty bytes. + let (mut bytes, a, b, c) = setup(); + assert(bytes.len() == 3); + assert(bytes.get(0).unwrap() == a); + assert(bytes.get(1).unwrap() == b); + assert(bytes.get(2).unwrap() == c); + + let mut bytes2 = Bytes::new(); + assert(bytes2.len() == 0); + let first_length = bytes.len(); + let first_cap = bytes.capacity(); + bytes.append(bytes2); + assert(bytes.len() == first_length); + assert(bytes.capacity() == first_cap); +} + +#[test()] +fn test_append_to_empty_bytes() { + let mut bytes = Bytes::new(); + assert(bytes.len() == 0); + let (mut bytes2, a, b, c) = setup(); + assert(bytes2.len() == 3); + + let _first_length = bytes.len(); + let _first_cap = bytes.capacity(); + let second_length = bytes2.len(); + let second_cap = bytes2.capacity(); + bytes.append(bytes2); + assert(bytes.len() == second_length); + assert(bytes.capacity() == second_cap); + let values = [a, b, c]; + let mut i = 0; + while i < 3 { + assert(bytes.get(i).unwrap() == values[i]); + i += 1; + }; + + assert(bytes2.len() == 0); + assert(bytes2.capacity() == 0); + +} + +#[test()] +fn test_eq() { + let (mut bytes, _a, _b, _c) = setup(); + let (mut bytes2, _a, _b, _c) = setup(); + assert(bytes == bytes2); + + let d = 5u8; + let e = 7u8; + let f = 9u8; + let mut other = Bytes::new(); + other.push(d); + other.push(e); + other.push(f); + assert(bytes == other); + + other.push(42u8); + assert(bytes != other); + + bytes.push(42u8); + assert(bytes == other); + + other.swap(0, 1); + assert(bytes != other); +} + +#[test()] +fn test_as_raw_slice() { + let val = 0x3497297632836282349729763283628234972976328362823497297632836282; + let slice_1 = asm(ptr: (__addr_of(val), 32)) { ptr: raw_slice }; + let mut bytes = Bytes::from(slice_1); + let slice_2 = bytes.as_raw_slice(); + assert(slice_1.ptr() == slice_2.ptr()); + assert(slice_1.number_of_bytes() == slice_2.number_of_bytes()); +} + +// This test will need to be updated once https://github.com/FuelLabs/sway/pull/3882 is resolved +#[test()] +fn test_from_raw_slice() { + let val = 0x3497297632836282349729763283628234972976328362823497297632836282; + let slice_1 = asm(ptr: (__addr_of(val), 32)) { ptr: raw_slice }; + let mut bytes = Bytes::from(slice_1); + let slice_2 = bytes.as_raw_slice(); + assert(slice_1.ptr() == slice_2.ptr()); + assert(slice_1.number_of_bytes() == slice_2.number_of_bytes()); +} + +#[test] +fn test_from_b256() { + let initial = 0x3333333333333333333333333333333333333333333333333333333333333333; + let b: Bytes = Bytes::from(initial); + let mut control_bytes = Bytes::with_capacity(32); + + let mut i = 0; + while i < 32 { + // 0x33 is 51 in decimal + control_bytes.push(51u8); + i += 1; + } + + assert(b == control_bytes); +} + +#[test] +fn test_into_b256() { + let mut initial_bytes = Bytes::with_capacity(32); + + let mut i = 0; + while i < 32 { + // 0x33 is 51 in decimal + initial_bytes.push(51u8); + i += 1; + } + + let value: b256 = initial_bytes.into(); + let expected: b256 = 0x3333333333333333333333333333333333333333333333333333333333333333; + + assert(value == expected); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions.sw new file mode 100644 index 00000000000..1b470fcc586 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions.sw @@ -0,0 +1,7 @@ +library; + +pub mod u16; +pub mod u32; +pub mod u64; +pub mod b256; +pub mod u256; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/b256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/b256.sw new file mode 100644 index 00000000000..d9f6a252ebb --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/b256.sw @@ -0,0 +1,200 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::alloc::alloc; +use ::option::Option; +use ::bytes_conversions::u64::*; + +impl b256 { + /// Converts the `b256` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 32 bytes that compose the `b256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + /// let bytes = x.to_le_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes.get(i.as_u64()).unwrap() == 32_u8 - i); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_le_bytes(self) -> Bytes { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_le_bytes(); + let b = b.to_le_bytes(); + let c = c.to_le_bytes(); + let d = d.to_le_bytes(); + + let (mut a, mut b, mut c, mut d) = (d,c,b,a); + + a.append(b); + a.append(c); + a.append(d); + + a + } + + /// Converts a sequence of little-endian bytes to a `b256`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 32 bytes that compose the `b256`. + /// + /// # Returns + /// + /// * [b256] - The resulting `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let mut bytes = Bytes::with_capacity(32); + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// bytes.push(32_u8 - i); + /// i += 1_u8; + /// } + /// + /// let x = b256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn from_le_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 32); + + let (a_bytes, rest) = bytes.split_at(8); + let (b_bytes, rest) = rest.split_at(8); + let (c_bytes, d_bytes) = rest.split_at(8); + + let a = u64::from_le_bytes(a_bytes); + let b = u64::from_le_bytes(b_bytes); + let c = u64::from_le_bytes(c_bytes); + let d = u64::from_le_bytes(d_bytes); + + let result = (d, c, b, a); + + asm(r1: result) { + r1: b256 + } + } + + /// Converts the `b256` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 32 bytes that compose the `b256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + /// let bytes = x.to_be_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes.get(i.as_u64()).unwrap() == i + 1_u8); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_be_bytes(self) -> Bytes { + Bytes::from(self) + } + + /// Converts a sequence of big-endian bytes to a `b256`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 32 bytes that compose the `b256`. + /// + /// # Returns + /// + /// * [b256] - The resulting `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let mut bytes = Bytes::with_capacity(32); + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// bytes.push(i + 1); + /// i += 1_u8; + /// } + /// + /// let x = b256::from_be_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn from_be_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 32); + bytes.into() + } +} + +#[test] +fn test_b256_from_le_bytes() { + let mut bytes = Bytes::with_capacity(32); + let mut i: u8 = 0; + while i < 32_u8 { + bytes.push(32_u8 - i); + i += 1_u8; + } + + let x = b256::from_le_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); +} + +#[test] +fn test_b256_to_le_bytes() { + let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + + let bytes = x.to_le_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes.get(i.as_u64()).unwrap() == 32_u8 - i); + i += 1_u8; + } +} + +#[test] +fn test_b256_from_be_bytes() { + let mut bytes = Bytes::with_capacity(32); + + let mut i: u8 = 0; + while i < 32_u8 { + bytes.push(i + 1_u8); + i += 1_u8; + } + + let x = b256::from_be_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); +} + +#[test] +fn test_b256_to_be_bytes() { + let x: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + + let bytes = x.to_be_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes.get(i.as_u64()).unwrap() == i + 1_u8); + i += 1_u8; + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u16.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u16.sw new file mode 100644 index 00000000000..9c5a5d6e6bd --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u16.sw @@ -0,0 +1,199 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::alloc::alloc; +use ::option::Option; + +impl u16 { + /// Converts the `u16` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 2 bytes that compose the `u16`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u16 = 513; + /// let result = x.to_le_bytes(); + /// + /// assert(result.get(0).unwrap() == 1_u8); + /// assert(result.get(1).unwrap() == 2_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, size: 2, ptr, r1) { + aloc size; + move ptr hp; + + and r1 input off; + sb ptr r1 i0; + + srl r1 input i; + and r1 r1 off; + sb ptr r1 i1; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 2)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of little-endian bytes to a `u16`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 2 bytes that compose the `u16`. + /// + /// # Returns + /// + /// * [u16] - The resulting `u16` value. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(1_u8); + /// bytes.push(2_u8); + /// let result = u16::from_le_bytes(bytes); + /// + /// assert(result == 513_u16); + /// } + /// ``` + pub fn from_le_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 2); + let ptr = bytes.buf.ptr(); + let a = ptr.read_byte(); + let b = (ptr.add_uint_offset(1)).read_byte(); + let i = 0x8; + asm(a: a, b: b, i: i, r1) { + sll r1 b i; + or r1 a r1; + r1: u16 + } + } + + /// Converts the `u16` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 2 bytes that compose the `u16`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes_conversions::u16; + /// + /// fn foo() { + /// let x: u16 = 513; + /// let result = x.to_be_bytes(); + /// + /// assert(result.get(0).unwrap() == 2_u8); + /// assert(result.get(1).unwrap() == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, size: 2, ptr, r1) { + aloc size; + move ptr hp; + + srl r1 input i; + sb ptr r1 i0; + + and r1 input off; + sb ptr r1 i1; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 2)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of big-endian bytes to a `u16`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 2 bytes that compose the `u16`. + /// + /// # Returns + /// + /// * [u16] - The resulting `u16` value. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes::Bytes; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(2_u8); + /// bytes.push(1_u8); + /// let result = u16::from_be_bytes(bytes); + /// + /// assert(result == 513_u16); + /// } + /// ``` + pub fn from_be_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 2); + let ptr = bytes.buf.ptr(); + let a = ptr.read_byte(); + let b = (ptr.add_uint_offset(1)).read_byte(); + + asm(a: a, b: b, i: 0x8, r1) { + sll r1 a i; + or r1 r1 b; + r1: u16 + } + } +} + +#[test] +fn test_u16_to_le_bytes() { + let x: u16 = 513; + let result = x.to_le_bytes(); + + assert(result.get(0).unwrap() == 1_u8); + assert(result.get(1).unwrap() == 2_u8); +} + +#[test] +fn test_u16_from_le_bytes() { + let mut bytes = Bytes::new(); + bytes.push(1_u8); + bytes.push(2_u8); + let result = u16::from_le_bytes(bytes); + + assert(result == 513_u16); +} + +#[test] +fn test_u16_to_be_bytes() { + let x: u16 = 513; + let result = x.to_be_bytes(); + + assert(result.get(0).unwrap() == 2_u8); + assert(result.get(1).unwrap() == 1_u8); +} + +#[test] +fn test_u16_from_be_bytes() { + let mut bytes = Bytes::new(); + bytes.push(2_u8); + bytes.push(1_u8); + let result = u16::from_be_bytes(bytes); + + assert(result == 513_u16); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u256.sw new file mode 100644 index 00000000000..b27dc40bf39 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u256.sw @@ -0,0 +1,204 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::alloc::alloc; +use ::option::Option; +use ::bytes_conversions::u64::*; + +impl u256 { + /// Converts the `u256` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 32 bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256; + /// let bytes = x.to_le_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes.get(i.as_u64()).unwrap() == 32_u8 - i); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_le_bytes(self) -> Bytes { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_le_bytes(); + let b = b.to_le_bytes(); + let c = c.to_le_bytes(); + let d = d.to_le_bytes(); + + let (mut a, mut b, mut c, mut d) = (d,c,b,a); + + a.append(b); + a.append(c); + a.append(d); + + a + } + + /// Converts a sequence of little-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 32 bytes that compose the `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let mut bytes = Bytes::with_capacity(32); + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// bytes.push(32_u8 - i); + /// i += 1_u8; + /// } + /// + /// let x = u256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256); + /// } + /// ``` + pub fn from_le_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 32); + + let (a_bytes, rest) = bytes.split_at(8); + let (b_bytes, rest) = rest.split_at(8); + let (c_bytes, d_bytes) = rest.split_at(8); + + let a = u64::from_le_bytes(a_bytes); + let b = u64::from_le_bytes(b_bytes); + let c = u64::from_le_bytes(c_bytes); + let d = u64::from_le_bytes(d_bytes); + + let result = (d, c, b, a); + + asm(r1: result) { + r1: u256 + } + } + + /// Converts the `u256` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 32 bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256; + /// let bytes = x.to_be_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes.get(i.as_u64()).unwrap() == i + 1_u8); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_be_bytes(self) -> Bytes { + let b: b256 = asm(r1: self) {r1: b256}; + Bytes::from(b) + } + + /// Converts a sequence of big-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 32 bytes that compose the `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let mut bytes = Bytes::with_capacity(32); + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// bytes.push(i + 1); + /// i += 1_u8; + /// } + /// + /// let x = u256::from_be_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256); + /// } + /// ``` + pub fn from_be_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 32); + let b: b256 = bytes.into(); + asm(r1: b) { + r1: u256 + } + } +} + +#[test] +fn test_u256_from_le_bytes() { + let mut bytes = Bytes::with_capacity(32); + let mut i: u8 = 0; + while i < 32_u8 { + bytes.push(32_u8 - i); + i += 1_u8; + } + + let x = u256::from_le_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256); +} + +#[test] +fn test_u256_to_le_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256; + + let bytes = x.to_le_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes.get(i.as_u64()).unwrap() == 32_u8 - i); + i += 1_u8; + } +} + +#[test] +fn test_u256_from_be_bytes() { + let mut bytes = Bytes::with_capacity(32); + + let mut i: u8 = 0; + while i < 32_u8 { + bytes.push(i + 1_u8); + i += 1_u8; + } + + let x = u256::from_be_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256); +} + +#[test] +fn test_u256_to_be_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256; + + let bytes = x.to_be_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes.get(i.as_u64()).unwrap() == i + 1_u8); + i += 1_u8; + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u32.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u32.sw new file mode 100644 index 00000000000..62b0ea2f8e1 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u32.sw @@ -0,0 +1,246 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::alloc::alloc; +use ::option::Option; + +impl u32 { + /// Converts the `u32` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 4 bytes that compose the `u32`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes_conversions::u32::*; + /// + /// fn foo() { + /// let x: u32 = 67305985; + /// let result = x.to_le_bytes(); + /// + /// assert(result.get(0).unwrap() == 1_u8); + /// assert(result.get(1).unwrap() == 2_u8); + /// assert(result.get(2).unwrap() == 3_u8); + /// assert(result.get(3).unwrap() == 4_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, size: 4, ptr, r1) { + aloc size; + move ptr hp; + + and r1 input off; + sb ptr r1 i0; + + srl r1 input i; + and r1 r1 off; + sb ptr r1 i1; + + srl r1 input j; + and r1 r1 off; + sb ptr r1 i2; + + srl r1 input k; + and r1 r1 off; + sb ptr r1 i3; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 4)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of little-endian bytes to a `u32`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 4 bytes that compose the `u32`. + /// + /// # Returns + /// + /// * [u32] - The resulting `u32` value. + /// + /// # Examples + /// + /// ```sway + /// use std::{bytes::Bytes, bytes_conversions::u32::*}; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(1_u8); + /// bytes.push(2_u8); + /// bytes.push(3_u8); + /// bytes.push(4_u8); + /// let result = u32::from_le_bytes(bytes); + /// + /// assert(result == 67305985_u32); + /// } + /// ``` + pub fn from_le_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 4); + let ptr = bytes.buf.ptr(); + let a = ptr.read_byte(); + let b = (ptr.add_uint_offset(1)).read_byte(); + let c = (ptr.add_uint_offset(2)).read_byte(); + let d = (ptr.add_uint_offset(3)).read_byte(); + + asm(a: a, b: b, c: c, d: d, i: 0x8, j: 0x10, k: 0x18, r1, r2, r3) { + sll r1 c j; + sll r2 d k; + or r3 r1 r2; + sll r1 b i; + or r2 a r1; + or r1 r2 r3; + r1: u32 + } + } + + /// Converts the `u32` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The 4 bytes that compose the `u32`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes_conversions::u32::*; + /// + /// fn foo() { + /// let x: u32 = 67305985; + /// let result = x.to_be_bytes(); + /// + /// assert(result.get(0).unwrap() == 4_u8); + /// assert(result.get(1).unwrap() == 3_u8); + /// assert(result.get(2).unwrap() == 2_u8); + /// assert(result.get(3).unwrap() == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, size: 4, ptr, r1) { + aloc size; + move ptr hp; + + srl r1 input k; + and r1 r1 off; + sb ptr r1 i0; + + srl r1 input j; + and r1 r1 off; + sb ptr r1 i1; + + srl r1 input i; + and r1 r1 off; + sb ptr r1 i2; + + and r1 input off; + sb ptr r1 i3; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 4)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of big-endian bytes to a `u32`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The 4 bytes that compose the `u32`. + /// + /// # Returns + /// + /// * [u32] - The resulting `u32` value. + /// + /// # Examples + /// + /// ```sway + /// use std::{bytes::Bytes, bytes_conversions::u32::*}; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(4_u8); + /// bytes.push(3_u8); + /// bytes.push(2_u8); + /// bytes.push(1_u8); + /// let result = u32::from_be_bytes(bytes); + /// + /// assert(result == 67305985_u32); + /// } + /// ``` + pub fn from_be_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 4); + let ptr = bytes.buf.ptr(); + let a = ptr.read_byte(); + let b = (ptr.add_uint_offset(1)).read_byte(); + let c = (ptr.add_uint_offset(2)).read_byte(); + let d = (ptr.add_uint_offset(3)).read_byte(); + + asm(a: a, b: b, c: c, d: d, i: 0x8, j: 0x10, k: 0x18, r1, r2, r3) { + sll r1 a k; + sll r2 b j; + or r3 r1 r2; + sll r1 c i; + or r2 r3 r1; + or r1 r2 d; + r1: u32 + } + } +} + +#[test] +fn test_u32_to_le_bytes() { + let x: u32 = 67305985; + let result = x.to_le_bytes(); + + assert(result.get(0).unwrap() == 1_u8); + assert(result.get(1).unwrap() == 2_u8); + assert(result.get(2).unwrap() == 3_u8); + assert(result.get(3).unwrap() == 4_u8); +} + +#[test] +fn test_u32_from_le_bytes() { + let mut bytes = Bytes::new(); + bytes.push(1_u8); + bytes.push(2_u8); + bytes.push(3_u8); + bytes.push(4_u8); + let result = u32::from_le_bytes(bytes); + + assert(result == 67305985_u32); +} + +#[test] +fn test_u32_to_be_bytes() { + let x: u32 = 67305985; + let result = x.to_be_bytes(); + + assert(result.get(0).unwrap() == 4_u8); + assert(result.get(1).unwrap() == 3_u8); + assert(result.get(2).unwrap() == 2_u8); + assert(result.get(3).unwrap() == 1_u8); +} + +#[test] +fn test_u32_from_be_bytes() { + let mut bytes = Bytes::new(); + bytes.push(4_u8); + bytes.push(3_u8); + bytes.push(2_u8); + bytes.push(1_u8); + let result = u32::from_be_bytes(bytes); + + assert(result == 67305985_u32); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u64.sw b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u64.sw new file mode 100644 index 00000000000..b286166967c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/bytes_conversions/u64.sw @@ -0,0 +1,336 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::alloc::alloc; +use ::option::Option; + +impl u64 { + /// Converts the `u64` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The bytes that compose the `u64`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes_conversions::u64::*; + /// + /// fn foo() { + /// let x: u64 = 578437695752307201; + /// let result = x.to_le_bytes(); + /// + /// assert(result.get(0).unwrap() == 1_u8); + /// assert(result.get(1).unwrap() == 2_u8); + /// assert(result.get(2).unwrap() == 3_u8); + /// assert(result.get(3).unwrap() == 4_u8); + /// assert(result.get(4).unwrap() == 5_u8); + /// assert(result.get(5).unwrap() == 6_u8); + /// assert(result.get(6).unwrap() == 7_u8); + /// assert(result.get(7).unwrap() == 8_u8); + /// } + /// ``` + pub fn to_le_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, size: 8, ptr, r1) { + aloc size; + move ptr hp; + + and r1 input off; + sb ptr r1 i0; + + srl r1 input i; + and r1 r1 off; + sb ptr r1 i1; + + srl r1 input j; + and r1 r1 off; + sb ptr r1 i2; + + srl r1 input k; + and r1 r1 off; + sb ptr r1 i3; + + srl r1 input l; + and r1 r1 off; + sb ptr r1 i4; + + srl r1 input m; + and r1 r1 off; + sb ptr r1 i5; + + srl r1 input n; + and r1 r1 off; + sb ptr r1 i6; + + srl r1 input o; + and r1 r1 off; + sb ptr r1 i7; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 8)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of little-endian bytes to a `u64`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - A `Bytes` object that represent a `u64`. + /// + /// # Returns + /// + /// * [u64] - The resulting `u64` value. + /// + /// # Examples + /// + /// ```sway + /// use std::{bytes::Bytes, bytes_conversions::u64::*}; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(1_u8); + /// bytes.push(2_u8); + /// bytes.push(3_u8); + /// bytes.push(4_u8); + /// bytes.push(5_u8); + /// bytes.push(6_u8); + /// bytes.push(7_u8); + /// bytes.push(8_u8); + /// let result = u64::from_le_bytes(bytes); + /// + /// assert(result == 578437695752307201); + /// } + /// ``` + pub fn from_le_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 8); + let ptr = bytes.buf.ptr(); + let a = ptr.read_byte(); + let b = (ptr.add_uint_offset(1)).read_byte(); + let c = (ptr.add_uint_offset(2)).read_byte(); + let d = (ptr.add_uint_offset(3)).read_byte(); + let e = (ptr.add_uint_offset(4)).read_byte(); + let f = (ptr.add_uint_offset(5)).read_byte(); + let g = (ptr.add_uint_offset(6)).read_byte(); + let h = (ptr.add_uint_offset(7)).read_byte(); + + asm(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, r1, r2, r3) { + sll r1 h o; + sll r2 g n; + or r3 r1 r2; + sll r1 f m; + or r2 r3 r1; + sll r3 e l; + or r1 r2 r3; + sll r2 d k; + or r3 r1 r2; + sll r1 c j; + or r2 r3 r1; + sll r3 b i; + or r1 r2 r3; + or r2 r1 a; + + r2: u64 + } + } + + /// Converts the `u64` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [Bytes] - The bytes that compose the `u64`. + /// + /// # Examples + /// + /// ```sway + /// use std::bytes_conversions::u64::*; + /// + /// fn foo() { + /// let x: u64 = 578437695752307201; + /// let result = x.to_be_bytes(); + /// + /// assert(result.get(0).unwrap() == 8_u8); + /// assert(result.get(1).unwrap() == 7_u8); + /// assert(result.get(2).unwrap() == 6_u8); + /// assert(result.get(3).unwrap() == 5_u8); + /// assert(result.get(4).unwrap() == 4_u8); + /// assert(result.get(5).unwrap() == 3_u8); + /// assert(result.get(6).unwrap() == 2_u8); + /// assert(result.get(7).unwrap() == 1_u8); + /// } + /// ``` + pub fn to_be_bytes(self) -> Bytes { + let ptr = asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, size: 8, ptr, r1) { + aloc size; + move ptr hp; + + and r1 input off; + sb ptr r1 i7; + + srl r1 input i; + and r1 r1 off; + sb ptr r1 i6; + + srl r1 input j; + and r1 r1 off; + sb ptr r1 i5; + + srl r1 input k; + and r1 r1 off; + sb ptr r1 i4; + + srl r1 input l; + and r1 r1 off; + sb ptr r1 i3; + + srl r1 input m; + and r1 r1 off; + sb ptr r1 i2; + + srl r1 input n; + and r1 r1 off; + sb ptr r1 i1; + + srl r1 input o; + and r1 r1 off; + sb ptr r1 i0; + + ptr: raw_ptr + }; + + let rs = asm(parts: (ptr, 8)) { + parts: raw_slice + }; + + Bytes::from(rs) + } + + /// Converts a sequence of big-endian bytes to a `u64`. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - A `Bytes` object that represent a `u64`. + /// + /// # Returns + /// + /// * [u64] - The resulting `u64` value. + /// + /// # Examples + /// + /// ```sway + /// use std::{bytes::Bytes, bytes_conversions::u64::*}; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(8_u8); + /// bytes.push(7_u8); + /// bytes.push(6_u8); + /// bytes.push(5_u8); + /// bytes.push(4_u8); + /// bytes.push(3_u8); + /// bytes.push(2_u8); + /// bytes.push(1_u8); + /// let result = u64::from_be_bytes(bytes); + /// + /// assert(result == 578437695752307201); + /// } + /// ``` + pub fn from_be_bytes(bytes: Bytes) -> Self { + assert(bytes.len() == 8); + let ptr = bytes.buf.ptr(); + let h = ptr.read_byte(); + let g = (ptr.add_uint_offset(1)).read_byte(); + let f = (ptr.add_uint_offset(2)).read_byte(); + let e = (ptr.add_uint_offset(3)).read_byte(); + let d = (ptr.add_uint_offset(4)).read_byte(); + let c = (ptr.add_uint_offset(5)).read_byte(); + let b = (ptr.add_uint_offset(6)).read_byte(); + let a = (ptr.add_uint_offset(7)).read_byte(); + + asm(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, r1, r2, r3) { + sll r1 h o; + sll r2 g n; + or r3 r1 r2; + sll r1 f m; + or r2 r3 r1; + sll r3 e l; + or r1 r2 r3; + sll r2 d k; + or r3 r1 r2; + sll r1 c j; + or r2 r3 r1; + sll r3 b i; + or r1 r2 r3; + or r2 r1 a; + + r2: u64 + } + } +} + +#[test] +fn test_u64_to_be_bytes() { + let x: u64 = 578437695752307201; + let result = x.to_be_bytes(); + + assert(result.get(0).unwrap() == 8_u8); + assert(result.get(1).unwrap() == 7_u8); + assert(result.get(2).unwrap() == 6_u8); + assert(result.get(3).unwrap() == 5_u8); + assert(result.get(4).unwrap() == 4_u8); + assert(result.get(5).unwrap() == 3_u8); + assert(result.get(6).unwrap() == 2_u8); + assert(result.get(7).unwrap() == 1_u8); +} + +#[test] +fn test_u64_from_be_bytes() { + let mut bytes = Bytes::new(); + bytes.push(8_u8); + bytes.push(7_u8); + bytes.push(6_u8); + bytes.push(5_u8); + bytes.push(4_u8); + bytes.push(3_u8); + bytes.push(2_u8); + bytes.push(1_u8); + let result = u64::from_be_bytes(bytes); + + assert(result == 578437695752307201); +} + +#[test] +fn test_u64_to_le_bytes() { + let x: u64 = 578437695752307201; + let result = x.to_le_bytes(); + + assert(result.get(0).unwrap() == 1_u8); + assert(result.get(1).unwrap() == 2_u8); + assert(result.get(2).unwrap() == 3_u8); + assert(result.get(3).unwrap() == 4_u8); + assert(result.get(4).unwrap() == 5_u8); + assert(result.get(5).unwrap() == 6_u8); + assert(result.get(6).unwrap() == 7_u8); + assert(result.get(7).unwrap() == 8_u8); +} + +#[test] +fn test_u64_from_le_bytes() { + let mut bytes = Bytes::new(); + bytes.push(1_u8); + bytes.push(2_u8); + bytes.push(3_u8); + bytes.push(4_u8); + bytes.push(5_u8); + bytes.push(6_u8); + bytes.push(7_u8); + bytes.push(8_u8); + let result = u64::from_le_bytes(bytes); + + assert(result == 578437695752307201); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/call_frames.sw b/sway-lsp/tests/fixtures/fixtures-std/src/call_frames.sw new file mode 100644 index 00000000000..937807722ef --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/call_frames.sw @@ -0,0 +1,215 @@ +//! Helper functions for accessing data from call frames. +//! [Call frames](https://fuellabs.github.io/fuel-specs/master/vm#call-frames) store metadata across untrusted inter-contract calls. +library; + +use ::contract_id::{AssetId, ContractId}; +use ::intrinsics::is_reference_type; +use ::registers::frame_ptr; + +// Note that everything when serialized is padded to word length. +// +// Call Frame : saved registers offset = 8 +// Reserved Registers: previous frame pointer offset = 6 +const SAVED_REGISTERS_OFFSET: u64 = 8; +const PREV_FRAME_POINTER_OFFSET: u64 = 6; +/// Where 73 is the current offset in words from the start of the call frame. +const FIRST_PARAMETER_OFFSET: u64 = 73; +/// Where 74 (73 + 1) is the current offset in words from the start of the call frame. +const SECOND_PARAMETER_OFFSET: u64 = 74; + +// Accessing the current call frame +// +/// Get the current contract's id when called in an internal context. +/// +/// # Additional Information +/// +/// **_Note:_** If called in an external context, this will **not** return a contract ID. +/// If called externally, will actually return a pointer to the transaction ID. +/// +/// # Returns +/// +/// * [ContractId] - The contract id of this contract. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::contract_id, constants::ZERO_B256, token::mint}; +/// +/// fn foo() { +/// let this_contract = contract_id(); +/// mint(ZERO_B256, 50); +/// Address::from(ZERO_B256).transfer(AssetId::default(this_contract), 50); +/// } +/// ``` +pub fn contract_id() -> ContractId { + ContractId::from(asm() { fp: b256 }) +} + +/// Get the `asset_id` of coins being sent from the current call frame. +/// +/// # Returns +/// +/// * [AssetId] - The asset included in the current call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::msg_asset_id, constants::BASE_ASSET_ID}; +/// +/// fn foo() { +/// let asset = msg_asset_id(); +/// assert(asset == BASE_ASSET_ID); +/// } +/// ``` +pub fn msg_asset_id() -> AssetId { + AssetId { + value: { + asm(asset_id) { + addi asset_id fp i32; + asset_id: b256 + } + } + } +} + +/// Get the code size in bytes (padded to word alignment) from the current call frame. +/// +/// # Additional Information +/// +/// More information on data from call frames can be found in the Fuel Specs. +/// https://specs.fuel.network/master/fuel-vm/index.html?search=#call-frames +/// +/// # Returns +/// +/// * [u64] - The code size of the current call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::call_frames::code_size; +/// +/// fn foo() { +/// let size = code_size(); +/// assert(size != 0); +/// } +/// ``` +pub fn code_size() -> u64 { + asm(size, ptr, offset: 576) { + add size fp offset; + size: u64 + } +} + +/// Get the first parameter from the current call frame. +/// +/// # Additional Information +/// +/// More information on data from call frames can be found in the Fuel Specs. +/// https://specs.fuel.network/master/fuel-vm/index.html?search=#call-frames +/// +/// # Returns +/// +/// * [u64] - The first parameter of the current call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::call_frames::first_param; +/// +/// fn foo() { +/// let param = first_param(); +/// assert(param != 0); +/// } +/// ``` +pub fn first_param() -> u64 { + frame_ptr().add::(FIRST_PARAMETER_OFFSET).read() +} + +/// Get the second parameter from the current call frame. +/// +/// # Additional Information +/// +/// More information on data from call frames can be found in the Fuel Specs. +/// https://specs.fuel.network/master/fuel-vm/index.html?search=#call-frames +/// +/// # Returns +/// +/// * [u64] - The second parameter of the current call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::call_frames::second_param; +/// +/// fn foo() { +/// let param: u64 = second_param(); +/// assert(param != 0); +/// } +/// ``` +pub fn second_param() -> T { + if !is_reference_type::() { + frame_ptr().add::(SECOND_PARAMETER_OFFSET).read::() + } else { + frame_ptr().add::(SECOND_PARAMETER_OFFSET).read::().read::() + } +} + +// Accessing arbitrary call frames by pointer +// +/// Get a pointer to the previous (relative to the `frame_pointer` parameter) call frame using offsets from a pointer. +/// +/// # Additional Information +/// +/// More information on data from call frames can be found in the Fuel Specs. +/// https://specs.fuel.network/master/fuel-vm/index.html?search=#call-frames +/// +/// # Arguments +/// +/// * `frame_pointer`: [raw_ptr] - The call frame reference directly before the returned call frame pointer. +/// +/// # Returns +/// +/// * [raw_ptr] - The memory location of the previous call frame data. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::get_previous_frame_pointer, registers::frame_ptr}; +/// +/// fn foo() { +/// let current_call_frame = frame_ptr(); +/// let previous_call_frame = get_previous_frame_pointer(current_call_frame); +/// assert(!previous_call_frame.is_null()); +/// } +/// ``` +pub fn get_previous_frame_pointer(frame_pointer: raw_ptr) -> raw_ptr { + let offset = frame_pointer.add::(SAVED_REGISTERS_OFFSET + PREV_FRAME_POINTER_OFFSET); + asm(res, ptr: offset) { + lw res ptr i0; + res: raw_ptr + } +} + +/// Get the value of `ContractId` from any call frame on the stack. +/// +/// # Arguments +/// +/// * `frame_pointer`: [raw_ptr] - The call frame for which the Contract Id is to be returned. +/// +/// # Returns +/// +/// * [ContractId] - The Contract Id of for the call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::get_contract_id_from_call_frame, registers::frame_ptr}; +/// +/// fn foo() { +/// let current_call_frame = frame_ptr(); +/// let contract_id = get_contract_id_from_call_frame(current_call_frame); +/// } +/// ``` +pub fn get_contract_id_from_call_frame(frame_pointer: raw_ptr) -> ContractId { + ContractId::from(asm(res, ptr: frame_pointer) { ptr: b256 }) +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/constants.sw b/sway-lsp/tests/fixtures/fixtures-std/src/constants.sw new file mode 100644 index 00000000000..2bbcc85047e --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/constants.sw @@ -0,0 +1,34 @@ +//! Base asset and zero address constants. +library; + +use ::contract_id::AssetId; + +/// The `BASE_ASSET_ID` represents the base asset of a chain. +/// +/// # Additional Information +/// +/// On the Fuel network, the base asset is Ether. It is hardcoded as the 0x00..00 ContractId. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::msg_asset_id, constants::BASE_ASSET_ID}; +/// +/// fn foo() { +/// assert(BASE_ASSET_ID == msg_asset_id()); +/// } +/// ``` +pub const BASE_ASSET_ID: AssetId = AssetId::from(ZERO_B256); + +/// A B256 of zero value. +/// +/// # Examples +/// +/// ```sway +/// use std::{call_frames::msg_asset_id, constants::ZERO_B256}; +/// +/// fn foo() { +/// assert(ZERO_B256 == msg_asset_id()); +/// } +/// ``` +pub const ZERO_B256 = 0x0000000000000000000000000000000000000000000000000000000000000000; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/context.sw b/sway-lsp/tests/fixtures/fixtures-std/src/context.sw new file mode 100644 index 00000000000..2b761eb295d --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/context.sw @@ -0,0 +1,77 @@ +//! Functionality for accessing context-specific information about the current contract or message. +library; + +use ::call_frames::contract_id; +use ::contract_id::{AssetId, ContractId}; +use ::registers::balance; + +/// Get the balance of coin `asset_id` for the current contract. +/// +/// # Arguments +/// +/// * `asset_id`: [AssetId] - The asset of which the balance should be returned. +/// +/// # Returns +/// +/// * [u64] - The amount of the asset which the contract holds. +/// +/// # Examples +/// +/// ```sway +/// use std::{context::this_balance, constants::ZERO_B256, hash::sha256, token::mint, call_frames::contract_id}; +/// +/// fn foo() { +/// mint(ZERO_B256, 50); +/// assert(this_balance(sha256((ZERO_B256, contract_id()))) == 50); +/// } +/// ``` +pub fn this_balance(asset_id: AssetId) -> u64 { + balance_of(contract_id(), asset_id) +} + +/// Get the balance of coin `asset_id` for the contract at 'target'. +/// +/// # Arguments +/// +/// * `target`: [ContractId] - The contract that contains the `asset_id`. +/// * `asset_id`: [AssetId] - The asset of which the balance should be returned. +/// +/// # Returns +/// +/// * [u64] - The amount of the asset which the `target` holds. +/// +/// # Examples +/// +/// ```sway +/// use std::{context::balance_of, constants::ZERO_B256, hash::sha256, token::mint, call_frames::contract_id}; +/// +/// fn foo() { +/// mint(ZERO_B256, 50); +/// assert(balance_of(contract_id(), sha256((ZERO_B256, contract_id()))) == 50); +/// } +/// ``` +pub fn balance_of(target: ContractId, asset_id: AssetId) -> u64 { + asm(balance, token: asset_id.value, id: target.value) { + bal balance token id; + balance: u64 + } +} + +/// Get the amount of units of `call_frames::msg_asset_id()` being sent. +/// +/// # Returns +/// +/// * [u64] - The amount of tokens being sent. +/// +/// # Examples +/// +/// ```sway +/// use std::context::msg_amount; +/// +/// fn foo() { +/// assert(msg_amount() == 0); +/// } +/// ``` +pub fn msg_amount() -> u64 { + balance() +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/contract_id.sw b/sway-lsp/tests/fixtures/fixtures-std/src/contract_id.sw new file mode 100644 index 00000000000..bdec134cbce --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/contract_id.sw @@ -0,0 +1,359 @@ +//! A wrapper around the `b256` type to help enhance type-safety. +library; + +use ::alias::SubId; +use ::convert::From; +use ::hash::*; + +/// The `ContractId` type, a struct wrapper around the inner `b256` value. +pub struct ContractId { + /// The underlying raw `b256` data of the contract id. + value: b256, +} + +impl core::ops::Eq for ContractId { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} + +/// Functions for casting between the `b256` and `ContractId` types. +impl From for ContractId { + /// Casts raw `b256` data to a `ContractId`. + /// + /// # Arguments + /// + /// * `bits`: [b256] - The raw `b256` data to be casted. + /// + /// # Returns + /// + /// * [ContractId] - The newly created `ContractId` from the raw `b256`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let contract_id = ContractId::from(ZERO_B256); + /// } + /// ``` + fn from(bits: b256) -> Self { + Self { value: bits } + } + + + /// Casts a `ContractId` to raw `b256` data. + /// + /// # Returns + /// + /// * [b256] - The underlying raw `b256` data of the `ContractId`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let contract_id = ContractId::from(ZERO_B256); + /// let b256_data = contract_id.into(); + /// assert(b256_data == ZERO_B256); + /// } + /// ``` + fn into(self) -> b256 { + self.value + } +} + +impl Hash for ContractId { + fn hash(self, ref mut state: Hasher) { + let Self { value } = self; + value.hash(state); + } +} + +/// An AssetId is used for interacting with an asset on the network. +/// +/// # Additional Information +/// +/// It is calculated by taking the sha256 hash of the originating ContractId and a SubId. +/// i.e. sha256((contract_id, sub_id)). +/// +/// An exception is the Base Asset, which is just the ZERO_B256 AssetId. +/// +/// The SubId is used to differentiate between different assets that are created by the same contract. +pub struct AssetId { + value: b256, +} + +impl Hash for AssetId { + fn hash(self, ref mut state: Hasher) { + let Self { value } = self; + value.hash(state); + } +} + +impl core::ops::Eq for AssetId { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} + +impl From for AssetId { + /// Casts raw `b256` data to an `AssetId`. + /// + /// # Arguments + /// + /// * `bits`: [b256] - The raw `b256` data to be casted. + /// + /// # Returns + /// + /// * [AssetId] - The newly created `AssetId` from the raw `b256`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let asset_id = AssetId::from(ZERO_B256); + /// } + /// ``` + fn from(bits: b256) -> Self { + Self { value: bits } + } + + /// Casts an `AssetId` to raw `b256` data. + /// + /// # Returns + /// + /// * [b256] - The underlying raw `b256` data of the `AssetId`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let asset_id = AssetId::from(ZERO_B256); + /// let b256_data = asset_id.into(); + /// assert(b256_data == ZERO_B256); + /// } + /// ``` + fn into(self) -> b256 { + self.value + } +} + +impl AssetId { + /// Creates a new AssetId from a ContractId and SubId. + /// + /// # Arguments + /// + /// * `contract_id`: [ContractId] - The ContractId of the contract that created the asset. + /// * `sub_id`: [SubId] - The SubId of the asset. + /// + /// # Returns + /// + /// * [AssetId] - The AssetId of the asset. Computed by hashing the ContractId and SubId. + /// + /// # Examples + /// + /// ```sway + /// use std::{callframes::contract_id, constants::ZERO_B256}; + /// + /// fn foo() { + /// let contract_id = contract_id(); + /// let sub_id = ZERO_B256; + /// + /// let asset_id = AssetId::new(contract_id, sub_id); + /// } + /// ``` + pub fn new(contract_id: ContractId, sub_id: SubId) -> Self { + let value = sha256((contract_id, sub_id)); + Self { value } + } + + /// Creates a new AssetId from a ContractId and the zero SubId. + /// + /// # Arguments + /// + /// * `contract_id`: [ContractId] - The ContractId of the contract that created the asset. + /// + /// # Returns + /// + /// * [AssetId] - The AssetId of the asset. Computed by hashing the ContractId and the zero SubId. + /// + /// # Examples + /// + /// ```sway + /// use std::{callframes::contract_id, constants::ZERO_B256}; + /// + /// fn foo() { + /// let contract_id = contract_id(); + /// let sub_id = ZERO_B256; + /// + /// let asset_id = AssetId::default(contract_id); + /// + /// assert(asset_id == AssetId::new(contract_id, sub_id)); + /// } + /// ``` + pub fn default(contract_id: ContractId) -> Self { + let value = sha256((contract_id, 0x0000000000000000000000000000000000000000000000000000000000000000)); + Self { value } + } + + /// The base_asset_id represents the base asset of a chain. + /// + /// # Additional Information + /// + /// On the Fuel network, the base asset is Ether. It is hardcoded as the 0x00..00 AssetId. + /// + /// # Returns + /// + /// * [AssetId] - The AssetId of the base asset. + /// + /// # Examples + /// + /// ```sway + /// use std::{constants::ZERO_B256, token::transfer}; + /// + /// fn foo() { + /// let asset_id = AssetId::base_asset_id(); + /// let amount = 100; + /// let recipient = Identity::ContractId(ContractId::from(ZERO_B256)); + /// + /// transfer(recipient, asset_id, amount); + /// ``` + pub fn base_asset_id() -> Self { + Self { + value: 0x0000000000000000000000000000000000000000000000000000000000000000, + } + } +} + +impl ContractId { + /// Returns the ContractId of the currently executing contract. + /// + /// # Additional Information + /// + /// This is equivalent to std::callframes::contract_id(). + /// + /// **_Note:_** If called in an external context, this will **not** return a ContractId. + /// If called externally, will actually return a pointer to the Transaction Id (Wrapped in the ContractId struct). + /// + /// # Returns + /// + /// * [ContractId] - The contract id of this contract. + /// + /// # Examples + /// + /// ```sway + /// use std::{constants::ZERO_B256, token::mint}; + /// + /// fn foo() { + /// let this_contract = ContractId::this(); + /// mint(ZERO_B256, 50); + /// Address::from(ZERO_B256).transfer(AssetId::default(this_contract), 50); + /// } + /// ``` + pub fn this() -> ContractId { + ContractId::from(asm() { fp: b256 }) + } + /// UNCONDITIONAL transfer of `amount` coins of type `asset_id` to + /// the ContractId. + /// + /// # Additional Informations + /// + /// **_WARNING:_** + /// This will transfer coins to a contract even with no way to retrieve them + /// (i.e. no withdrawal functionality on receiving contract), possibly leading + /// to the **_PERMANENT LOSS OF COINS_** if not used with care. + /// + /// # Arguments + /// + /// * `asset_id`: [AssetId] - The `AssetId` of the token to transfer. + /// * `amount`: [u64] - The amount of tokens to transfer. + /// + /// # Reverts + /// + /// * When `amount` is greater than the contract balance for `asset_id`. + /// * When `amount` is equal to zero. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::{BASE_ASSET_ID, ZERO_B256}; + /// + /// fn foo() { + /// let contract_id = ContractId::from(ZERO_B256); + /// contract_id.transfer(BASE_ASSET_ID, 500); + /// } + /// ``` + pub fn transfer(self, asset_id: AssetId, amount: u64) { + asm(r1: amount, r2: asset_id.value, r3: self.value) { + tr r3 r1 r2; + } + } +} + +impl ContractId { + /// Mint `amount` coins of `sub_id` and send them UNCONDITIONALLY to the contract at `to`. + /// + /// # Additional Information + /// + /// **_WARNING:_** + /// This will transfer coins to a contract even with no way to retrieve them + /// (i.e: no withdrawal functionality on the receiving contract), possibly leading to + /// the **_PERMANENT LOSS OF COINS_** if not used with care. + /// + /// # Arguments + /// + /// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. + /// * `amount`: [u64] - The amount of tokens to mint. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let contract_id = ContractId::from(ZERO_B256); + /// contract_id.mint_to(ZERO_B256, 500); + /// } + /// ``` + pub fn mint_to(self, sub_id: SubId, amount: u64) { + asm(r1: amount, r2: sub_id) { + mint r1 r2; + }; + self.transfer(AssetId::new(ContractId::from(asm() { fp: b256 }), sub_id), amount); + } +} + +#[test()] +fn test_hasher_sha256_asset_id() { + use ::assert::assert; + let mut hasher = Hasher::new(); + AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000).hash(hasher); + let s256 = hasher.sha256(); + assert(s256 == 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925); + + let mut hasher = Hasher::new(); + AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000001).hash(hasher); + let s256 = hasher.sha256(); + assert(s256 == 0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5); +} + +#[test()] +fn test_hasher_sha256_contract_id() { + use ::assert::assert; + let mut hasher = Hasher::new(); + ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000).hash(hasher); + let s256 = hasher.sha256(); + assert(s256 == 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925); + + let mut hasher = Hasher::new(); + ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001).hash(hasher); + let s256 = hasher.sha256(); + assert(s256 == 0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/convert.sw b/sway-lsp/tests/fixtures/fixtures-std/src/convert.sw new file mode 100644 index 00000000000..aae78310e01 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/convert.sw @@ -0,0 +1,20 @@ +//! Traits for conversions between types. +library; + +use ::option::Option; + +/// Used to do value-to-value conversions. +pub trait From { + /// Converts to this type from the input type. + fn from(b: T) -> Self; + /// Converts this type into the (usually inferred) input type. + fn into(self) -> T; +} + +// TODO: return a Result when https://github.com/FuelLabs/sway/issues/610 is resolved +/// Used to attempt to do value-to-value conversions. +/// Returns None if the conversion can't be performed in a lossless manner. +pub trait TryFrom { + /// Performs the conversion. Returns None if the conversion can't be performed in a lossless manner. + fn try_from(b: T) -> Option; +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/ecr.sw b/sway-lsp/tests/fixtures/fixtures-std/src/ecr.sw new file mode 100644 index 00000000000..da9d2c52155 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/ecr.sw @@ -0,0 +1,298 @@ +//! Helper functions to verify signatures. +library; + +use ::address::Address; +use ::b512::B512; +use ::registers::error; +use ::hash::*; +use ::result::Result::{self, *}; + +/// The error type used when the `ec_recover` function fails. +pub enum EcRecoverError { + /// The error varient used when the recover fails. + UnrecoverablePublicKey: (), +} + +/// Recover the public key derived from the private key used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Additional Informaiton +/// +/// Follows the Secp256k1 elliptical curve. +/// +/// # Arguments +/// +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The signed data. +/// +/// # Returns +/// +/// * [Result] - The recovered public key or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{ecr::ec_recover, b512::B512}; +/// +/// fn foo() { +/// let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c; +/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; +/// let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// let pub_hi = 0xD73A188181464CC84AE267E45041AEF6AB938F278E636AA1D02D3014C1BEF74E; +/// let pub_lo = 0xC44415635160ACFC87A84300EED97928C949A2D958FC0947C535F7539C59AE75; +/// let signature: B512 = B512::from((hi, lo)); +/// // A recovered public key pair. +/// let public_key = ec_recover(signature, msg_hash).unwrap(); +/// +/// assert(public_key.bytes[0] == pub_hi); +/// assert(public_key.bytes[1] == pub_lo); +/// } +/// ``` +pub fn ec_recover(signature: B512, msg_hash: b256) -> Result { + let public_key = B512::new(); + let was_error = asm(buffer: public_key.bytes, sig: signature.bytes, hash: msg_hash) { + eck1 buffer sig hash; + err + }; + // check the $err register to see if the `eck1` opcode succeeded + if was_error == 1 { + Err(EcRecoverError::UnrecoverablePublicKey) + } else { + Ok(public_key) + } +} + +/// Recover the public key derived from the private key used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Additional Informaiton +/// +/// Follows the Secp256r1 elliptical curve. +/// +/// # Arguments +/// +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The signed data. +/// +/// # Returns +/// +/// * [Result] - The recovered public key or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{ecr::ec_recover_r1, b512::B512}; +/// +/// fn foo() { +/// let hi = 0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac; +/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d; +/// let msg_hash = 0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323; +/// let pub_hi = 0xD73A188181464CC84AE267E45041AEF6AB938F278E636AA1D02D3014C1BEF74E; +/// let pub_lo = 0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452; +/// let signature: B512 = B512::from((hi, lo)); +/// // A recovered public key pair. +/// let public_key = ec_recover_r1(signature, msg_hash).unwrap(); +/// +/// assert(public_key.bytes[0] == pub_hi); +/// assert(public_key.bytes[1] == pub_lo); +/// } +/// ``` +pub fn ec_recover_r1(signature: B512, msg_hash: b256) -> Result { + let public_key = B512::new(); + let was_error = asm(buffer: public_key.bytes, sig: signature.bytes, hash: msg_hash) { + ecr1 buffer sig hash; + err + }; + // check the $err register to see if the `ecr1` opcode succeeded + if was_error == 1 { + Err(EcRecoverError::UnrecoverablePublicKey) + } else { + Ok(public_key) + } +} + +/// Verifies that a public key derived from the private key was used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Additional Informaiton +/// +/// Follows the edDSA curve25519 verification. +/// +/// # Arguments +/// +/// * `public_key`: [b256] - The public key that signed the message. +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The hashed signed data. +/// +/// # Returns +/// +/// * [Result] - A verfied result or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{ecr::ed_verify, b512::B512, constants::ZERO_B256}; +/// +/// fn foo() { +/// let pub_key = 0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10; +/// let msg = ZERO_B256; +/// let msg_hash = sha256(msg); + +/// let hi = 0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545; +/// let lo = 0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00; +/// let signature: B512 = B512::from((hi, lo)); +/// // A verify public key with signature +/// let verfied = ed_verify(pub_key, signature, msg_hash).unwrap(); +/// assert(verfied); +/// } +/// ``` +pub fn ed_verify(public_key: b256, signature: B512, msg_hash: b256) -> Result { + let was_error = asm(buffer: public_key, sig: signature.bytes, hash: msg_hash) { + ed19 buffer sig hash; + err + }; + // check the $err register to see if the `ed19` opcode succeeded + if was_error == 1 { + Err(EcRecoverError::UnrecoverablePublicKey) + } else { + Ok(true) + } +} + +/// Recover the address derived from the private key used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Additional Informaiton +/// +/// Follows the Secp256k1 elliptical curve. +/// +/// # Arguments +/// +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The signed data. +/// +/// # Returns +/// +/// * [Result] - The recovered Fuel address or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{ecr::ec_recover_address, b512::B512}; +/// +/// fn foo() { +/// let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c; +/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; +/// let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// let address = Address::from(0x7AAE2D980BE4C3275C72CE5B527FA23FFB97B766966559DD062E2B78FD9D3766); +/// let signature: B512 = B512::from((hi, lo)); +/// // A recovered Fuel address. +/// let result_address = ec_recover_address(signature, msg_hash).unwrap(); +/// assert(result_address == address); +/// } +/// ``` +pub fn ec_recover_address(signature: B512, msg_hash: b256) -> Result { + let pub_key_result = ec_recover(signature, msg_hash); + + if let Err(e) = pub_key_result { + // propagate the error if it exists + Err(e) + } else { + let pub_key = pub_key_result.unwrap(); + let address = sha256(((pub_key.bytes)[0], (pub_key.bytes)[1])); + Ok(Address::from(address)) + } +} + +/// Recover the address derived from the private key used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Additional Informaiton +/// +/// Follows the Secp256r1 elliptical curve. +/// +/// # Arguments +/// +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The signed data. +/// +/// # Returns +/// +/// * [Result] - The recovered Fuel address or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{ecr::ec_recover_address_r1, b512::B512}; +/// +/// fn foo() { +/// let hi = 0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c; +/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; +/// let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// let address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a); +/// let signature: B512 = B512::from((hi, lo)); +/// // A recovered Fuel address. +/// let result_address = ec_recover_address_r1(signature, msg_hash).unwrap(); +/// assert(result_address == address); +/// } +/// ``` +pub fn ec_recover_address_r1(signature: B512, msg_hash: b256) -> Result { + let pub_key_result = ec_recover_r1(signature, msg_hash); + + if let Err(e) = pub_key_result { + // propagate the error if it exists + Err(e) + } else { + let pub_key = pub_key_result.unwrap(); + let address = sha256(((pub_key.bytes)[0], (pub_key.bytes)[1])); + Ok(Address::from(address)) + } +} + +#[test] +fn test_ec_recover_r1() { + use ::assert::assert; + + let hi = 0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac; + let lo = 0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d; + let msg_hash = 0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323; + let pub_hi = 0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2; + let pub_lo = 0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452; + let signature: B512 = B512::from((hi, lo)); + // A recovered public key pair. + let public_key = ec_recover_r1(signature, msg_hash).unwrap(); + + assert(public_key.bytes[0] == pub_hi); + assert(public_key.bytes[1] == pub_lo); +} + +#[test] +fn test_ec_recover_address_r1() { + use ::assert::assert; + + let hi = 0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c; + let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; + let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; + let address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a); + let signature: B512 = B512::from((hi, lo)); + // A recovered Fuel address. + let result_address = ec_recover_address_r1(signature, msg_hash).unwrap(); + assert(result_address == address); +} + +#[test] +fn test_ed_verify() { + use ::assert::assert; + use ::constants::ZERO_B256; + + let pub_key = 0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10; + let msg = ZERO_B256; + let msg_hash = sha256(msg); + + let hi = 0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545; + let lo = 0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00; + let signature: B512 = B512::from((hi, lo)); + // A verify public key with signature + let verfied = ed_verify(pub_key, signature, msg_hash).unwrap(); + assert(verfied); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/error_signals.sw b/sway-lsp/tests/fixtures/fixtures-std/src/error_signals.sw new file mode 100644 index 00000000000..ae6dedefd7f --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/error_signals.sw @@ -0,0 +1,30 @@ +//! Values which signify special types of errors when passed to `std::revert::revert`. +library; + +/// A revert with this value signals that it was caused by a failing call to `std::revert::require`. +/// +/// # Additional Information +/// +/// The value is: 18446744073709486080 +pub const FAILED_REQUIRE_SIGNAL = 0xffff_ffff_ffff_0000; + +/// A revert with this value signals that it was caused by a failing call to `std::token::transfer_to_address`. +/// +/// # Additional Information +/// +/// The value is: 18446744073709486081 +pub const FAILED_TRANSFER_TO_ADDRESS_SIGNAL = 0xffff_ffff_ffff_0001; + +/// A revert with this value signals that it was caused by a failing call to `std::assert::assert_eq`. +/// +/// # Additional Information +/// +/// The value is: 18446744073709486083 +pub const FAILED_ASSERT_EQ_SIGNAL = 0xffff_ffff_ffff_0003; + +/// A revert with this value signals that it was caused by a failing call to `std::assert::assert`. +/// +/// # Additional Information +/// +/// The value is: 18446744073709486084 +pub const FAILED_ASSERT_SIGNAL = 0xffff_ffff_ffff_0004; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/external.sw b/sway-lsp/tests/fixtures/fixtures-std/src/external.sw new file mode 100644 index 00000000000..21caeb21a22 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/external.sw @@ -0,0 +1,34 @@ +//! Functions to work with external contracts. +library; + +use ::constants::ZERO_B256; +use ::contract_id::ContractId; + +/// Get the root of the bytecode of the contract at 'contract_id'. +/// +/// # Arguments +/// +/// * `contract_id`: [ContractId] - The contract of which the the bytecode should be returned. +/// +/// # Returns +/// +/// * [b256] - The bytecode root of the contract. +/// +/// # Examples +/// +/// ```sway +/// use std::{external::bytecode_root, call_frames::contract_id, constants::ZERO_B256}; +/// +/// fn foo() { +/// let root_of_this_contract = bytecode_root(contract_id()); +/// assert(root_of_this_contract != ZERO_B256); +/// } +/// ``` +pub fn bytecode_root(contract_id: ContractId) -> b256 { + let root: b256 = ZERO_B256; + + asm(root_addr: root, target: contract_id.value) { + croo root_addr target; + root_addr: b256 + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/flags.sw b/sway-lsp/tests/fixtures/fixtures-std/src/flags.sw new file mode 100644 index 00000000000..46fd12fd631 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/flags.sw @@ -0,0 +1,267 @@ +//! Functionality for setting and unsetting FuelVM flags to modify behavior related to the `$err` and `$of` registers. +library; + +use ::{assert::assert, logging::log, registers::{flags, error}}; + +// Mask second bit, which is `F_WRAPPING`. +const F_WRAPPING_DISABLE_MASK: u64 = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000010; +// Mask second bit, which is `F_WRAPPING`. +const F_WRAPPING_ENABLE_MASK: u64 = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111101; +// Mask first bit, which is `F_UNSAFEMATH`. +const F_UNSAFEMATH_DISABLE_MASK: u64 = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001; +// Mask first bit, which is `F_UNSAFEMATH`. +const F_UNSAFEMATH_ENABLE_MASK: u64 = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111110; + +/// Sets the flag register to the given value. +/// +/// # Arguments +/// +/// * `new_flags`: [u64] - Binary encoded 64 bit value representing the flags to set. +/// +/// # Examples +/// +/// ```sway +/// use std::flags::{disable_panic_on_overflow_preserving, set_flags}; +/// +/// fn foo() { +/// let prior_flags = disable_panic_on_overflow_preserving(); +/// +/// // Adding 1 to the max value of a u64 is considered an overflow. +/// let bar = u64::max() + 1; +/// +/// set_flags(prior_flags); +/// } +/// ``` +pub fn set_flags(new_flags: u64) { + asm(new_flags: new_flags) { + flag new_flags; + } +} + +/// Allows overflowing operations to occur without a FuelVM panic. +/// +/// # Additional Information +/// +/// > **_WARNING:_** +/// > +/// > Don't forget to call `enable_panic_on_overflow` or `set_flags` after performing the operations for which you disabled the default `panic-on-overflow` behavior in the first place! +/// +/// # Returns +/// +/// * [u64] - The flag prior to disabling panic on overflow. +/// +/// # Examples +/// +/// ```sway +/// use std::flags::{disable_panic_on_overflow, enable_panic_on_overflow}; +/// +/// fn main() { +/// disable_panic_on_overflow(); +/// +/// // Adding 1 to the max value of a u64 is considered an overflow. +/// let bar = u64::max() + 1; +/// +/// enable_panic_on_overflow(); +/// } +/// ``` +/// +/// ```sway +/// use std::flags::{disable_panic_on_overflow, set_flags}; +/// +/// fn foo() { +/// let prior_flags = disable_panic_on_overflow(); +/// +/// // Adding 1 to the max value of a u64 is considered an overflow. +/// let bar = u64::max() + 1; +/// +/// set_flags(prior_flags); +/// } +/// ``` +pub fn disable_panic_on_overflow() -> u64 { + let prior_flags = flags(); + + // Get the current value of the flags register and mask it, setting the + // masked bit. Flags are inverted, so set = off. + let flag_val = prior_flags | F_WRAPPING_DISABLE_MASK; + asm(flag_val: flag_val) { + flag flag_val; + } + + prior_flags +} + +/// Enables the default `panic-on-overflow` behavior in the FuelVM. +/// +/// # Additional Information +/// +/// > **_Note:_** +/// > +/// > `panic-on-overflow` is the default, so there is no need to use this function unless you have previously called `disable_panic_on_overflow`. +/// +/// # Examples +/// +/// ```sway +/// use std::flags::{disable_panic_on_overflow, enable_panic_on_overflow}; +/// +/// fn main() { +/// disable_panic_on_overflow(); +/// +/// // Adding 1 to the max value of a u64 is considered an overflow. +/// let bar = u64::max() + 1; +/// +/// enable_panic_on_overflow(); +/// } +/// ``` +pub fn enable_panic_on_overflow() { + // Get the current value of the flags register and mask it, unsetting the + // masked bit. Flags are inverted, so unset = on. + let flag_val = flags() & F_WRAPPING_ENABLE_MASK; + asm(flag_val: flag_val) { + flag flag_val; + } +} + +/// Allows unsafe math operations to occur without a FuelVM panic. +/// Sets the `$err` register to `true` whenever unsafe math occurs. +/// +/// # Additional Information +/// +/// > **_WARNING:_** +/// > +/// > Don't forget to call `enable_panic_on_unsafe_math` or `set_flags` after performing the operations for which you disabled the default `panic-on-unsafe-math` behavior in the first place! +/// +/// # Returns +/// +/// * [u64] - The flag prior to disabling panic on overflow. +/// +/// # Examples +/// +/// ```sway +/// use std::{assert::assert, flags::{disable_panic_on_unsafe_math, enable_panic_on_unsafe_math}, registers::error}; +/// +/// fn main() { +/// disable_panic_on_unsafe_math(); +/// +/// // Division by zero is considered unsafe math. +/// let bar = 1 / 0; +/// // Error flag is set to true whenever unsafe math occurs. Here represented as 1. +/// assert(error() == 1); +/// +/// enable_panic_on_unsafe_math(); +/// } +/// ``` +/// +/// ```sway +/// use std::{assert::assert, flags::{disable_panic_on_unsafe_math, set_flags}, registers::error}; +/// +/// fn foo() { +/// let prior_flags = disable_panic_on_unsafe_math(); +/// +/// // Division by zero is considered unsafe math. +/// let bar = 1 / 0; +/// // Error flag is set to true whenever unsafe math occurs. Here represented as 1. +/// assert(error() == 1); +/// +/// set_flags(prior_flags); +/// } +/// ``` +pub fn disable_panic_on_unsafe_math() -> u64 { + let prior_flags = flags(); + + // Get the current value of the flags register and mask it, setting the + // masked bit. Flags are inverted, so set = off. + let flag_val = prior_flags | F_UNSAFEMATH_DISABLE_MASK; + asm(flag_val: flag_val) { + flag flag_val; + } + + prior_flags +} + +/// Enables the default `panic-on-unsafe-math` behavior in the FuelVM. +/// +/// # Additional Information +/// +/// > **_Note:_** +/// > +/// > `panic-on-unsafe-math` is the default, so there is no need to use this function unless you have previously called `disable_panic_on_unsafe_math`. +/// +/// # Examples +/// +/// ```sway +/// use std::{assert::assert, flags::{disable_panic_on_unsafe_math, enable_panic_on_unsafe_math}, registers::error}; +/// +/// fn main() { +/// disable_panic_on_unsafe_math(); +/// +/// // Division by zero is considered unsafe math. +/// let bar = 1 / 0; +/// // Error flag is set to true whenever unsafe math occurs. Here represented as 1. +/// assert(error() == 1); +/// +/// enable_panic_on_unsafe_math(); +/// } +/// ``` +pub fn enable_panic_on_unsafe_math() { + // Get the current value of the flags register and mask it, unsetting the + // masked bit. Flags are inverted, so unset = on. + let flag_val = flags() & F_UNSAFEMATH_ENABLE_MASK; + asm(flag_val: flag_val) { + flag flag_val; + } +} + +#[test] +fn test_disable_panic_on_overflow() { + let _ = disable_panic_on_overflow(); + let _bar = u64::max() + 1; + enable_panic_on_overflow(); +} + +#[test] +fn test_disable_panic_on_overflow_preserving() { + let _ = disable_panic_on_overflow(); + + let prior_flags = disable_panic_on_overflow(); + let _bar = u64::max() + 1; + set_flags(prior_flags); + + let _bar = u64::max() + 1; + + enable_panic_on_overflow(); +} + +#[test] +fn test_disable_panic_on_unsafe_math() { + let _ = disable_panic_on_unsafe_math(); + + let _bar = asm(r2: 1, r3: 0, r1) { + div r1 r2 r3; + r1: u64 + }; + + assert(error() == 1); + + enable_panic_on_unsafe_math(); +} + +#[test] +fn test_disable_panic_on_unsafe_math_preserving() { + let _ = disable_panic_on_unsafe_math(); + + let prior_flags = disable_panic_on_unsafe_math(); + let _bar = asm(r2: 1, r3: 0, r1) { + div r1 r2 r3; + r1: u64 + }; + assert(error() == 1); + set_flags(prior_flags); + + let _bar = asm(r2: 1, r3: 0, r1) { + div r1 r2 r3; + r1: u64 + }; + assert(error() == 1); + + enable_panic_on_unsafe_math(); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/hash.sw b/sway-lsp/tests/fixtures/fixtures-std/src/hash.sw new file mode 100644 index 00000000000..2e09a41187c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/hash.sw @@ -0,0 +1,618 @@ +//! Utility functions for cryptographic hashing. +library; + +use ::bytes::*; + +pub struct Hasher { + bytes: Bytes +} + +impl Hasher { + pub fn new() -> Self { + Self { bytes: Bytes::new() } + } + + /// Writes some data into this `Hasher`. + pub fn write(ref mut self, bytes: Bytes) { + self.bytes.append(bytes); + } + + pub fn sha256(self) -> b256 { + let mut result_buffer = b256::min(); + asm(hash: result_buffer, ptr: self.bytes.buf.ptr, bytes: self.bytes.len) { + s256 hash ptr bytes; + hash: b256 + } + } + + pub fn keccak256(self) -> b256 { + let mut result_buffer = b256::min(); + asm(hash: result_buffer, ptr: self.bytes.buf.ptr, bytes: self.bytes.len) { + k256 hash ptr bytes; + hash: b256 + } + } +} + +impl Hasher { + /// Writes a single `str` into this hasher. + pub fn write_str(ref mut self, s: str) { + let str_size = s.len(); + let str_ptr = s.as_ptr(); + + let mut bytes = Bytes::with_capacity(str_size); + bytes.len = str_size; + + str_ptr.copy_bytes_to(bytes.buf.ptr(), str_size); + self.write(bytes); + } + + #![inline(never)] + pub fn write_str_array(ref mut self, s: S) { + __assert_is_str_array::(); + let str_size = __size_of_str_array::(); + let str_ptr = __addr_of(s); + + let mut bytes = Bytes::with_capacity(str_size); + bytes.len = str_size; + + str_ptr.copy_bytes_to(bytes.buf.ptr(), str_size); + + self.write(bytes); + } + +} + +pub trait Hash { + fn hash(self, ref mut state: Hasher); +} + +impl Hash for u8 { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(1); + bytes.push(self); + state.write(bytes); + } +} + +impl Hash for u16 { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(8); // one word capacity + bytes.len = 2; + + asm(ptr: bytes.buf.ptr(), val: self, r1) { + slli r1 val i48; + sw ptr r1 i0; + }; + + state.write(bytes); + } +} + +impl Hash for u32 { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(8); // one word capacity + bytes.len = 4; + + asm(ptr: bytes.buf.ptr(), val: self, r1) { + slli r1 val i32; + sw ptr r1 i0; + }; + + state.write(bytes); + } +} + +impl Hash for u64 { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(8); // one word capacity + bytes.len = 8; + + asm(ptr: bytes.buf.ptr(), val: self) { + sw ptr val i0; + }; + + state.write(bytes); + } +} + +impl Hash for b256 { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(32); // four word capacity + bytes.len = 32; + + let (word_1, word_2, word_3, word_4) = asm(r1: self) { r1: (u64, u64, u64, u64) }; + + asm(ptr: bytes.buf.ptr(), val_1: word_1, val_2: word_2, val_3: word_3, val_4: word_4) { + sw ptr val_1 i0; + sw ptr val_2 i1; + sw ptr val_3 i2; + sw ptr val_4 i3; + }; + + state.write(bytes); + } +} + +impl Hash for bool { + fn hash(self, ref mut state: Hasher) { + let mut bytes = Bytes::with_capacity(1); + if self { + bytes.push(1_u8); + } else { + bytes.push(0_u8); + } + state.write(bytes); + } +} + +impl Hash for Bytes { + fn hash(self, ref mut state: Hasher) { + state.write(self); + } +} + +impl Hash for str { + fn hash(self, ref mut state: Hasher) { + state.write_str(self); + } +} + +impl Hash for (A, B) where A: Hash, B: Hash { + #![inline(never)] + fn hash(self, ref mut state: Hasher) { + self.0.hash(state); + self.1.hash(state); + } +} + +impl Hash for (A, B, C) where A: Hash, B: Hash, C: Hash { + fn hash(self, ref mut state: Hasher) { + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + } +} + +impl Hash for (A, B, C, D) where A: Hash, B: Hash, C: Hash, D: Hash { + fn hash(self, ref mut state: Hasher) { + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + } +} + +impl Hash for (A, B, C, D, E) where A: Hash, B: Hash, C: Hash, D: Hash, E: Hash { + fn hash(self, ref mut state: Hasher) { + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + self.4.hash(state); + } +} + +impl Hash for [T; 1] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + } +} + +impl Hash for [T; 2] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + } +} + +impl Hash for [T; 3] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + } +} + +impl Hash for [T; 4] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + } +} + +impl Hash for [T; 5] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + } +} + +impl Hash for [T; 6] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + self[5].hash(state); + } +} + +impl Hash for [T; 7] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + self[5].hash(state); + self[6].hash(state); + } +} + +impl Hash for [T; 8] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + self[5].hash(state); + self[6].hash(state); + self[7].hash(state); + } +} + +impl Hash for [T; 9] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + self[5].hash(state); + self[6].hash(state); + self[7].hash(state); + self[8].hash(state); + } +} + +impl Hash for [T; 10] where T: Hash { + fn hash(self, ref mut state: Hasher) { + self[0].hash(state); + self[1].hash(state); + self[2].hash(state); + self[3].hash(state); + self[4].hash(state); + self[5].hash(state); + self[6].hash(state); + self[7].hash(state); + self[8].hash(state); + self[9].hash(state); + } +} + +/// Returns the `SHA-2-256` hash of `param`. +/// +/// # Arguments +/// +/// * `s`: [T] - The value to be hashed. +/// +/// # Returns +/// +/// * [b256] - The sha-256 hash of the value. +/// +/// # Examples +/// +/// ```sway +/// use std::hash::*; +/// +/// fn foo() { +/// let result = sha256("Fuel"); +/// assert(result = 0xa80f942f4112036dfc2da86daf6d2ef6ede3164dd56d1000eb82fa87c992450f); +/// } +/// ``` +#![inline(never)] +pub fn sha256(s: T) -> b256 where T: Hash { + let mut hasher = Hasher::new(); + s.hash(hasher); + hasher.sha256() +} + +/// Returns the `SHA-2-256` hash of `param`. +/// This function is specific for string arrays +/// +/// # Examples +/// +/// ```sway +/// use std::hash::*; +/// +/// fn foo() { +/// let result = sha256_str_array(__to_str_array("Fuel")); +/// assert(result = 0xa80f942f4112036dfc2da86daf6d2ef6ede3164dd56d1000eb82fa87c992450f); +/// } +/// ``` +#![inline(never)] +pub fn sha256_str_array(param: S) -> b256 { + __assert_is_str_array::(); + let str_size = __size_of_str_array::(); + let str_ptr = __addr_of(param); + + let mut bytes = Bytes::with_capacity(str_size); + bytes.len = str_size; + + str_ptr.copy_bytes_to(bytes.buf.ptr(), str_size); + + let mut hasher = Hasher::new(); + hasher.write(bytes); + hasher.sha256() +} + +/// Returns the `KECCAK-256` hash of `param`. +/// +/// # Arguments +/// +/// * `s`: [T] - The value to be hashed. +/// +/// # Returns +/// +/// * [b256] - The keccak-256 hash of the value. +/// +/// # Examples +/// +/// ```sway +/// use std::hash::keccak256; +/// +/// fn foo() { +/// let result = keccak256("Fuel"); +/// assert(result = 0x4375c8bcdc904e5f51752581202ae9ae2bb6eddf8de05d5567d9a6b0ae4789ad); +/// } +/// ``` +#![inline(never)] +pub fn keccak256(s: T) -> b256 where T: Hash { + let mut hasher = Hasher::new(); + s.hash(hasher); + hasher.keccak256() +} + +// Tests + +#[test()] +fn test_hasher_sha256_str_array() { + use ::assert::assert; + let mut hasher = Hasher::new(); + hasher.write_str("test"); + let sha256 = hasher.sha256(); + assert(sha256 == 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08); + + let mut hasher = Hasher::new(); + hasher.write_str("Fastest Modular Execution Layer!"); + let sha256 = hasher.sha256(); + assert(sha256 == 0x4a3cd7c8b44dbf7941e55179425f746adeaa97fe2d99b571fffee78e9b41743c); +} + +// The hashes for the following test can be obtained in Rust by running the following script: +// https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a2d83e9ea48b35a3e991c904c3451ed5 +#[test()] +fn test_hasher_sha256_u8() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u8.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d); + + let mut hasher = Hasher::new(); + 1_u8.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a); +} + +#[test()] +fn test_hasher_sha256_u16() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u16.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7); + + let mut hasher = Hasher::new(); + 1_u16.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xb413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2); +} + +#[test()] +fn test_hasher_sha256_u32() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u32.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xdf3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119); + + let mut hasher = Hasher::new(); + 1_u32.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xb40711a88c7039756fb8a73827eabe2c0fe5a0346ca7e0a104adc0fc764f528d); +} + +#[test()] +fn test_hasher_sha256_u64() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u64.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xaf5570f5a1810b7af78caf4bc70a660f0df51e42baf91d4de5b2328de0e83dfc); + + let mut hasher = Hasher::new(); + 1_u64.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xcd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50); +} + +#[test()] +fn test_hasher_sha256_b256() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0x0000000000000000000000000000000000000000000000000000000000000000.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925); + + let mut hasher = Hasher::new(); + 0x0000000000000000000000000000000000000000000000000000000000000001.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5); +} + +#[test()] +fn test_hasher_sha256_bool() { + use ::assert::assert; + let mut hasher = Hasher::new(); + false.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d); + + let mut hasher = Hasher::new(); + true.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a); +} + +#[test] +fn test_hasher_sha256_bytes() { + use ::assert::assert; + let mut hasher = Hasher::new(); + let mut bytes = Bytes::new(); + bytes.push(0u8); + bytes.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d); + + let mut hasher = Hasher::new(); + let mut bytes = Bytes::new(); + bytes.push(1u8); + bytes.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a); +} + +#[test()] +fn test_hasher_keccak256_str_array() { + use ::assert::assert; + let mut hasher = Hasher::new(); + hasher.write_str("test"); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658); + + let mut hasher = Hasher::new(); + hasher.write_str("Fastest Modular Execution Layer!"); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xab8e83e041e001bcf797c9cc7d6bc472bfdb8c736bab7999f13b7c26f48c354f); +} + +#[test()] +fn test_hasher_keccak256_u8() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u8.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a); + + let mut hasher = Hasher::new(); + 1_u8.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2); +} + +#[test()] +fn test_hasher_keccak256_u16() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u16.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x54a8c0ab653c15bfb48b47fd011ba2b9617af01cb45cab344acd57c924d56798); + + let mut hasher = Hasher::new(); + 1_u16.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x49d03a195e239b52779866b33024210fc7dc66e9c2998975c0aa45c1702549d5); +} + +#[test()] +fn test_hasher_keccak256_u32() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u32.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c); + + let mut hasher = Hasher::new(); + 1_u32.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f); +} + +#[test()] +fn test_hasher_keccak256_u64() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0_u64.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce); + + let mut hasher = Hasher::new(); + 1_u64.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x6c31fc15422ebad28aaf9089c306702f67540b53c7eea8b7d2941044b027100f); +} + +#[test()] +fn test_hasher_keccak256_b256() { + use ::assert::assert; + let mut hasher = Hasher::new(); + 0x0000000000000000000000000000000000000000000000000000000000000000.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563); + + let mut hasher = Hasher::new(); + 0x0000000000000000000000000000000000000000000000000000000000000001.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6); +} + +#[test()] +fn test_hasher_keccak256_bool() { + use ::assert::assert; + let mut hasher = Hasher::new(); + false.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a); + + let mut hasher = Hasher::new(); + true.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2); +} + +#[test] +fn test_hasher_keccak256_bytes() { + use ::assert::assert; + let mut hasher = Hasher::new(); + let mut bytes = Bytes::with_capacity(1); + bytes.push(0u8); + bytes.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a); + + let mut hasher = Hasher::new(); + let mut bytes = Bytes::with_capacity(1); + bytes.push(1u8); + bytes.hash(hasher); + let keccak256 = hasher.keccak256(); + assert(keccak256 == 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/identity.sw b/sway-lsp/tests/fixtures/fixtures-std/src/identity.sw new file mode 100644 index 00000000000..1fdd346d6ec --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/identity.sw @@ -0,0 +1,236 @@ +//! A wrapper type with two variants, `Address` and `ContractId`. +//! The use of this type allows for handling interactions with contracts and addresses in a unified manner. +library; + +use ::assert::assert; +use ::address::Address; +use ::alias::SubId; +use ::call_frames::contract_id; +use ::constants::{ZERO_B256, BASE_ASSET_ID}; +use ::contract_id::{AssetId, ContractId}; +use ::hash::*; +use ::option::Option::{self, *}; + +/// The `Identity` type: either an `Address` or a `ContractId`. +// ANCHOR: docs_identity +pub enum Identity { + Address: Address, + ContractId: ContractId, +} +// ANCHOR_END: docs_identity + +impl core::ops::Eq for Identity { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Identity::Address(addr1), Identity::Address(addr2)) => addr1 == addr2, + (Identity::ContractId(id1), Identity::ContractId(id2)) => id1 == id2, + _ => false, + } + } +} + +impl Identity { + /// Returns the `Address` of the `Identity`. + /// + /// # Returns + /// + /// * [Option
] - `Some(Address)` if the underlying type is an `Address`, otherwise `None`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let identity = Identity::Address(Address::from(ZERO_B256)); + /// let address = identity.as_address(); + /// assert(address == Address::from(ZERO_B256)); + /// } + /// ``` + pub fn as_address(self) -> Option
{ + match self { + Self::Address(addr) => Some(addr), + Self::ContractId(_) => None, + } + } + + /// Returns the `ContractId` of the `Identity`. + /// + /// # Returns + /// + /// * [Option] - `Some(Contract)` if the underlying type is an `ContractId`, otherwise `None`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let identity = Identity::ContractId(ContractId::from(ZERO_B256)); + /// let contract_id = identity.as_contract_id(); + /// assert(contract_id == ContractId::from(ZERO_B256)); + /// } + /// ``` + pub fn as_contract_id(self) -> Option { + match self { + Self::Address(_) => None, + Self::ContractId(id) => Some(id), + } + } + + /// Returns whether the `Identity` represents an `Address`. + /// + /// # Returns + /// + /// * [bool] - Indicates whether the `Identity` holds an `Address`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let identity = Identity::Address(Address::from(ZERO_B256)); + /// assert(identity.is_address()); + /// } + /// ``` + pub fn is_address(self) -> bool { + match self { + Self::Address(_) => true, + Self::ContractId(_) => false, + } + } + + /// Returns whether the `Identity` represents a `ContractId`. + /// + /// # Returns + /// + /// * [bool] - Indicates whether the `Identity` holds a `ContractId`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let identity = Identity::ContractId(ContractId::from(ZERO_B256)); + /// assert(identity.is_contract_id()); + /// } + /// ``` + pub fn is_contract_id(self) -> bool { + match self { + Self::Address(_) => false, + Self::ContractId(_) => true, + } + } + + /// Transfer `amount` coins of the type `asset_id` and send them + /// to the Identity. + /// + /// # Additional Information + /// + /// **_WARNING:_** + /// If the Identity is a contract this may transfer coins to the contract even with no way to retrieve them + /// (i.e. no withdrawal functionality on receiving contract), possibly leading + /// to the **_PERMANENT LOSS OF COINS_** if not used with care. + /// + /// # Arguments + /// + /// * `asset_id`: [AssetId] - The `AssetId` of the token to transfer. + /// * `amount`: [u64] - The amount of tokens to transfer. + /// + /// # Reverts + /// + /// * When `amount` is greater than the contract balance for `asset_id`. + /// * When `amount` is equal to zero. + /// * When there are no free variable outputs when transferring to an `Address`. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::{BASE_ASSET_ID, ZERO_B256}; + /// + /// fn foo() { + /// let to_address = Identity::Address(Address::from(ZERO_B256)); + /// let to_contract_id = Identity::ContractId(ContractId::from(ZERO_B256)); + /// to_address.transfer(BASE_ASSET_ID, 500); + /// to_contract_id.transfer(BASE_ASSET_ID, 500); + /// } + /// ``` + pub fn transfer(self, asset_id: AssetId, amount: u64) { + match self { + Identity::Address(addr) => addr.transfer(asset_id, amount), + Identity::ContractId(id) => id.transfer(asset_id, amount), + }; + } +} + +impl Identity { + /// Mint `amount` coins of `sub_id` and transfer them to the Identity. + /// + /// # Additional Information + /// + /// **_WARNING:_** + /// If the Identity is a contract, this will transfer coins to the contract even with no way to retrieve them + /// (i.e: no withdrawal functionality on the receiving contract), possibly leading to + /// the **_PERMANENT LOSS OF COINS_** if not used with care. + /// + /// # Arguments + /// + /// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. + /// * `amount`: [u64] - The amount of tokens to mint. + /// + /// # Examples + /// + /// ```sway + /// use std::constants::ZERO_B256; + /// + /// fn foo() { + /// let address_identity = Identity::Address(Address::from(ZERO_B256)); + /// let contract_identity = Identity::ContractId(ContractId::from(ZERO_B256)); + /// address_identity.mint_to(ZERO_B256, 500); + /// contract_identity.mint_to(ZERO_B256, 500); + /// } + /// ``` + pub fn mint_to(self, sub_id: SubId, amount: u64) { + asm(r1: amount, r2: sub_id) { + mint r1 r2; + }; + self.transfer(AssetId::new(contract_id(), sub_id), amount); + } +} + +#[test] +fn test_address() { + let address = Address::from(ZERO_B256); + let identity = Identity::Address(address); + assert(identity.is_address()); + assert(!identity.is_contract_id()); + assert(identity.as_address().unwrap() == address); + assert(identity.as_contract_id().is_none()); +} + +#[test] +fn test_contract_id() { + let id = ZERO_B256; + let identity = Identity::ContractId(ContractId::from(ZERO_B256)); + assert(!identity.is_address()); + assert(identity.is_contract_id()); + assert(identity.as_contract_id().unwrap().value == id); + assert(identity.as_address().is_none()); +} + +impl Hash for Identity { + fn hash(self, ref mut state: Hasher) { + match self { + Identity::Address(address) => { + 0_u8.hash(state); + address.hash(state); + }, + Identity::ContractId(id) => { + 1_u8.hash(state); + id.hash(state); + }, + } + } +} \ No newline at end of file diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/inputs.sw b/sway-lsp/tests/fixtures/fixtures-std/src/inputs.sw new file mode 100644 index 00000000000..537a03c6118 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/inputs.sw @@ -0,0 +1,624 @@ +//! Getters for fields on transaction inputs. +//! This includes `Input::Coins`, `Input::Messages` and `Input::Contracts`. +library; + +use ::address::Address; +use ::assert::assert; +use ::bytes::Bytes; +use ::constants::BASE_ASSET_ID; +use ::contract_id::{AssetId, ContractId}; +use ::option::Option::{self, *}; +use ::revert::revert; +use ::tx::{ + GTF_CREATE_INPUT_AT_INDEX, + GTF_CREATE_INPUTS_COUNT, + GTF_SCRIPT_INPUT_AT_INDEX, + GTF_SCRIPT_INPUTS_COUNT, + Transaction, + tx_type, +}; +use core::ops::Eq; + +const GTF_INPUT_TYPE = 0x101; + +// GTF Opcode const selectors +// +// pub const GTF_INPUT_COIN_TX_ID = 0x102; +// pub const GTF_INPUT_COIN_OUTPUT_INDEX = 0x103; +pub const GTF_INPUT_COIN_OWNER = 0x104; +pub const GTF_INPUT_COIN_AMOUNT = 0x105; +pub const GTF_INPUT_COIN_ASSET_ID = 0x106; +// pub const GTF_INPUT_COIN_TX_POINTER = 0x107; +pub const GTF_INPUT_COIN_WITNESS_INDEX = 0x108; +pub const GTF_INPUT_COIN_MATURITY = 0x109; +pub const GTF_INPUT_COIN_PREDICATE_LENGTH = 0x10A; +pub const GTF_INPUT_COIN_PREDICATE_DATA_LENGTH = 0x10B; +pub const GTF_INPUT_COIN_PREDICATE = 0x10C; +pub const GTF_INPUT_COIN_PREDICATE_DATA = 0x10D; + +// pub const GTF_INPUT_CONTRACT_TX_ID = 0x10E; +// pub const GTF_INPUT_CONTRACT_OUTPUT_INDEX = 0x10F; +// pub const GTF_INPUT_CONTRACT_BALANCE_ROOT = 0x110; +// pub const GTF_INPUT_CONTRACT_STATE_ROOT = 0x111; +// pub const GTF_INPUT_CONTRACT_TX_POINTER = 0x112; +// pub const GTF_INPUT_CONTRACT_CONTRACT_ID = 0x113; +pub const GTF_INPUT_MESSAGE_SENDER = 0x115; +pub const GTF_INPUT_MESSAGE_RECIPIENT = 0x116; +pub const GTF_INPUT_MESSAGE_AMOUNT = 0x117; +pub const GTF_INPUT_MESSAGE_NONCE = 0x118; +// These are based on the old spec (before +// https://github.com/FuelLabs/fuel-specs/pull/400) because that's what's +// currently implemented in `fuel-core`, `fuel-asm`, and `fuel-tx. They should +// eventually be updated. +pub const GTF_INPUT_MESSAGE_WITNESS_INDEX = 0x119; +pub const GTF_INPUT_MESSAGE_DATA_LENGTH = 0x11A; +pub const GTF_INPUT_MESSAGE_PREDICATE_LENGTH = 0x11B; +pub const GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH = 0x11C; +pub const GTF_INPUT_MESSAGE_DATA = 0x11D; +pub const GTF_INPUT_MESSAGE_PREDICATE = 0x11E; +pub const GTF_INPUT_MESSAGE_PREDICATE_DATA = 0x11F; + +/// The input type for a transaction. +pub enum Input { + /// A coin input. + Coin: (), + /// A contract input. + Contract: (), + /// A message input. + Message: (), +} + +impl Eq for Input { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Input::Coin, Input::Coin) => true, + (Input::Contract, Input::Contract) => true, + (Input::Message, Input::Message) => true, + _ => false, + } + } +} + +// General Inputs + +/// Gets the type of the input at `index`. +/// +/// # Additional Information +/// +/// The Input can be of 3 variants, `Input::Coin`, `Input::Contract` or `Input::Message`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Input] - The type of the input at `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_type; +/// +/// fn foo() { +/// let input_type = input_type(0); +/// assert(input_type == Input::Coin); +/// } +/// ``` +pub fn input_type(index: u64) -> Input { + match __gtf::(index, GTF_INPUT_TYPE) { + 0u8 => Input::Coin, + 1u8 => Input::Contract, + 2u8 => Input::Message, + _ => revert(0), + } +} + +/// Gets the transaction inputs count. +/// +/// # Returns +/// +/// * [u8] - The number of inputs in the transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_count; +/// +/// fn foo() { +/// let input_count = input_count(); +/// assert(input_count == 1); +/// } +/// ``` +pub fn input_count() -> u8 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), + Transaction::Create => __gtf::(0, GTF_CREATE_INPUTS_COUNT), + } +} + +/// Gets the pointer of the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [u64] - The pointer of the input at `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_pointer; +/// +/// fn foo() { +/// let input_pointer = input_pointer(0); +/// } +/// ``` +pub fn input_pointer(index: u64) -> u64 { + match tx_type() { + Transaction::Script => __gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX), + Transaction::Create => __gtf::(index, GTF_CREATE_INPUT_AT_INDEX), + } +} + +/// Gets amount field from input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The amount of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_amount; +/// +/// fn foo() { +/// let input_amount = input_amount(0); +/// assert(input_amount.unwrap() == 100); +/// } +/// ``` +pub fn input_amount(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_AMOUNT)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_AMOUNT)), + Input::Contract => None, + } +} + +/// Gets owner field from input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option
] - The owner of the input at `index`, if the input's type is `Input::Coin`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_owner; +/// +/// fn foo() { +/// let input_owner = input_owner(0); +/// assert(input_owner.is_some()); // Ensure the input is a coin input. +/// } +/// ``` +pub fn input_owner(index: u64) -> Option
{ + match input_type(index) { + Input::Coin => Some(Address::from(__gtf::(index, GTF_INPUT_COIN_OWNER))), + _ => None, + } +} + +/// Gets the predicate data pointer from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The predicate data pointer of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate_data_pointer; +/// +/// fn foo() { +/// let input_predicate_data_pointer = input_predicate_data_pointer(0); +/// assert(input_predicate_data_pointer.is_some()); // Ensure the input is a coin or message input. +/// } +pub fn input_predicate_data_pointer(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_PREDICATE_DATA)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_PREDICATE_DATA)), + Input::Contract => None, + } +} + +/// Gets the predicate data from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [T] - The predicate data of the input at `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate_data; +/// +/// fn foo() { +/// let input_predicate_data: u64 = input_predicate_data(0); +/// assert(input_predicate_data == 100); +/// } +/// ``` +pub fn input_predicate_data(index: u64) -> T { + match input_predicate_data_pointer(index) { + Some(d) => d.read::(), + None => revert(0), + } +} + +/// Gets the AssetId of the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The asset_id of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::BASE_ASSET_ID, inputs::input_asset_id}; +/// +/// fn foo() { +/// let input_asset_id = input_asset_id(0); +/// assert(input_asset_id.unwrap() == BASE_ASSET_ID); +/// } +/// ``` +pub fn input_asset_id(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(AssetId::from(__gtf::(index, GTF_INPUT_COIN_ASSET_ID))), + Input::Message => Some(BASE_ASSET_ID), + Input::Contract => None, + } +} + +/// Gets the witness index from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The witness index of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_witness_index; +/// +/// fn foo() { +/// let input_witness_index = input_witness_index(0); +/// assert(input_witness_index.is_some()); // Ensure the input has a witness index. +/// } +/// ``` +pub fn input_witness_index(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_WITNESS_INDEX)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_WITNESS_INDEX)), + Input::Contract => None, + } +} + +/// Gets the predicate length from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The predicate length of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate_length; +/// +/// fn foo() { +/// let input_predicate_length = input_predicate_length(0); +/// assert(input_predicate_length.unwrap() != 0u16); +/// } +/// ``` +pub fn input_predicate_length(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_PREDICATE_LENGTH)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_PREDICATE_LENGTH)), + Input::Contract => None, + } +} + +/// Gets the predicate pointer from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The predicate pointer of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate_pointer; +/// +/// fn foo() { +/// let input_predicate_pointer = input_predicate_pointer(0); +/// assert(input_predicate_pointer.is_some()); +/// } +/// ``` +pub fn input_predicate_pointer(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_PREDICATE)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_PREDICATE)), + Input::Contract => None, + } +} + +/// Gets the predicate from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Bytes] - The predicate bytecode of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`. +/// +/// # Reverts +/// +/// * When the input's type is not `Input::Coin` or `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate; +/// +/// fn foo() { +/// let input_predicate = input_predicate(0); +/// assert(input_predicate.len() != 0); +/// } +/// ``` +pub fn input_predicate(index: u64) -> Bytes { + let wrapped = input_predicate_length(index); + if wrapped.is_none() { + revert(0); + }; + let length = wrapped.unwrap().as_u64(); + let mut data_bytes = Bytes::with_capacity(length); + match input_predicate_pointer(index) { + Some(d) => { + data_bytes.len = length; + d.copy_bytes_to(data_bytes.buf.ptr, length); + data_bytes + }, + None => revert(0), + } +} + +/// Gets the predicate data length from the input at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The predicate data length of the input at `index`, if the input's type is `Input::Coin` or `Input::Message`, else `None`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_predicate_data_length; +/// +/// fn foo() { +/// let input_predicate_data_length = input_predicate_data_length(0); +/// assert(input_predicate_data_length.unwrap() != 0_u16); +/// } +/// ``` +pub fn input_predicate_data_length(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_PREDICATE_DATA_LENGTH)), + Input::Message => Some(__gtf::(index, GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH)), + Input::Contract => None, + } +} + +// Coin Inputs + +/// Gets the maturity from the input at `index`. +/// +/// # Additional Information +/// +/// The matury of an input refers to the number of blocks that must pass before the input can be spent. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Option] - The maturity of the input at `index`, if the input's type is `Input::Coin`, else `None`. +/// +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_maturity; +/// +/// fn foo() { +/// let input_maturity = input_maturity(0); +/// assert(input_maturity.unwrap() == 0_u32); +/// } +/// ``` +pub fn input_maturity(index: u64) -> Option { + match input_type(index) { + Input::Coin => Some(__gtf::(index, GTF_INPUT_COIN_MATURITY)), + _ => None, + } +} + +/// Gets the sender of the input message at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Address] - The sender of the input message at `index`, if the input's type is `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, inputs::input_message_sender}; +/// +/// fn foo() { +/// let input_message_sender = input_message_sender(0); +/// assert(input_message_sender != Address::from(ZERO_B256)); +/// } +/// ``` +pub fn input_message_sender(index: u64) -> Address { + Address::from(__gtf::(index, GTF_INPUT_MESSAGE_SENDER)) +} + +/// Gets the recipient of the input message at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [Address] - The recipient of the input message at `index`, if the input's type is `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, inputs::input_message_recipient}; +/// +/// fn foo() { +/// let input_message_recipient = input_message_recipient(0); +/// assert(input_message_recipient != Address::from(ZERO_B256)); +/// } +/// ``` +pub fn input_message_recipient(index: u64) -> Address { + Address::from(__gtf::(index, GTF_INPUT_MESSAGE_RECIPIENT)) +} + +/// Gets the nonce of input message at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [b256] - The nonce of the input message at `index`, if the input's type is `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, inputs::input_message_nonce}; +/// +/// fn foo() { +/// let input_message_nonce = input_message_nonce(0); +/// assert(input_message_nonce != b256::from(ZERO_B256)); +/// } +/// ``` +pub fn input_message_nonce(index: u64) -> b256 { + __gtf::(index, GTF_INPUT_MESSAGE_NONCE) +} + +/// Gets the length of the input message at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// +/// # Returns +/// +/// * [u16] - The length of the input message at `index`, if the input's type is `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_message_length; +/// +/// fn foo() { +/// let input_message_length = input_message_length(0); +/// assert(input_message_length != 0_u16); +/// } +/// ``` +pub fn input_message_data_length(index: u64) -> u16 { + __gtf::(index, GTF_INPUT_MESSAGE_DATA_LENGTH) +} + +/// Gets the data of the input message at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the input to check. +/// * `offset`: [u64] - The offset to start reading the data from. +/// +/// # Returns +/// +/// * [Bytes] - The data of the input message at `index`, if the input's type is `Input::Message`. +/// +/// # Reverts +/// +/// * When the input's type is not `Input::Message`. +/// +/// # Examples +/// +/// ```sway +/// use std::inputs::input_message_data; +/// +/// fn foo() { +/// let input_message_data = input_message_data(0, 0); +/// assert(input_message_data.len() != 0); +/// } +/// ``` +pub fn input_message_data(index: u64, offset: u64) -> Bytes { + assert(valid_input_type(index, Input::Message)); + let data = __gtf::(index, GTF_INPUT_MESSAGE_DATA); + let data_with_offset = data.add_uint_offset(offset); + let length = input_message_data_length(index).as_u64(); + let mut data_bytes = Bytes::with_capacity(length); + data_bytes.len = length; + data_with_offset.copy_bytes_to(data_bytes.buf.ptr, length); + data_bytes +} + +fn valid_input_type(index: u64, expected_type: Input) -> bool { + input_type(index) == expected_type +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/intrinsics.sw b/sway-lsp/tests/fixtures/fixtures-std/src/intrinsics.sw new file mode 100644 index 00000000000..ca80a8ea2b1 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/intrinsics.sw @@ -0,0 +1,92 @@ +//! Exposes compiler intrinsics as stdlib wrapper functions. +library; + +/// Returns whether a generic type `T` is a reference type or not. +/// +/// # Returns +/// +/// * [bool] - `true` if `T` is a reference type, `false` otherwise. +/// +/// # Examples +/// +/// ```sway +/// use std::intrinsics::is_reference_type; +/// +/// fn foo() { +/// let a = 1; +/// assert(is_reference_type(a)) +/// } +/// ``` +pub fn is_reference_type() -> bool { + __is_reference_type::() +} + +/// Returns the size of a generic type `T` in bytes. +/// +/// # Returns +/// +/// * [u64] - The size of `T` in bytes. +/// +/// # Examples +/// +/// ```sway +/// use std::intrinsics::size_of; +/// +/// fn foo() { +/// assert(size_of::() == 8); +/// } +/// ``` +/// +/// ```sway +/// use std::intrinsics::size_of; +/// +/// pub struct Foo { +/// a: u64, +/// b: u64, +/// } +/// +/// fn foo() { +/// assert(size_of::() == 16); +/// } +/// ``` +pub fn size_of() -> u64 { + __size_of::() +} + +/// Returns the size of the type of a value in bytes. +/// +/// # Arguments +/// +/// * `val` - The value to get the size of. +/// +/// # Returns +/// +/// * [u64] - The size of the type of `val` in bytes. +/// +/// # Examples +/// +/// ```sway +/// use std::intrinsics::size_of_val; +/// +/// fn foo() { +/// let a = 1; +/// assert(size_of_val(a) == 8); +/// } +/// ``` +/// +/// ```sway +/// use std::intrinsics::size_of_val; +/// +/// pub struct Foo { +/// a: u64, +/// b: u64, +/// } +/// +/// fn foo() { +/// let a = Foo { a: 1, b: 2 }; +/// assert(size_of_val(a) == 16); +/// } +/// ``` +pub fn size_of_val(val: T) -> u64 { + __size_of_val::(val) +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/lib.sw b/sway-lsp/tests/fixtures/fixtures-std/src/lib.sw new file mode 100644 index 00000000000..6b8529f2187 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/lib.sw @@ -0,0 +1,46 @@ +library; + +pub mod error_signals; +pub mod logging; +pub mod revert; +pub mod result; +pub mod option; +pub mod assert; +pub mod convert; +pub mod intrinsics; +pub mod alloc; +pub mod registers; +pub mod vec; +pub mod bytes; +pub mod primitive_conversions; +pub mod math; +pub mod flags; +pub mod u128; +pub mod u256; +pub mod alias; +pub mod hash; +pub mod contract_id; +pub mod constants; +pub mod call_frames; +pub mod context; +pub mod external; +pub mod b512; +pub mod tx; +pub mod outputs; +pub mod address; +pub mod identity; +pub mod ecr; +pub mod vm; +pub mod string; +pub mod r#storage; +pub mod block; +pub mod inputs; +pub mod auth; +pub mod token; +pub mod message; +pub mod prelude; +pub mod low_level_call; +pub mod array_conversions; +pub mod bytes_conversions; + +use core::*; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/logging.sw b/sway-lsp/tests/fixtures/fixtures-std/src/logging.sw new file mode 100644 index 00000000000..6d5dfcb6b38 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/logging.sw @@ -0,0 +1,24 @@ +//! Allows logging of arbitrary stack types, emitted as either `Log` or `Logd` receipts. +library; + +/// Log any stack type. +/// +/// # Additional Information +/// +/// If the type is a reference type, `log` is used. +/// Otherwise `logd` is used.' +/// +/// # Arguments +/// +/// * `value`: [T] - The value to log. +/// +/// # Examples +/// +/// ```sway +/// fn foo() { +/// log("Fuel is blazingly fast"); +/// } +/// ``` +pub fn log(value: T) { + __log::(value); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/low_level_call.sw b/sway-lsp/tests/fixtures/fixtures-std/src/low_level_call.sw new file mode 100644 index 00000000000..b52fc776064 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/low_level_call.sw @@ -0,0 +1,273 @@ +//! Utilities to help with low level calls. +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::contract_id::{AssetId, ContractId}; +use ::option::Option; +use ::revert::require; +use ::vec::Vec; + +/// A struct representing the call parameters of a function call. +pub struct CallParams { + /// Amount of the asset to transfer. + coins: u64, + /// AssetId of the asset to transfer. + asset_id: AssetId, + /// Gas to forward. + gas: u64, +} + +// TODO : Replace with `from` when implemented +/// Represent a contract ID as a `Bytes`, so it can be concatenated with a payload. +/// +/// # Additional Information +/// +/// It is recommended to use the `call_with_function_selector` function directly, unless you know what you are doing. +/// +/// # Arguments +/// +/// * `contract_id`: [ContractId] - The contract ID to be represented as a `Bytes`. +/// +/// # Returns +/// +/// * [Bytes] - The input ContractId represented as a `Bytes`. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{contract_id_to_bytes, call_with_raw_payload, CallParams}; +/// +/// fn call_with_copy_type_arg(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams) { +/// let mut payload = Bytes::new(); +/// payload.append(contract_id_to_bytes(target)); +/// payload.append(function_selector); +/// payload.append(calldata); +/// +/// call_with_raw_payload(payload, call_params); +/// } +/// ``` +/// +/// ```sway +/// use std::low_level_call::{bytes::Bytes, contract_id_to_bytes, call_with_raw_payload, CallParams, ptr_as_bytes}; +/// +/// fn call_with_reference_type_arg(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams) { +/// let mut payload = Bytes::new(); +/// payload.append(contract_id_to_bytes(target)); +/// payload.append(function_selector); +/// payload.append(ptr_as_bytes(calldata.buf.ptr)); +/// +/// call_with_raw_payload(payload, call_params); +/// } +/// ``` +fn contract_id_to_bytes(contract_id: ContractId) -> Bytes { + let mut target_bytes = Bytes::with_capacity(32); + target_bytes.len = 32; + + __addr_of(contract_id).copy_bytes_to(target_bytes.buf.ptr, 32); + + target_bytes +} + +/// Represent a raw pointer as a `Bytes`, so it can be concatenated with a payload. +/// +/// # Additional Information +/// +/// It is recommended to use the `call_with_function_selector` function directly, unless you know what you are doing. +/// +/// # Arguments +/// +/// * `ptr`: [raw_ptr] - The raw pointer to be represented as a `Bytes`. +/// +/// # Returns +/// +/// * [Bytes] - The input raw pointer represented as a `Bytes`. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{bytes::Bytes, contract_id_to_bytes, call_with_raw_payload, CallParams, ptr_as_bytes}; +/// +/// fn call_with_reference_type_arg(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams) { +/// let mut payload = Bytes::new(); +/// payload.append(contract_id_to_bytes(target)); +/// payload.append(function_selector); +/// payload.append(ptr_as_bytes(calldata.buf.ptr)); +/// +/// call_with_raw_payload(payload, call_params); +/// } +/// ``` +fn ptr_as_bytes(ptr: raw_ptr) -> Bytes { + let mut bytes = Bytes::with_capacity(8); + bytes.len = 8; + + // Need to copy pointer to heap so it has an address and can be copied onto the bytes buffer + let mut ptr_on_heap = Vec::new(); + ptr_on_heap.push(ptr); + ptr_on_heap.buf.ptr.copy_bytes_to(bytes.buf.ptr, 8); + + bytes +} + +/// Call a target contract with an already-encoded payload. +/// +/// # Additional Information +/// +/// It is recommended to use the `call_with_function_selector` function directly, unless you know what you are doing. +/// +/// The payload needs to be encoded according to the [Fuel VM specification](https://github.com/FuelLabs/fuel-specs/blob/master/src/vm/instruction_set.md#call-call-contract): +/// +/// bytes type value description +/// 32 byte[32] to Contract ID to call. +/// 8 byte[8] param1 First parameter (function selector). +/// 8 byte[8] param2 Second parameter (abi-encoded calldata: value if value type, otherwise pointer to reference type). +/// +/// # Arguments +/// +/// * `payload` : [Bytes] - The encoded payload to be called. +/// * `call_params` : [CallParams] - The call parameters of the function call. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{bytes::Bytes, contract_id_to_bytes, call_with_raw_payload, CallParams}; +/// +/// fn call_with_copy_type_arg(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams) { +/// let mut payload = Bytes::new(); +/// payload.append(contract_id_to_bytes(target)); +/// payload.append(function_selector); +/// payload.append(calldata); +/// +/// call_with_raw_payload(payload, call_params); +/// } +/// ``` +fn call_with_raw_payload(payload: Bytes, call_params: CallParams) { + asm(r1: payload.buf.ptr, r2: call_params.coins, r3: call_params.asset_id, r4: call_params.gas) { + call r1 r2 r3 r4; + }; +} + +/// Encode a payload from the function selection and calldata. +/// +/// # Additional Information +/// +/// It is recommended to use the `call_with_function_selector` function directly, unless you know what you are doing. +/// +/// # Arguments +/// +/// * `target` : [ContractId] - The ContractId of the contract to be called. +/// * `function_selector` : [Bytes] - The function selector of the function to be called, i.e. the first 8 bytes of `sha256("my_func(u64)")`. +/// * `calldata` : [Bytes] - The encoded arguments with which to call the function. +/// * `single_value_type_arg` : [bool] - Whether the function being called takes a single value-type argument. +/// +/// # Returns +/// +/// * [Bytes] - The encoded payload. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{bytes::Bytes, create_payload, call_with_raw_payload, CallParams}; +/// +/// fn call_contract(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams, single_value_type_arg: bool) { +/// let payload = create_payload(target, function_selector, calldata, single_value_type_arg); +/// +/// call_with_raw_payload(payload, call_params); +/// } +/// ``` +fn create_payload( + target: ContractId, + function_selector: Bytes, + calldata: Bytes, + single_value_type_arg: bool, +) -> Bytes { + /* + packs args according to spec (https://github.com/FuelLabs/fuel-specs/blob/master/src/vm/instruction_set.md#call-call-contract) : + + bytes type value description + 32 byte[32] to Contract ID to call. + 8 byte[8] param1 First parameter (function selector). + 8 byte[8] param2 Second parameter (abi-encoded calldata: value if value type, otherwise pointer to reference type). + */ + require(function_selector.len() == 8, "function selector must be 8 bytes"); + + // let mut payload = Bytes::new().append(contract_id_to_bytes(target)).append(function_selector); + let mut payload = Bytes::new(); + payload.append(contract_id_to_bytes(target)); + payload.append(function_selector); + + if (single_value_type_arg) { + payload.append(calldata); // When calldata is copy type, just pass calldata + } else { + payload.append(ptr_as_bytes(calldata.buf.ptr)); // When calldata is reference type, need to get pointer as bytes + }; + + payload +} + +/// Call a target contract with a function selector and calldata, provided as `Bytes`. +/// +/// # Arguments +/// +/// * `target` : [ContractId] - The ContractId of the contract to be called. +/// * `function_selector` : [Bytes] - The function selector of the function to be called, i.e. the first 8 bytes of `sha256("my_func(u64)")`. +/// * `calldata` : [Bytes] - The encoded arguments with which to call the function. +/// * `single_value_type_arg` : [bool] - Whether the function being called takes a single value-type argument. +/// * `call_params` : [CallParams] - The amount and color of coins to forward, and the gas to forward. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{bytes::Bytes, call_with_function_selector, CallParams}; +/// +/// fn call_contract(target: ContractId, function_selector: Bytes, calldata: Bytes, call_params: CallParams, single_value_type_arg: bool) { +/// call_with_function_selector(target, function_selector, calldata, single_value_type_arg, call_params); +/// } +/// ``` +pub fn call_with_function_selector( + target: ContractId, + function_selector: Bytes, + calldata: Bytes, + single_value_type_arg: bool, + call_params: CallParams, +) { + let payload = create_payload(target, function_selector, calldata, single_value_type_arg); + call_with_raw_payload(payload, call_params); +} + +// TO DO: Deprecate when SDK supports Bytes +/// Call a target contract with a function selector and calldata, provided as `Vec`. +/// +/// # Additional Information +/// +/// It is recommended to use the `call_with_function_selector` function as this function will be deprecated in the future. +/// +/// # Arguments +/// +/// * `target` : [ContractId] - The ContractId of the contract to be called. +/// * `function_selector` : [Vec] - The function selector of the function to be called, i.e. the first 8 bytes of `sha256("my_func(u64)")`. +/// * `calldata` : [Vec] - The encoded arguments with which to call the function. +/// * `single_value_type_arg` : [bool] - Whether the function being called takes a single value-type argument. +/// * `call_params` : [CallParams] - The amount and color of coins to forward, and the gas to forward. +/// +/// # Examples +/// +/// ```sway +/// use std::low_level_call::{call_with_function_selector_vec, CallParams}; +/// +/// fn call_contract(target: ContractId, function_selector: Vec, calldata: Vec, call_params: CallParams, single_value_type_arg: bool) { +/// call_with_function_selector_vec(target, function_selector, calldata, single_value_type_arg, call_params); +/// } +/// ``` +pub fn call_with_function_selector_vec( + target: ContractId, + function_selector: Vec, + calldata: Vec, + single_value_type_arg: bool, + call_params: CallParams, +) { + let mut function_selector = function_selector; + let mut calldata = calldata; + + call_with_function_selector(target, Bytes::from(function_selector), Bytes::from(calldata), single_value_type_arg, call_params); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/math.sw b/sway-lsp/tests/fixtures/fixtures-std/src/math.sw new file mode 100644 index 00000000000..eec1b436951 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/math.sw @@ -0,0 +1,165 @@ +//! Utilities for common math operations. +library; + +/// Calculates the square root. +pub trait Root { + fn sqrt(self) -> Self; +} + +impl Root for u64 { + fn sqrt(self) -> Self { + let index: u64 = 2; + asm(r1: self, r2: index, r3) { + mroo r3 r1 r2; + r3: u64 + } + } +} + +impl Root for u32 { + fn sqrt(self) -> Self { + let index: u64 = 2; + asm(r1: self, r2: index, r3) { + mroo r3 r1 r2; + r3: u32 + } + } +} + +impl Root for u16 { + fn sqrt(self) -> Self { + let index: u64 = 2; + asm(r1: self, r2: index, r3) { + mroo r3 r1 r2; + r3: u16 + } + } +} + +impl Root for u8 { + fn sqrt(self) -> Self { + let index: u64 = 2; + asm(r1: self, r2: index, r3) { + mroo r3 r1 r2; + r3: u8 + } + } +} + +/// Calculates a number to a given power. +pub trait Power { + fn pow(self, exponent: u32) -> Self; +} + +impl Power for u64 { + fn pow(self, exponent: u32) -> Self { + asm(r1: self, r2: exponent, r3) { + exp r3 r1 r2; + r3: Self + } + } +} + +impl Power for u32 { + fn pow(self, exponent: u32) -> Self { + asm(r1: self, r2: exponent, r3) { + exp r3 r1 r2; + r3: Self + } + } +} + +impl Power for u16 { + fn pow(self, exponent: u32) -> Self { + asm(r1: self, r2: exponent, r3) { + exp r3 r1 r2; + r3: Self + } + } +} + +impl Power for u8 { + fn pow(self, exponent: u32) -> Self { + asm(r1: self, r2: exponent, r3) { + exp r3 r1 r2; + r3: Self + } + } +} + +/// Trait for exponential functions. +/// This should exist for UFP64, UFP128 and their signed versions. +pub trait Exponent { + // exponential function: e ^ exponent + fn exp(exponent: Self) -> Self; +} + +/// Calculates the log with a given base. +pub trait Logarithm { + fn log(self, base: Self) -> Self; +} + +impl Logarithm for u64 { + fn log(self, base: Self) -> Self { + asm(r1: self, r2: base, r3) { + mlog r3 r1 r2; + r3: Self + } + } +} + +impl Logarithm for u32 { + fn log(self, base: Self) -> Self { + asm(r1: self, r2: base, r3) { + mlog r3 r1 r2; + r3: Self + } + } +} + +impl Logarithm for u16 { + fn log(self, base: Self) -> Self { + asm(r1: self, r2: base, r3) { + mlog r3 r1 r2; + r3: Self + } + } +} + +impl Logarithm for u8 { + fn log(self, base: Self) -> Self { + asm(r1: self, r2: base, r3) { + mlog r3 r1 r2; + r3: Self + } + } +} + +/// Calculates the binary log. +pub trait BinaryLogarithm { + fn log2(self) -> Self; +} + +impl BinaryLogarithm for u64 { + fn log2(self) -> Self { + self.log(2) + } +} + +impl BinaryLogarithm for u32 { + fn log2(self) -> Self { + self.log(2u32) + } +} + +impl BinaryLogarithm for u16 { + fn log2(self) -> Self { + self.log(2u16) + } +} + +impl BinaryLogarithm for u8 { + fn log2(self) -> Self { + self.log(2u8) + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/message.sw b/sway-lsp/tests/fixtures/fixtures-std/src/message.sw new file mode 100644 index 00000000000..8fe365b7ed9 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/message.sw @@ -0,0 +1,73 @@ +//! Helper functions to sign and send messages. +library; + +use ::alloc::alloc_bytes; +use ::bytes::Bytes; +use ::outputs::{Output, output_count, output_type}; +use ::revert::revert; + +/// Sends a message `msg_data` to `recipient` with a `coins` amount of the base asset. +/// +/// # Additional Information +/// +/// Use `send_typed_message` instead of `send_message` if the message needs to be indexed. +/// +/// # Arguments +/// +/// * `recipient`: [b256] - The address of the message recipient. +/// * `msg_data`: [Bytes] - Arbitrary length message data. +/// * `coins`: [u64] - Amount of base asset to send. +/// +/// # Examples +/// +/// ```sway +/// use std::{message::send_message, bytes::Bytes}; +/// +/// fn foo() { +/// let recipient = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// let mut bytes = Bytes::new(); +/// bytes.push(5u8); +/// send_message(recipient, bytes, 50); +/// } +/// ``` +pub fn send_message(recipient: b256, msg_data: Bytes, coins: u64) { + let recipient_pointer = __addr_of(recipient); + let mut size = 0; + let mut msg_data_pointer = recipient_pointer; + + // If msg_data is empty, we just ignore it and pass `smo` a pointer to the inner value of recipient. + if !msg_data.is_empty() { + size = msg_data.len(); + msg_data_pointer = msg_data.buf.ptr; + } + + asm(r1: recipient_pointer, r2: msg_data_pointer, r3: size, r4: coins) { + smo r1 r2 r3 r4; + }; +} + +/// Sends a message `msg_data` of type `T` to `recipient` with a `coins` amount of the base asset. +/// +/// # Additional Information +/// +/// Use `send_typed_message` instead of `send_message` if the message needs to be indexed. +/// +/// # Arguments +/// +/// * `recipient`: [b256] - The address of the message recipient. +/// * `msg_data`: [T] - Message data of arbitrary type `T`. +/// * `coins`: [u64] - Amount of base asset to send. +/// +/// # Examples +/// +/// ```sway +/// use std::message::send_typed_message; +/// +/// fn foo() { +/// let recipient = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// send_message(recipient, "Fuel is blazingly fast", 50); +/// } +/// ``` +pub fn send_typed_message(recipient: b256, msg_data: T, coins: u64) { + __smo(recipient, msg_data, coins); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/option.sw b/sway-lsp/tests/fixtures/fixtures-std/src/option.sw new file mode 100644 index 00000000000..d6e865e3071 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/option.sw @@ -0,0 +1,251 @@ +//! A type for optional values. +//! +//! Type `Option` represents an optional value: every `Option` +//! is either `Some` and contains a value, or `None`, and +//! does not. `Option` types are very common in Sway code, as +//! they have a number of uses: +//! +//! * Initial values where `None` can be used as an initializer. +//! * Return value for otherwise reporting simple errors, where `None` is +//! returned on error. +//! * Optional struct fields. +//! * Optional function arguments. +//! +//! `Option`s are commonly paired with pattern matching to query the presence +//! of a value and take action, always accounting for the `None` case. +//! +//! ``` +//! fn divide(numerator: u64, denominator: u64) -> Option { +//! if denominator == 0 { +//! None +//! } else { +//! Some(numerator / denominator) +//! } +//! } +//! +//! fn call_divide() { +//! // The return value of the function is an option +//! let result = divide(6, 2); +//! +//! // Pattern match to retrieve the value +//! match result { +//! // The division was valid +//! Some(x) => std::logging::log(x), +//! // The division was invalid +//! None => std::logging::log("Cannot divide by 0"), +//! } +//! } +//! ``` +//! +//! # Method overview +//! +//! In addition to working with pattern matching, `Option` provides a wide +//! variety of different methods. +//! +//! # Querying the variant +//! +//! The `is_some` and `is_none` methods return `true` if the `Option` +//! is `Some` or `None`, respectively. +//! +//! `is_none`: `Option::is_none` +//! `is_some`: `Option::is_some` +//! +//! # Extracting the contained value +//! +//! These methods extract the contained value in an `Option` when it +//! is the `Some` variant. If the `Option` is `None`: +//! +//! * `unwrap` reverts. +//! * `unwrap_or` returns the provided default value. +//! +//! `unwrap` : `Option::unwrap` +//! `unwrap_or`: `Option::unwrap_or` +//! +//! # Transforming contained values +//! +//! These methods transform `Option` to `Result`: +//! +//! * `ok_or` transforms `Some(v)` to `Ok(v)`, and `None` to +//! `Err(e)` using the provided default error value. +//! +//! `Err(e)` : `Result::Err` +//! `Ok(v)` : `Result::Ok` +//! `Some(v)`: `Option::Some` +//! `ok_or` : `Option::ok_or` +library; + +use ::result::Result; +use ::revert::revert; + +// ANCHOR: docs_option +/// A type that represents an optional value, either `Some(val)` or `None`. +pub enum Option { + /// No value. + None: (), + /// Some value of type `T`. + Some: T, +} +// ANCHOR_END: docs_option + +// Type implementation +// +impl Option { + // Querying the contained values + // + /// Returns whether the option is the `Some` variant. + /// + /// # Returns + /// + /// * [bool] - Returns `true` if the option is `Some`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: Option = Some(2); + /// assert(x.is_some()); + /// + /// let x: Option = None; + /// assert(!x.is_some()); + /// } + /// ``` + pub fn is_some(self) -> bool { + match self { + Self::Some(_) => true, + _ => false, + } + } + + /// Returns whether the option is the `None` variant. + /// + /// # Returns + /// + /// * [bool] - Returns `true` if the option is `None`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: Option = Some(2); + /// assert(!x.is_none()); + /// + /// let x: Option = None; + /// assert(x.is_none()); + /// } + /// ``` + pub fn is_none(self) -> bool { + match self { + Self::Some(_) => false, + _ => true, + } + } + + // Getting to contained values + // + /// Returns the contained `Some` value, consuming the `self` value. + /// + /// # Additional Information + /// + /// Because this function may revert, its use is generally discouraged. + /// Instead, use pattern matching and handle the `None` + /// case explicitly, or call `unwrap_or`. + /// + /// # Returns + /// + /// * [T] - The value contained by the option. + /// + /// # Reverts + /// + /// * Reverts if the `Option` is the `None` variant. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x = Some(42); + /// assert(x.unwrap() == 42); + /// } + /// ``` + /// + /// ```sway + /// fn foo() { + /// let x: Option = None; + /// let value = x.unwrap(); // reverts + /// } + /// ``` + pub fn unwrap(self) -> T { + match self { + Self::Some(inner_value) => inner_value, + _ => revert(0), + } + } + + /// Returns the contained `Some` value or a provided default. + /// + /// # Arguments + /// + /// * `default`: [T] - The default value the function will revert to. + /// + /// # Returns + /// + /// * [T] - The contained value or the default value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// assert(Some(42).unwrap_or(69) == 42); + /// assert(None::().unwrap_or(69) == 69); + /// } + /// ``` + pub fn unwrap_or(self, default: T) -> T { + match self { + Self::Some(x) => x, + Self::None => default, + } + } + + // Transforming contained values + // + /// Transforms the `Option` into a `Result`, mapping `Some(v)` to + /// `Ok(v)` and `None` to `Err(e)`. + /// + /// # Additional Information + /// + /// `Ok(v)` : `Result::Ok` + /// `Err(e)` : `Result::Err` + /// `Some(v)`: `Option::Some` + /// `ok_or` : `Option::ok_or` + /// + /// # Arguments + /// + /// * `err`: [E] - The error value if the option is `None`. + /// + /// # Returns + /// + /// * [Result] - The result containing the value or the error. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x = Some(42); + /// match x.ok_or(0) { + /// Result::Ok(inner) => assert(inner == 42), + /// Result::Err => revert(0), + /// } + /// + /// let x: Option = None; + /// match x.ok_or(0) { + /// Result::Ok(_) => revert(0), + /// Result::Err(e) => assert(e == 0), + /// } + /// } + /// ``` + pub fn ok_or(self, err: E) -> Result { + match self { + Self::Some(v) => Result::Ok(v), + Self::None => Result::Err(err), + } + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/outputs.sw b/sway-lsp/tests/fixtures/fixtures-std/src/outputs.sw new file mode 100644 index 00000000000..e97013fb300 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/outputs.sw @@ -0,0 +1,258 @@ +//! Getters for fields on transaction outputs. +//! This includes `Output::Coins`, `Input::Messages` and `Input::Contracts`. +library; + +use ::contract_id::{AssetId, ContractId}; +use ::revert::revert; +use ::tx::{ + GTF_CREATE_OUTPUT_AT_INDEX, + GTF_CREATE_OUTPUTS_COUNT, + GTF_SCRIPT_OUTPUT_AT_INDEX, + GTF_SCRIPT_OUTPUTS_COUNT, + Transaction, + tx_type, +}; +use ::option::Option::{self, *}; + +// GTF Opcode const selectors +// +pub const GTF_OUTPUT_TYPE = 0x201; +pub const GTF_OUTPUT_COIN_TO = 0x202; +pub const GTF_OUTPUT_COIN_AMOUNT = 0x203; +pub const GTF_OUTPUT_COIN_ASSET_ID = 0x204; +// pub const GTF_OUTPUT_CONTRACT_INPUT_INDEX = 0x205; +// pub const GTF_OUTPUT_CONTRACT_BALANCE_ROOT = 0x206; +// pub const GTF_OUTPUT_CONTRACT_STATE_ROOT = 0x207; +// pub const GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID = 0x208; +// pub const GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT = 0x209; + +/// The output type for a transaction. +pub enum Output { + /// A coin output. + Coin: (), + /// A contract output. + Contract: (), + /// Remaining "change" from spending of a coin. + Change: (), + /// A variable output. + Variable: (), +} + +/// Get the type of an output at `index`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the output to get the type of. +/// +/// # Returns +/// +/// * [Output] - The type of the output at `index`. +/// +/// # Reverts +/// +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_type; +/// +/// fn foo() { +/// let output_type = output_type(0); +/// match output_type { +/// Output::Coin => { log("The output is a coin") }, +/// Output::Contract => { log("The output is a contract") }, +/// Output::Change => { log("The output is change") }, +/// Output::Variable => { log("The output is a variable") }, +/// }; +/// } +/// ``` +pub fn output_type(index: u64) -> Output { + match __gtf::(index, GTF_OUTPUT_TYPE) { + 0u8 => Output::Coin, + 1u8 => Output::Contract, + 2u8 => Output::Change, + 3u8 => Output::Variable, + _ => revert(0), + } +} + +/// Get a pointer to the output at `index` +/// for either `tx_type` (transaction-script or transaction-create). +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the output to get the pointer to. +/// +/// # Returns +/// +/// * [u64] - A pointer to the output at `index`. +/// +/// # Reverts +/// +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_pointer; +/// +/// fn foo() { +/// let output_pointer = output_pointer(0); +/// log(output_pointer); +/// } +/// ``` +pub fn output_pointer(index: u64) -> u64 { + match tx_type() { + Transaction::Script => __gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX), + Transaction::Create => __gtf::(index, GTF_CREATE_OUTPUT_AT_INDEX), + } +} + +/// Get the transaction outputs count for either `tx_type` +/// (transaction-script or transaction-create). +/// +/// # Returns +/// +/// * [u64] - The transaction outputs count. +/// +/// # Reverts +/// +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_count; +/// +/// fn foo() { +/// let output_count = output_count(); +/// log(output_count); +/// } +/// ``` +pub fn output_count() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), + Transaction::Create => __gtf::(0, GTF_CREATE_OUTPUTS_COUNT), + } +} + +/// The amount of coins to send to the output at `index`. +/// +/// # Additional Information +/// +/// This method is only meaningful if the `Output` type has the `amount` field, +/// specifically: `Output::Coin`, `Output::Change` & `Output::Variable`. +/// +/// For now, output changes are always guaranteed to have an amount of +/// zero since they're only set after execution terminates. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the output to get the amount of. +/// +/// # Returns +/// +/// * [u64] - The amount of coins to send to the output at `index`. +/// +/// # Reverts +/// +/// * When the output type is `Output::Contract`. +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_amount; +/// +/// fn foo() { +/// let output_amount = output_amount(0); +/// log(output_amount); +/// } +/// ``` +pub fn output_amount(index: u64) -> u64 { + match output_type(index) { + Output::Coin => __gtf::(index, GTF_OUTPUT_COIN_AMOUNT), + Output::Contract => revert(0), + // For now, output changes are always guaranteed to have an amount of + // zero since they're only set after execution terminates. + // use `__gtf` when GTF_OUTPUT_CHANGE_AMOUNT is available. + // See https://github.com/FuelLabs/fuel-specs/issues/402 + // and https://github.com/FuelLabs/sway/issues/2671. + Output::Change => 0, + // use `__gtf` when GTF_OUTPUT_VARIABLE_AMOUNT is available. + // See https://github.com/FuelLabs/fuel-specs/issues/402 + // and https://github.com/FuelLabs/sway/issues/2671. + Output::Variable => { + let ptr = output_pointer(index); + asm(r1, r2, r3: ptr) { + addi r2 r3 i40; + lw r1 r2 i0; + r1: u64 + } + }, + } +} + +/// Gets the AssetId of the output if it is a `Output::Coin`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the output to get the AssetId of. +/// +/// # Returns +/// +/// * [Option] - The AssetId of the output if it is a `Output::Coin`. None otherwise. +/// +/// # Reverts +/// +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_asset_id; +/// +/// fn foo() { +/// let output_asset_id = output_asset_id(0); +/// log(output_asset_id); +/// } +/// ``` +pub fn output_asset_id(index: u64) -> Option { + match output_type(index) { + Output::Coin => Some(AssetId::from(__gtf::(index, GTF_OUTPUT_COIN_ASSET_ID))), + _ => None, + } +} + +// TODO: Update to `Identity` when https://github.com/FuelLabs/sway/issues/4569 is resolved +/// Returns the reciever of the output if it is a `Output::Coin`. Represents the reciever as a `b256`. +/// +/// # Arguments +/// +/// * `index`: [u64] - The index of the output to get the reciever of. +/// +/// # Returns +/// +/// * [Option] - The reciever of the output if it is a `Output::Coin`. None otherwise. +/// +/// # Reverts +/// +/// * When the output type is unrecognized. This should never happen. +/// +/// # Examples +/// +/// ```sway +/// use std::outputs::output_asset_to; +/// +/// fn foo() { +/// let output_reciever = output_asset_to(0); +/// log(output_reciever); +/// } +/// ``` +pub fn output_asset_to(index: u64) -> Option { + match output_type(index) { + Output::Coin => Some(__gtf::(index, GTF_OUTPUT_COIN_TO)), + _ => None, + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/prelude.sw b/sway-lsp/tests/fixtures/fixtures-std/src/prelude.sw new file mode 100644 index 00000000000..b96483e7d6e --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/prelude.sw @@ -0,0 +1,35 @@ +//! Defines the Sway standard library prelude. +//! The prelude consists of implicitly available items, +//! for which `use` is not required. +library; + +// Blockchain types +use ::address::Address; +use ::alias::SubId; +use ::contract_id::{AssetId, ContractId}; +use ::identity::Identity; + +// `StorageKey` API +use ::storage::storage_key::*; + +// Collections +use ::storage::storage_map::*; +use ::vec::Vec; + +// Error handling +use ::assert::{assert, assert_eq}; +use ::option::Option::{self, *}; +use ::result::Result::{self, *}; +use ::revert::{require, revert}; + +// Convert +use ::convert::From; + +// Primitive conversions +use ::primitive_conversions::*; + +// Logging +use ::logging::log; + +// Auth +use ::auth::msg_sender; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions.sw new file mode 100644 index 00000000000..99d170048fa --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions.sw @@ -0,0 +1,8 @@ +library; + +pub mod b256; +pub mod r#str; +pub mod u8; +pub mod u16; +pub mod u32; +pub mod u64; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/b256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/b256.sw new file mode 100644 index 00000000000..19da87cc854 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/b256.sw @@ -0,0 +1,49 @@ +library; + +use ::bytes::Bytes; +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl TryFrom for b256 { + fn try_from(b: Bytes) -> Option { + if b.len() > 32 { + None + } else { + let mut val = 0x0000000000000000000000000000000000000000000000000000000000000000; + let ptr = __addr_of(val); + b.buf.ptr().copy_to::(ptr, 1); + Some(val) + } + } +} + +#[test] +fn test_b256_try_from() { + use ::assert::assert; + + let mut initial_bytes = Bytes::with_capacity(32); + let mut i = 0; + while i < 32 { + // 0x33 is 51 in decimal + initial_bytes.push(51u8); + i += 1; + } + let res = b256::try_from(initial_bytes); + let expected = 0x3333333333333333333333333333333333333333333333333333333333333333; + + assert(res.unwrap() == expected); + + let mut second_bytes = Bytes::with_capacity(33); + i = 0; + while i < 33 { + // 0x33 is 51 in decimal + second_bytes.push(51u8); + i += 1; + } + let res = b256::try_from(second_bytes); + assert(res.is_none()); + + // bytes is still available to use: + assert(second_bytes.len() == 33); + assert(second_bytes.capacity() == 33); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/str.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/str.sw new file mode 100644 index 00000000000..7a590424a90 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/str.sw @@ -0,0 +1,38 @@ +library; + +use ::option::Option::{self, *}; + +impl str { + pub fn try_as_str_array(self) -> Option { + __assert_is_str_array::(); + let str_size = __size_of_str_array::(); + let source = self.as_ptr(); + + if self.len() == str_size { + let s: S = asm(str_size: str_size, source: source, dest) { + move dest sp; + cfe str_size; + mcp dest source str_size; + dest: S + }; + asm (str_size: str_size) { + cfs str_size; + } + Some(s) + } else { + None + } + } +} + +#[test] +fn str_slice_to_str_array() { + use ::assert::*; + use core::str::*; + + let a = "abcd"; + let b: str[4] = a.try_as_str_array().unwrap(); + let c = from_str_array(b); + + assert(a == c); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u16.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u16.sw new file mode 100644 index 00000000000..1c3a0aba42f --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u16.sw @@ -0,0 +1,96 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u16 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u16() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u32) -> Option { + if u > u16::max().as_u32() { + None + } else { + Some(asm(r1: u) {r1: u16}) + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u64) -> Option { + if u > u16::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u16}) + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u16::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u16}) + } + } +} + +#[test] +fn test_u16_try_from_u32() { + use ::assert::assert; + + let u32_1: u32 = 2u32; + let u32_2: u32 = u16::max().as_u32() + 1; + + let u16_1 = >::try_from(u32_1); + let u16_2 = >::try_from(u32_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} + +#[test] +fn test_u16_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u16::max().as_u64() + 1; + + let u16_1 = >::try_from(u64_1); + let u16_2 = >::try_from(u64_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} + +#[test] +fn test_u16_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u16_1 = >::try_from(u256_1); + let u16_2 = >::try_from(u256_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u32.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u32.sw new file mode 100644 index 00000000000..fe6c33cf33c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u32.sw @@ -0,0 +1,81 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u32 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u32() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } + + pub fn try_as_u16(self) -> Option { + if self <= u16::max().as_u32() { + Some(asm(input: self) { + input: u16 + }) + } else { + None + } + } +} + + +impl TryFrom for u32 { + fn try_from(u: u64) -> Option { + if u > u32::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u32}) + } + } +} + +impl TryFrom for u32 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u32::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u32}) + } + } +} + +#[test] +fn test_u32_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u32::max().as_u64() + 1; + + let u32_1 = >::try_from(u64_1); + let u32_2 = >::try_from(u64_2); + + assert(u32_1.is_some()); + assert(u32_1.unwrap() == 2u32); + + assert(u32_2.is_none()); +} + +#[test] +fn test_u32_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u32_1 = >::try_from(u256_1); + let u32_2 = >::try_from(u256_2); + + assert(u32_1.is_some()); + assert(u32_1.unwrap() == 2u32); + + assert(u32_2.is_none()); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u64.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u64.sw new file mode 100644 index 00000000000..c7745fc778e --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u64.sw @@ -0,0 +1,64 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u64 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u64() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } + + pub fn try_as_u16(self) -> Option { + if self <= u16::max().as_u64() { + Some(asm(input: self) { + input: u16 + }) + } else { + None + } + } + + pub fn try_as_u32(self) -> Option { + if self <= u32::max().as_u64() { + Some(asm(input: self) { + input: u32 + }) + } else { + None + } + } +} + +impl TryFrom for u64 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 { + None + } else { + Some(parts.3) + } + } +} + +#[test] +fn test_u64_try_from_u256() { + use ::assert::assert; + + let u256_1 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u64_1 = u64::try_from(u256_1); + let u64_2 = u64::try_from(u256_2); + + assert(u64_1.is_some()); + assert(u64_1.unwrap() == 2); + + assert(u64_2.is_none()); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u8.sw b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u8.sw new file mode 100644 index 00000000000..e1ef5d86b9f --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/primitive_conversions/u8.sw @@ -0,0 +1,110 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl TryFrom for u8 { + fn try_from(u: u16) -> Option { + if u > u8::max().as_u16() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u32) -> Option { + if u > u8::max().as_u32() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u64) -> Option { + if u > u8::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u8::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u8}) + } + } +} + +#[test] +fn test_u8_try_from_u16() { + use ::assert::assert; + + let u16_1: u16 = 2u16; + let u16_2: u16 = u8::max().as_u16() + 1; + + let u8_1 = >::try_from(u16_1); + let u8_2 = >::try_from(u16_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u32() { + use ::assert::assert; + + let u32_1: u32 = 2u32; + let u32_2: u32 = u16::max().as_u32() + 1; + + let u8_1 = >::try_from(u32_1); + let u8_2 = >::try_from(u32_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u16::max().as_u64() + 1; + + let u8_1 = >::try_from(u64_1); + let u8_2 = >::try_from(u64_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u8_1 = >::try_from(u256_1); + let u8_2 = >::try_from(u256_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/registers.sw b/sway-lsp/tests/fixtures/fixtures-std/src/registers.sw new file mode 100644 index 00000000000..6431654714b --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/registers.sw @@ -0,0 +1,317 @@ +//! Functions to expose 14 of the reserved FuelVM registers for ease of use. +//! Ref: https://fuellabs.github.io/fuel-specs/master/vm#semantics +library; + +/// Contains overflow & underflow of addition, subtraction, and multiplication. +/// +/// # Addtional Information +/// +/// In order to use this function, panic on overflow must be disabled. +/// +/// # Returns +/// +/// * [u64] - The overflow or underflow remaining value. +/// +/// # Examples +/// +/// ```sway +/// use std::{registers::overflow, flags::{disable_panic_on_overflow, enable_panic_on_overflow}}; +/// +/// fn foo() { +/// disable_panic_on_overflow(); +/// let max = u64::max(); +/// let result = max + 1; +/// let overflow_val = overflow(); +/// +/// assert(result == 0); +/// assert(overflow_val == 1); +/// enable_panic_on_overflow(); +/// } +/// ``` +pub fn overflow() -> u64 { + asm() { of } +} + +/// The program counter. Memory address of the current instruction. +/// +/// # Returns +/// +/// * [raw_ptr] - The location in memory of the current instruction. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::program_counter; +/// +/// fn foo() { +/// let pc = program_counter(); +/// assert(!pc.is_null()); +/// } +/// ``` +pub fn program_counter() -> raw_ptr { + asm() { pc: raw_ptr } +} + +/// Memory address of bottom of current writable stack area. +/// +/// # Returns +/// +/// * [raw_ptr] - The location in memory of the bottom of the stack. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::stack_start_ptr; +/// +/// fn foo() { +/// let ssp = stack_start_ptr(); +/// assert(!ssp.is_null()); +/// } +/// ``` +pub fn stack_start_ptr() -> raw_ptr { + asm() { ssp: raw_ptr } +} + +/// Memory address on top of current writable stack area (points to free memory). +/// +/// # Returns +/// +/// * [raw_ptr] - The location in memory of the top of the stack. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::stack_ptr; +/// +/// fn foo() { +/// let sp = stack_ptr(); +/// assert(!sp.is_null()); +/// } +/// ``` +pub fn stack_ptr() -> raw_ptr { + asm() { sp: raw_ptr } +} + +/// Memory address of beginning of current call frame. +/// +/// # Returns +/// +/// * [raw_ptr] - The location in memory of the start of the call frame. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::frame_ptr; +/// +/// fn foo() { +/// let fp = frame_ptr(); +/// assert(!fp.is_null()); +/// } +/// ``` +pub fn frame_ptr() -> raw_ptr { + asm() { fp: raw_ptr } +} + +/// Memory address below the current bottom of the heap (points to free memory). +/// +/// # Returns +/// +/// * [raw_ptr] - The location in memory of the bottom of the heap. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::heap_ptr; +/// +/// fn foo() { +/// let hp = heap_ptr(); +/// assert(!hp.is_null()); +/// } +/// ``` +pub fn heap_ptr() -> raw_ptr { + asm() { hp: raw_ptr } +} + +/// Error codes for particular operations. +/// +/// # Additional Information +/// +/// Normally, if the result of an ALU operation is mathematically undefined (e.g. dividing by zero), the VM Reverts. +/// However, if the `F_UNSAFEMATH` flag is set, $err is set to `true` and execution continues. +/// +/// # Returns +/// +/// * [u64] - A VM error code. +/// +/// # Examples +/// +/// ```sway +/// use std::{registers::error, flags::{disable_panic_on_unsafe_math, enable_panic_on_unsafe_math}}; +/// +/// fn foo() { +/// disable_panic_on_unsafe_math(); +/// let bar = 1 / 0; +/// assert(error() == 1); +/// enable_panic_on_unsafe_math(); +/// } +/// ``` +pub fn error() -> u64 { + asm() { err } +} + +/// Remaining gas globally. +/// +/// # Returns +/// +/// * [u64] - The remaining gas. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::global_gas; +/// +/// fn foo() { +/// let gas = global_gas(); +/// assert(gas != 0); +/// bar(); +/// +/// let gas_2 = global_gas(); +/// assert(gas_2 < gas); +/// } +/// +/// fn bar() { +/// let val = 0; +/// } +/// ``` +pub fn global_gas() -> u64 { + asm() { ggas } +} + +/// Remaining gas in the context. +/// +/// # Returns +/// +/// * [u64] - The remaining gas for the curren context. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::context_gas; +/// +/// fn foo() { +/// let gas = context_gas(); +/// let gas_2 = bar(); +/// assert(gas_2 < gas); +/// } +/// +/// fn bar() -> u64 { +/// context_gas(); +/// } +/// ``` +pub fn context_gas() -> u64 { + asm() { cgas } +} + +/// Get the amount of units of `call_frames::msg_asset_id()` being sent. +/// +/// # Returns +/// +/// * [u64] - The forwarded coins in the context. +/// +/// # Examples +/// ```sway +/// use std::register::balance; +/// +/// fn foo() { +/// let bal = balance(); +/// assert(bal == 0); +/// } +/// ``` +pub fn balance() -> u64 { + asm() { bal } +} + +/// Pointer to the start of the currently-executing code. +/// +/// # Returns +/// +/// * [raw_ptr] - The memory location of the start of the currently-executing code. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::instrs_start; +/// +/// fn foo() { +/// let is = instrs_start(); +/// assert(!is.is_null()); +/// } +/// ``` +pub fn instrs_start() -> raw_ptr { + asm() { is: raw_ptr } +} + +/// Return value or pointer. +/// +/// # Returns +/// +/// * [u64] - The value or pointer stored in the return register of the VM for the current context. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::return_value; +/// +/// fn foo() { +/// let ret = return_value(); +/// assert(ret == 0); +/// } +/// ``` +pub fn return_value() -> u64 { + asm() { ret } +} + +/// Return value length in bytes. +/// +/// # Returns +/// +/// * [u64] - The length in bytes of the value stored in the return register of the VM for the current context. +/// +/// # Examples +/// +/// ```sway +/// use std::registers::return_length; +/// +/// fn foo() { +/// let ret = return_length(); +/// assert(ret == 0); +/// } +/// ``` +pub fn return_length() -> u64 { + asm() { retl } +} + +/// Flags register. +/// +/// # Returns +/// +/// * [u64] - The current flags set within the VM. +/// +/// # Examples +/// +/// ```sway +/// use std::{registers::flags, flags::disable_panic_on_overflow}; +/// +/// const F_WRAPPING_DISABLE_MASK: u64 = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000010; +/// +/// fn foo() { +/// let flag = flags(); +/// assert(flag == 0); +/// disable_panic_on_overflow(); +/// let flag_2 = flags(); +/// assert(flag_2 == F_WRAPPING_DISABLE_MASK); +/// } +/// ``` +pub fn flags() -> u64 { + asm() { flag } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/result.sw b/sway-lsp/tests/fixtures/fixtures-std/src/result.sw new file mode 100644 index 00000000000..3b1c9a4909c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/result.sw @@ -0,0 +1,210 @@ +//! Error handling with the `Result` type. +//! +//! `Result` `Result` is the type used for returning and propagating +//! errors. It is an enum with the variants, `Ok(T)`, representing +//! success and containing a value, and `Err(E)`, representing error +//! and containing an error value. +//! +//! Functions return `Result` whenever errors are expected and recoverable. In +//! the `std` crate, `Result` is most prominently used for `Identity` +//! interactions and cryptographic operations. +//! +//! A simple function returning `Result` might be defined and used like so: +//! +//! ``` +//! enum Version { +//! Version1, +//! Version2, +//! } +//! +//! enum VersionError { +//! InvalidNumber, +//! } +//! +//! fn parse_version(version_number: u8) -> Result { +//! match version_number { +//! 1 => Ok(Version::Version1), +//! 2 => Ok(Version::Version2), +//! _ => Err(VersionError::InvalidNumber), +//! } +//! } +//! ``` +//! +//! ### Method overview +//! +//! In addition to working with pattern matching, `Result` provides a variety +//! of methods. +//! +//! ### Querying the variant +//! +//! The `is_ok` and `is_err` methods return `true` if the `Result` is +//! `Ok` or `Err`, respectively. +//! +//! `is_ok` : `Result::is_ok` +//! `is_err`: `Result::is_err` +//! +//! ### Extracting the contained value +//! +//! These methods exctract the contained value in a `Result` when it is +//! the `Ok` variant. If the `Result` is `Err`: +//! +//! * `unwrap` reverts. +//! * `unwrap_or` returns the default provided value. +//! +//! `unwrap` : `Result::unwrap` +//! `unwrap_or`: `Result::unwrap_or` +library; + +use ::revert::revert; + +// ANCHOR: docs_result +/// `Result` is a type that represents either success (`Ok`) or failure (`Err`). +pub enum Result { + /// Contains the success value. + Ok: T, + /// Contains the error value. + Err: E, +} +// ANCHOR_END: docs_result + + +// Type implementation +// +impl Result { + // Querying the contained values + // + /// Returns whether a result contains a success value. + /// + /// # Returns + /// + /// * [bool] - Returns `true` if the result is `Ok`. + /// + /// # Examples + /// + /// ```sway + /// enum Error { + /// NotFound, + /// Invalid, + /// } + /// + /// fn foo() { + /// let x: Result = Result::Ok(42); + /// assert(x.is_ok()); + /// + /// let y: Result = Result::Err(Error::NotFound)); + /// assert(!y.is_ok()); + /// } + /// ``` + pub fn is_ok(self) -> bool { + match self { + Self::Ok(_) => true, + _ => false, + } + } + + /// Returns whether a result contains an error value. + /// + /// # Returns + /// + /// * [bool] - Returns `true` if the result is `Err`. + /// + /// # Examples + /// + /// ```sway + /// enum Error { + /// NotFound, + /// Invalid, + /// } + /// + /// fn foo() { + /// let x: Result = Result::Ok(42); + /// assert(!x.is_err()); + /// + /// let y: Result = Result::Err(Error::NotFound)); + /// assert(y.is_err()); + /// } + /// ``` + pub fn is_err(self) -> bool { + match self { + Self::Ok(_) => false, + _ => true, + } + } + + /// Returns the contained `Ok` value, consuming the `self` value. + /// + /// # Additional Information + /// + /// Because this function may revert, its use is generally discouraged. + /// Instead, prefer to use pattern matching and handle the `Err` + /// case explicitly. + /// + /// # Returns + /// + /// * [T] - The value contained by the result. + /// + /// # Reverts + /// + /// * Reverts if the `Result` is the `Err` variant. + /// + /// # Examples + /// + /// ```sway + /// enum Error { + /// NotFound, + /// Invalid, + /// } + /// + /// fn foo() { + /// let x: Result = Result::Ok(42); + /// assert(x.unwrap() == 42); + /// + /// let y: Result = Result::Err(Error::NotFound)); + /// let val = y.unwrap(); // reverts + /// } + /// ``` + pub fn unwrap(self) -> T { + match self { + Self::Ok(inner_value) => inner_value, + _ => revert(0), + } + } + + /// Returns the contained `Ok` value or a provided default. + /// + /// # Arguments + /// + /// * `default`: [T] - The value that is the default. + /// + /// # Returns + /// + /// * [T] - The value of the result or the default. + /// + /// # Examples + /// + /// ```sway + /// enum Error { + /// NotFound, + /// Invalid, + /// } + /// + /// fn foo() { + /// let x: Result = Result::Ok(42); + /// assert(x.unwrap_or(69) == 42); + /// + /// let y: Result = Result::Err(Error::NotFound)); + /// assert(y.unwrap_or(69) == 69); + /// } + /// ``` + pub fn unwrap_or(self, default: T) -> T { + match self { + Self::Ok(inner_value) => inner_value, + Self::Err(_) => default, + } + } + + // TODO: Implement the following transforms when Option and Result can + // import one another: + // - `ok(self) -> Option` + // - `err(self) -> Option` +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/revert.sw b/sway-lsp/tests/fixtures/fixtures-std/src/revert.sw new file mode 100644 index 00000000000..a64a9a0acba --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/revert.sw @@ -0,0 +1,61 @@ +//! Functions to panic or revert with a given error code. +library; + +use ::logging::log; +use ::error_signals::FAILED_REQUIRE_SIGNAL; + +/// Will either panic or revert with a given number depending on the context. +/// +/// # Additional Information +/// +/// If used in a predicate, it will panic. +/// If used in a contract, it will revert. +/// +/// # Arguments +/// +/// * `code`: [u64] - The code with which to revert the program. +/// +/// # Reverts +/// +/// * Reverts unconditionally. +/// +/// # Examples +/// +/// ```sway +/// fn foo(should_revert: bool) { +/// match should_revert { +/// true => revert(0), +/// false => {}, +/// } +/// } +/// ``` +pub fn revert(code: u64) { + __revert(code) +} + +/// Checks if the given `condition` is `true` and if not, logs `value` and reverts. +/// +/// # Arguments +/// +/// * `condition`: [bool] - The condition upon which to decide whether to revert or not. +/// * `value`: [T] - The value which will be logged in case `condition` is `false`. +/// +/// # Reverts +/// +/// * Reverts when `condition` is `false`. +/// +/// # Examples +/// +/// ```sway +/// fn foo(a: u64, b: u64) { +/// require(a == b, "a was not equal to b"); +/// // If the condition was true, code execution will continue +/// log("The require function did not revert"); +/// } +/// ``` +pub fn require(condition: bool, value: T) { + if !condition { + log(value); + revert(FAILED_REQUIRE_SIGNAL) + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage.sw new file mode 100644 index 00000000000..87fcd8b5cad --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage.sw @@ -0,0 +1,10 @@ +//! Contract storage utilities. +library; + +pub mod storage_api; +pub mod storage_key; +pub mod storable_slice; +pub mod storage_map; +pub mod storage_vec; +pub mod storage_bytes; +pub mod storage_string; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storable_slice.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storable_slice.sw new file mode 100644 index 00000000000..a1f9dd16ed6 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storable_slice.sw @@ -0,0 +1,144 @@ +library; + +use ::alloc::{alloc, alloc_bytes, realloc_bytes}; +use ::hash::*; +use ::option::Option::{self, *}; +use ::storage::storage_api::*; + +/// Store a raw_slice from the heap into storage. +/// +/// # Arguments +/// +/// * `key`: [b256] - The storage slot at which the variable will be stored. +/// * `slice`: [raw_slice] - The raw_slice to be stored. +/// +/// # Number of Storage Accesses +/// +/// * Writes: `2` +/// +/// # Examples +/// +/// ```sway +/// use std::{alloc::alloc_bytes, storage::{write_slice, read_slice}, constants::ZERO_B256}; +/// +/// fn foo() { +/// let slice = asm(ptr: (alloc_bytes(1), 1)) { ptr: raw_slice }; +/// assert(read_slice(ZERO_B256).is_none()); +/// write_slice(ZERO_B256, slice); +/// let stored_slice = read_slice(ZERO_B256).unwrap(); +/// assert(slice == stored_slice); +/// } +/// ``` +#[storage(read, write)] +pub fn write_slice(key: b256, slice: raw_slice) { + // Get the number of storage slots needed based on the size of bytes. + let number_of_bytes = slice.number_of_bytes(); + let number_of_slots = (number_of_bytes + 31) >> 5; + let mut ptr = slice.ptr(); + + // The capacity needs to be a multiple of 32 bytes so we can + // make the 'quad' storage instruction store without accessing unallocated heap memory. + ptr = realloc_bytes(ptr, number_of_bytes, number_of_slots * 32); + + // Store `number_of_slots * 32` bytes starting at storage slot `key`. + let _ = __state_store_quad(sha256(key), ptr, number_of_slots); + + // Store the length of the bytes + write(key, 0, number_of_bytes); +} + +/// Load a raw_slice from storage. +/// +/// # Arguments +/// +/// * `key`: [b256] - The storage slot to load the value from. +/// +/// # Returns +/// +/// - [Option] - If no value was previously stored at `key`, `None` is returned. Otherwise, +/// `Some(value)` is returned, where `value` is the value stored at `key`. +/// +/// # Number of Storage Accesses +/// +/// * Reads: `2` +/// +/// # Examples +/// +/// ```sway +/// use std::{alloc::alloc_bytes, storage::{write_slice, read_slice}, constants::ZERO_B256}; +/// +/// fn foo { +/// let slice = asm(ptr: (alloc_bytes(1), 1)) { ptr: raw_slice }; +/// assert(read_slice(ZERO_B256).is_none()); +/// write_slice(ZERO_B256, slice); +/// let stored_slice = read_slice(ZERO_B256).unwrap(); +/// assert(slice == stored_slice); +/// } +/// ``` +#[storage(read)] +pub fn read_slice(key: b256) -> Option { + // Get the length of the slice that is stored. + match read::(key, 0).unwrap_or(0) { + 0 => None, + len => { + // Get the number of storage slots needed based on the size. + let number_of_slots = (len + 31) >> 5; + let ptr = alloc_bytes(number_of_slots * 32); + // Load the stored slice into the pointer. + let _ = __state_load_quad(sha256(key), ptr, number_of_slots); + Some(asm(ptr: (ptr, len)) { ptr: raw_slice }) + } + } +} + +/// Clear a sequence of storage slots starting at a some key. +/// +/// # Arguments +/// +/// * `key`: [b256] - The key of the first storage slot that will be cleared +/// +/// # Returns +/// +/// * [bool] - Indicates whether all of the storage slots cleared were previously set. +/// +/// # Number of Storage Accesses +/// +/// * Reads: `1` +/// * Clears: `2` +/// +/// # Examples +/// +/// ```sway +/// use std::{alloc::alloc_bytes, storage::{clear_slice, write_slice, read_slice}, constants::ZERO_B256}; +/// +/// fn foo() { +/// let slice = asm(ptr: (alloc_bytes(1), 1)) { ptr: raw_slice }; +/// write_slice(ZERO_B256, slice); +/// assert(read_slice(ZERO_B256).is_some()); +/// let cleared = clear_slice(ZERO_B256); +/// assert(cleared); +/// assert(read_slice(ZERO_B256).is_none()); +/// } +/// ``` +#[storage(read, write)] +pub fn clear_slice(key: b256) -> bool { + // Get the number of storage slots needed based on the ceiling of `len / 32` + let len = read::(key, 0).unwrap_or(0); + let number_of_slots = (len + 31) >> 5; + + // Clear length and `number_of_slots` bytes starting at storage slot `sha256(key)` + let _ = __state_clear(key, 1); + __state_clear(sha256(key), number_of_slots) +} + +/// A general way to persistently store heap types. +pub trait StorableSlice { + #[storage(read, write)] + fn write_slice(self, argument: T); + #[storage(read)] + fn read_slice(self) -> Option; + #[storage(read, write)] + fn clear(self) -> bool; + #[storage(read)] + fn len(self) -> u64; +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_api.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_api.sw new file mode 100644 index 00000000000..3b5e1b5ea92 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_api.sw @@ -0,0 +1,183 @@ +library; + +use ::alloc::alloc; +use ::option::Option::{self, *}; + +/// Stores a stack value in storage. Will not work for heap values. +/// +/// # Additional Information +/// +/// If the value crosses the boundary of a storage slot, writing continues at the following slot. +/// +/// # Arguments +/// +/// * `slot`: [b256] - The storage slot at which the variable will be stored. +/// * `offset`: [u64] - An offset starting at the beginning of `slot` at which `value` should be stored. +/// * `value`: [T] - The value to be stored. +/// +/// # Number of Storage Accesses +/// +/// * Reads: `1` +/// * Writes: `1` +/// +/// # Examples +/// +/// ```sway +/// use std::{storage::storage_api::{read, write}, constants::ZERO_B256}; +/// +/// fn foo() { +/// let five = 5_u64; +/// write(ZERO_B256, 2, five); +/// let stored_five = read::(ZERO_B256, 2).unwrap(); +/// assert(five == stored_five); +/// } +/// ``` +#[storage(read, write)] +pub fn write(slot: b256, offset: u64, value: T) { + if __size_of::() == 0 { + return; + } + + // Determine how many slots and where the value is to be stored. + let (offset_slot, number_of_slots, place_in_slot) = slot_calculator::(slot, offset); + + // Allocate enough memory on the heap for `value` as well as any potential padding required due + // to `offset`. + let padded_value = alloc::(number_of_slots * 32); + + // Read the values that currently exist in the affected storage slots. + let _ = __state_load_quad(offset_slot, padded_value, number_of_slots); + + // Copy the value to be stored to `padded_value + offset`. + padded_value.add::(place_in_slot).write::(value); + + // Now store back the data at `padded_value` which now contains the old data but partially + // overwritten by the new data in the desired locations. + let _ = __state_store_quad(offset_slot, padded_value, number_of_slots); +} + +/// Reads a value of type `T` starting at the location specified by `slot` and `offset`. If the +/// value crosses the boundary of a storage slot, reading continues at the following slot. +/// +/// # Arguments +/// +/// * `slot`: [b256] - The storage slot to load the value from. +/// * `offset`: [u64] - An offset, in words, from the start of `slot`, from which the value should be read. +/// +/// # Returns +/// +/// * [Option] - `Option(value)` if the storage slots read were valid and contain `value`. Otherwise, +/// returns `None`. +/// +/// # Number of Storage Accesses +/// +/// * Reads: `1` +/// +/// # Examples +/// +/// ```sway +/// use std::{storage::storage_api::{read, write}, constants::ZERO_B256}; +/// +/// fn foo() { +/// let five = 5_u64; +/// write(ZERO_B256, 2, five); +/// let stored_five = read::(ZERO_B256, 2); +/// assert(five == stored_five.unwrap()); +/// } +/// ``` +#[storage(read)] +pub fn read(slot: b256, offset: u64) -> Option { + if __size_of::() == 0 { + return None; + } + + // Determine how many slots and where the value is to be read. + let (offset_slot, number_of_slots, place_in_slot) = slot_calculator::(slot, offset); + + // Allocate a buffer for the result. Its size needs to be a multiple of 32 bytes so we can + // make the 'quad' storage instruction read without overflowing. + let result_ptr = alloc::(number_of_slots * 32); + + // Read `number_of_slots * 32` bytes starting at storage slot `slot` and return an `Option` + // wrapping the value stored at `result_ptr + offset` if all the slots are valid. Otherwise, + // return `None`. + if __state_load_quad(offset_slot, result_ptr, number_of_slots) { + Some(result_ptr.add::(place_in_slot).read::()) + } else { + None + } +} + +/// Clear a value starting at some slot with an offset. +/// +/// # Arguments +/// +/// * `slot` - The key of the stored value that will be cleared +/// * `offset` - An offset, in words, from the start of `slot`, from which the value should be cleared. +/// +/// # Number of Storage Accesses +/// +/// * Clears: `1` +/// +/// # Examples +/// +/// ```sway +/// use std::{storage::storage_api::{read, write, clear}, constants::ZERO_B256}; +/// +/// fn foo() { +/// let five = 5_u64; +/// write(ZERO_B256, 0, five); +/// let cleared = clear::(ZERO_B256); +/// assert(cleared); +/// assert(read::(ZERO_B256, 0).is_none()); +/// } +/// ``` +#[storage(write)] +pub fn clear(slot: b256, offset: u64) -> bool { + if __size_of::() == 0 { + return true; + } + + // Determine how many slots and where the value is to be cleared. + let (offset_slot, number_of_slots, _place_in_slot) = slot_calculator::(slot, offset); + + // Clear `number_of_slots * 32` bytes starting at storage slot `slot`. + __state_clear(offset_slot, number_of_slots) +} + +/// Given a slot, offset, and type this function determines where something should be stored. +/// +/// # Arguments +/// +/// * `slot`: [b256] - The starting address at which something should be stored. +/// * `offset`: [u64] - The offset from `slot` to store the value. +/// +/// # Returns +/// +/// [b256] - The calculated offset slot to store the value. +/// [u64] - The number of slots the value will occupy in storage. +/// [u64] - The word in the slot where the value will start. +fn slot_calculator(slot: b256, offset: u64) -> (b256, u64, u64) { + let size_of_t = __size_of::(); + + // Get the last storage slot needed based on the size of `T`. + // ((offset * bytes_in_word) + bytes + (bytes_in_slot - 1)) >> align_to_slot = last slot + let last_slot = ((offset * 8) + size_of_t + 31) >> 5; + + // Where in the storage slot to align `T` in order to pack word-aligned. + // offset % number_words_in_slot = word_place_in_slot + let place_in_slot = offset % 4; + + // Get the number of slots `T` spans based on its packed position. + // ((place_in_slot * bytes_in_word) + bytes + (bytes_in_slot - 1)) >> align_to_slot = number_of_slots + let number_of_slots = match __is_reference_type::() { + true => ((place_in_slot * 8) + size_of_t + 31) >> 5, + false => 1, + }; + + // Determine which starting slot `T` will be stored based on the offset. + let mut offset_slot = slot.as_u256(); + offset_slot += last_slot.as_u256() - number_of_slots.as_u256(); + + (offset_slot.as_b256(), number_of_slots, place_in_slot) +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_bytes.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_bytes.sw new file mode 100644 index 00000000000..14a00e3c770 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_bytes.sw @@ -0,0 +1,159 @@ +library; + +use ::bytes::Bytes; +use ::option::Option::{self, *}; +use ::storage::storable_slice::*; +use ::storage::storage_api::*; + +/// A persistent storage type to store a collection of tightly packed bytes. +pub struct StorageBytes {} + +impl StorableSlice for StorageKey { + /// Takes a `Bytes` type and stores the underlying collection of tightly packed bytes. + /// + /// # Arguments + /// + /// * `bytes`: [Bytes] - The bytes which will be stored. + /// + /// # Number of Storage Accesses + /// + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_bytes::StorageBytes, bytes::Bytes}; + /// + /// storage { + /// stored_bytes: StorageBytes = StorageBytes {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// + /// storage.stored_bytes.write_slice(bytes); + /// } + /// ``` + #[storage(read, write)] + fn write_slice(self, bytes: Bytes) { + write_slice(self.field_id, bytes.as_raw_slice()); + } + + /// Constructs a `Bytes` type from a collection of tightly packed bytes in storage. + /// + /// # Returns + /// + /// * [Option] - The valid `Bytes` stored, otherwise `None`. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_bytes::StorageBytes, bytes::Bytes}; + /// + /// storage { + /// stored_bytes: StorageBytes = StorageBytes {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// + /// assert(storage.stored_bytes.read_slice(key).is_none()); + /// storage.stored_bytes.write_slice(bytes); + /// let retrieved_bytes = storage.stored_bytes.read_slice(key).unwrap(); + /// assert(bytes == retrieved_bytes); + /// } + /// ``` + #[storage(read)] + fn read_slice(self) -> Option { + match read_slice(self.field_id) { + Some(slice) => { + Some(Bytes::from(slice)) + }, + None => None, + } + } + + /// Clears a collection of tightly packed bytes in storage. + /// + /// # Returns + /// + /// * [bool] - Indicates whether all of the storage slots cleared were previously set. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Clears: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_bytes::StorageBytes, bytes::Bytes}; + /// + /// storage { + /// stored_bytes: StorageBytes = StorageBytes {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// storage.stored_bytes.write_slice(bytes); + /// + /// assert(storage.stored_bytes.read_slice(key).is_some()); + /// let cleared = storage.stored_bytes.clear(); + /// assert(cleared); + /// let retrieved_bytes = storage.stored_bytes.read_slice(key); + /// assert(retrieved_bytes.is_none()); + /// } + /// ``` + #[storage(read, write)] + fn clear(self) -> bool { + clear_slice(self.field_id) + } + + /// Returns the length of tightly packed bytes in storage. + /// + /// # Returns + /// + /// * [u64] - The length of the bytes in storage. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_bytes::StorageBytes, bytes::Bytes}; + /// + /// storage { + /// stored_bytes: StorageBytes = StorageBytes {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// + /// assert(storage.stored_bytes.len() == 0) + /// storage.stored_bytes.write_slice(bytes); + /// assert(storage.stored_bytes.len() == 3); + /// } + /// ``` + #[storage(read)] + fn len(self) -> u64 { + read::(self.field_id, 0).unwrap_or(0) + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_key.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_key.sw new file mode 100644 index 00000000000..a4cb12f7d91 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_key.sw @@ -0,0 +1,139 @@ +library; + +use ::option::Option; +use ::storage::storage_api::*; + +impl StorageKey { + /// Reads a value of type `T` starting at the location specified by `self`. If the value + /// crosses the boundary of a storage slot, reading continues at the following slot. + /// + /// # Returns + /// + /// * [T] - Returns the value previously stored if a the storage slots read were + /// valid and contain `value`. Reverts otherwise. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let r: StorageKey = StorageKey { + /// slot: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// offset: 2, + /// field_id: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// }; + /// + /// // Reads the third word from storage slot with key 0x000...0 + /// let x: u64 = r.read(); + /// } + /// ``` + #[storage(read)] + pub fn read(self) -> T { + read::(self.slot, self.offset).unwrap() + } + + /// Reads a value of type `T` starting at the location specified by `self`. If the value + /// crosses the boundary of a storage slot, reading continues at the following slot. + /// + /// # Returns + /// + /// * [Option] - Returns `Some(value)` if a the storage slots read were valid and contain `value`. + /// Otherwise, return `None`. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let r: StorageKey = StorageKey { + /// slot: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// offset: 2, + /// field_id: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// }; + /// + /// // Reads the third word from storage slot with key 0x000...0 + /// let x: Option = r.try_read(); + /// } + /// ``` + #[storage(read)] + pub fn try_read(self) -> Option { + read(self.slot, self.offset) + } + + /// Writes a value of type `T` starting at the location specified by `self`. If the value + /// crosses the boundary of a storage slot, writing continues at the following slot. + /// + /// # Arguments + /// + /// * `value`: [T] - The value of type `T` to write. + /// + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Writes: `1` + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let r: StorageKey = StorageKey { + /// slot: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// offset: 2, + /// field_id: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// }; + /// + /// // Writes 42 at the third word of storage slot with key 0x000...0 + /// let x = r.write(42); + /// } + /// ``` + #[storage(read, write)] + pub fn write(self, value: T) { + write(self.slot, self.offset, value); + } + + /// Create a new `StorageKey`. + /// + /// # Arguments + /// + /// * `slot`: [b256] - The assigned location in storage for the new `StorageKey`. + /// * `offset`: [u64] - The assigned offset based on the data structure `T` for the new `StorageKey`. + /// * `field_id`: [b256] - A unique identifier for the new `StorageKey`. + /// + /// # Returns + /// + /// * [StorageKey] - The newly create `StorageKey`. + /// + /// # Examples + /// + /// ```sway + /// use std::{constants::ZERO_B256, hash::sha256}; + /// + /// fn foo() { + /// let my_key = StorageKey::::new(ZERO_B256, 0, sha256(ZERO_B256)); + /// assert(my_key.slot == ZERO_B256); + /// } + /// ``` + pub fn new(slot: b256, offset: u64, field_id: b256) -> Self { + Self { + slot, offset, field_id + } + } +} + +#[test] +fn test_storage_key_new() { + use ::constants::ZERO_B256; + use ::assert::assert; + + let key = StorageKey::::new(ZERO_B256, 0, ZERO_B256); + assert(key.slot == ZERO_B256); + assert(key.offset == 0); + assert(key.field_id == ZERO_B256); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_map.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_map.sw new file mode 100644 index 00000000000..b166f555f1c --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_map.sw @@ -0,0 +1,113 @@ +library; + +use ::hash::*; +use ::storage::storage_api::*; +use ::storage::storage_key::*; + +/// A persistent key-value pair mapping struct. +pub struct StorageMap where K: Hash {} + +impl StorageKey> where K: Hash { + /// Inserts a key-value pair into the map. + /// + /// # Arguments + /// + /// * `key`: [K] - The key to which the value is paired. + /// * `value`: [V] - The value to be stored. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Writes: `1` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// map: StorageMap = StorageMap {} + /// } + /// + /// fn foo() { + /// let key = 5_u64; + /// let value = true; + /// storage.map.insert(key, value); + /// let retrieved_value = storage.map.get(key).read(); + /// assert(value == retrieved_value); + /// } + /// ``` + #[storage(read, write)] + pub fn insert(self, key: K, value: V) where K: Hash { + let key = sha256((key, self.field_id)); + write::(key, 0, value); + } + + /// Retrieves the `StorageKey` that describes the raw location in storage of the value + /// stored at `key`, regardless of whether a value is actually stored at that location or not. + /// + /// # Arguments + /// + /// * `key`: [K] - The key to which the value is paired. + /// + /// # Returns + /// + /// * [StorageKey] - Describes the raw location in storage of the value stored at `key`. + /// + /// # Examples + /// + /// ```sway + /// storage { + /// map: StorageMap = StorageMap {} + /// } + /// + /// fn foo() { + /// let key = 5_u64; + /// let value = true; + /// storage.map.insert(key, value); + /// let retrieved_value = storage.map.get(key).read(); + /// assert(value == retrieved_value); + /// } + /// ``` + pub fn get(self, key: K) -> StorageKey where K: Hash { + StorageKey::::new( + sha256((key, self.field_id)), + 0, + sha256((key, self.field_id)) + ) + } + + /// Clears a value previously stored using a key + /// + /// # Arguments + /// + /// * `key`: [K] - The key to which the value is paired. + /// + /// # Returns + /// + /// * [bool] - Indicates whether there was a value previously stored at `key`. + /// + /// # Number of Storage Accesses + /// + /// * Clears: `1` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// map: StorageMap = StorageMap {} + /// } + /// + /// fn foo() { + /// let key = 5_u64; + /// let value = true; + /// storage.map.insert(key, value); + /// let removed = storage.map.remove(key); + /// assert(removed); + /// assert(storage.map.get(key).is_none()); + /// } + /// ``` + #[storage(write)] + pub fn remove(self, key: K) -> bool where K: Hash { + let key = sha256((key, self.slot)); + clear::(key, 0) + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_string.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_string.sw new file mode 100644 index 00000000000..89245055c69 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_string.sw @@ -0,0 +1,164 @@ +library; + +use ::bytes::Bytes; +use ::option::Option::{self, *}; +use ::storage::storable_slice::*; +use ::storage::storage_api::read; +use ::string::String; + +pub struct StorageString {} + +impl StorableSlice for StorageKey { + /// Takes a `String` type and saves the underlying data in storage. + /// + /// # Arguments + /// + /// * `string`: [String] - The string which will be stored. + /// + /// # Number of Storage Accesses + /// + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_string::StorageString, bytes::Bytes, string::String}; + /// + /// storage { + /// stored_string: StorageString = StorageString {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// let string = String::from(bytes); + /// + /// storage.stored_string.write_slice(string); + /// } + /// ``` + #[storage(read, write)] + fn write_slice(self, string: String) { + write_slice(self.slot, string.as_raw_slice()); + } + + /// Constructs a `String` type from storage. + /// + /// # Returns + /// + /// * [Option] - The valid `String` stored, otherwise `None`. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_string::StorageString, bytes::Bytes, string::String}; + /// + /// storage { + /// stored_string: StorageString = StorageString {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// let string = String::from(bytes); + /// + /// assert(storage.stored_string.read_slice(key).is_none()); + /// storage.stored_string.write_slice(string); + /// let retrieved_string = storage.stored_string.read_slice(key).unwrap(); + /// assert(string == retrieved_string); + /// } + /// ``` + #[storage(read)] + fn read_slice(self) -> Option { + match read_slice(self.slot) { + Some(slice) => { + Some(String::from(slice)) + }, + None => None, + } + } + + /// Clears a stored `String` in storage. + /// + /// # Returns + /// + /// * [bool] - Indicates whether all of the storage slots cleared were previously set. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// * Clears: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_string::StorageString, bytes::Bytes, string::String}; + /// + /// storage { + /// stored_string: StorageString = StorageString {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// let string = String::from(bytes); + /// + /// storage.stored_string.write_slice(string); + /// + /// assert(storage.stored_string.read_slice(key).is_some()); + /// let cleared = storage.stored_string.clear(); + /// assert(cleared); + /// let retrieved_string = storage.stored_string.read_slice(key); + /// assert(retrieved_string.is_none()); + /// } + /// ``` + #[storage(read, write)] + fn clear(self) -> bool { + clear_slice(self.slot) + } + + /// Returns the length of `String` in storage. + /// + /// # Returns + /// + /// * [u64] - The length of the bytes in storage. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::{storage::storage_string::StorageString, bytes::Bytes, string::String}; + /// + /// storage { + /// stored_string: StorageString = StorageString {} + /// } + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(5_u8); + /// bytes.push(7_u8); + /// bytes.push(9_u8); + /// let string = String::from(bytes); + /// + /// assert(storage.stored_string.len() == 0) + /// storage.stored_string.write_slice(string); + /// assert(storage.stored_string.len() == 3); + /// } + /// ``` + #[storage(read)] + fn len(self) -> u64 { + read::(self.slot, 0).unwrap_or(0) + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_vec.sw b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_vec.sw new file mode 100644 index 00000000000..d90a189d664 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/storage/storage_vec.sw @@ -0,0 +1,914 @@ +library; + +use ::alloc::{alloc_bytes, realloc_bytes}; +use ::assert::assert; +use ::hash::*; +use ::option::Option::{self, *}; +use ::storage::storage_api::*; +use ::storage::storage_key::*; +use ::vec::Vec; + +/// A persistant vector struct. +pub struct StorageVec {} + +impl StorageKey> { + /// Appends the value to the end of the vector. + /// + /// # Arguments + /// + /// * `value`: [V] - The item being added to the end of the vector. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `3` + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// let five = 5_u64; + /// storage.vec.push(five); + /// assert(five == storage.vec.get(0).unwrap()); + /// } + /// ``` + #[storage(read, write)] + pub fn push(self, value: V) { + let len = read::(self.field_id, 0).unwrap_or(0); + + // Storing the value at the current length index (if this is the first item, starts off at 0) + let key = sha256(self.field_id); + let offset = offset_calculator::(len); + write::(key, offset, value); + + // Incrementing the length + write(self.field_id, 0, len + 1); + } + + /// Removes the last element of the vector and returns it, `None` if empty. + /// + /// # Returns + /// + /// * [Option] - The last element `V` or `None`. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `3` + /// * Writes: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// let five = 5_u64; + /// storage.vec.push(five); + /// let popped_value = storage.vec.pop().unwrap(); + /// assert(five == popped_value); + /// let none_value = storage.vec.pop(); + /// assert(none_value.is_none()) + /// } + /// ``` + #[storage(read, write)] + pub fn pop(self) -> Option { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the length is 0, there is no item to pop from the vec + if len == 0 { + return None; + } + + // reduces len by 1, effectively removing the last item in the vec + write(self.field_id, 0, len - 1); + + let key = sha256(self.field_id); + let offset = offset_calculator::(len - 1); + read::(key, offset) + } + + /// Gets the value in the given index, `None` if index is out of bounds. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the vec to retrieve the item from. + /// + /// # Returns + /// + /// * [Option>] - Describes the raw location in storage of the value stored at + /// `key` or `None` if out of bounds. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// let five = 5_u64; + /// storage.vec.push(five); + /// assert(five == storage.vec.get(0).unwrap()); + /// assert(storage.vec.get(1).is_none()) + /// } + /// ``` + #[storage(read)] + pub fn get(self, index: u64) -> Option> { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the index is larger or equal to len, there is no item to return + if len <= index { + return None; + } + + let key = sha256(self.field_id); + let offset = offset_calculator::(index); + // This StorageKey can be read by the standard storage api. + // Field Id must be unique such that nested storage vecs work as they have a + // __size_of() zero and will there forefore always have an offset of zero. + Some(StorageKey::::new( + key, + offset, + sha256((index, key)) + )) + } + + /// Removes the element in the given index and moves all the elements in the following indexes + /// down one index. Also returns the element. + /// + /// # Additional Information + /// + /// **_WARNING:_** Expensive for larger vecs. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the vec to remove the item from. + /// + /// # Returns + /// + /// * [V] - The element that has been removed at the index. + /// + /// # Reverts + /// + /// * Reverts if index is larger or equal to length of the vec. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `3 + (2 * (self.len() - index))` + /// * Writes: `self.len() - index` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// let removed_value = storage.vec.remove(1); + /// assert(10 == removed_value); + /// assert(storage.vec.len() == 2); + /// } + /// ``` + #[storage(read, write)] + pub fn remove(self, index: u64) -> V { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the index is larger or equal to len, there is no item to remove + assert(index < len); + + // gets the element before removing it, so it can be returned + let key = sha256(self.field_id); + let removed_offset = offset_calculator::(index); + let removed_element = read::(key, removed_offset).unwrap(); + + // for every element in the vec with an index greater than the input index, + // shifts the index for that element down one + let mut count = index + 1; + while count < len { + // gets the storage location for the previous index and + // moves the element of the current index into the previous index + let write_offset = offset_calculator::(count - 1); + let read_offset = offset_calculator::(count); + write::(key, write_offset, read::(key, read_offset).unwrap()); + + count += 1; + } + + // decrements len by 1 + write(self.field_id, 0, len - 1); + + removed_element + } + + /// Removes the element at the specified index and fills it with the last element. + /// This does not preserve ordering and returns the element. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the vec to remove the item from. + /// + /// # Returns + /// + /// * [V] - The element that has been removed at the index. + /// + /// # Reverts + /// + /// * Reverts if index is larger or equal to length of the vec. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `5` + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// let removed_value = storage.vec.swap_remove(0); + /// assert(5 == removed_value); + /// let swapped_value = storage.vec.get(0).unwrap(); + /// assert(15 == swapped_value); + /// } + /// ``` + #[storage(read, write)] + pub fn swap_remove(self, index: u64) -> V { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the index is larger or equal to len, there is no item to remove + assert(index < len); + + let key = sha256(self.field_id); + // gets the element before removing it, so it can be returned + let element_offset = offset_calculator::(index); + let element_to_be_removed = read::(key, element_offset).unwrap(); + + let last_offset = offset_calculator::(len - 1); + let last_element = read::(key, last_offset).unwrap(); + + write::(key, element_offset, last_element); + + // decrements len by 1 + write(self.field_id, 0, len - 1); + + element_to_be_removed + } + + /// Sets or mutates the value at the given index. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the vec to set the value at + /// * `value`: [V] - The value to be set + /// + /// # Reverts + /// + /// * Reverts if index is larger than or equal to the length of the vec. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `2` + /// * Writes: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// + /// storage.vec.set(0, 20); + /// let set_value = storage.vec.get(0).unwrap(); + /// assert(20 == set_value); + /// } + /// ``` + #[storage(read, write)] + pub fn set(self, index: u64, value: V) { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the index is higher than or equal len, there is no element to set + assert(index < len); + + let key = sha256(self.field_id); + let offset = offset_calculator::(index); + write::(key, offset, value); + } + + /// Inserts the value at the given index, moving the current index's value + /// as well as the following index's value up by one index. + /// + /// # Additional Information + /// + /// > **_WARNING:_** Expensive for larger vecs. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the vec to insert the item into. + /// * `value`: [V] - The value to insert into the vec. + /// + /// # Reverts + /// + /// * Reverts if index is larger than the length of the vec. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `if self.len() == index { 3 } else { 5 + (2 * (self.len() - index)) }` + /// * Writes: `if self.len() == index { 2 } else { 2 + self.len() - index }` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(15); + /// + /// storage.vec.insert(1, 10); + /// + /// assert(5 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(15 == storage.vec.get(2).unwrap()); + /// } + /// ``` + #[storage(read, write)] + pub fn insert(self, index: u64, value: V) { + let len = read::(self.field_id, 0).unwrap_or(0); + + // if the index is larger than len, there is no space to insert + assert(index <= len); + + // if len is 0, index must also be 0 due to above check + let key = sha256(self.field_id); + if len == index { + let offset = offset_calculator::(index); + write::(key, offset, value); + + // increments len by 1 + write(self.field_id, 0, len + 1); + + return; + } + + // for every element in the vec with an index larger than the input index, + // move the element up one index. + // performed in reverse to prevent data overwriting + let mut count = len - 1; + while count >= index { + // shifts all the values up one index + let write_offset = offset_calculator::(count + 1); + let read_offset = offset_calculator::(count); + write::(key, write_offset, read::(key, read_offset).unwrap()); + + if count == 0 { break; } + count -= 1; + } + + // inserts the value into the now unused index + let offset = offset_calculator::(index); + write::(key, offset, value); + + // increments len by 1 + write(self.field_id, 0, len + 1); + } + + /// Returns the length of the vector. + /// + /// # Returns + /// + /// * [u64] - The stored length of the vector. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// assert(0 == storage.vec.len()); + /// storage.vec.push(5); + /// assert(1 == storage.vec.len()); + /// storage.vec.push(10); + /// assert(2 == storage.vec.len()); + /// } + /// ``` + #[storage(read)] + pub fn len(self) -> u64 { + read::(self.field_id, 0).unwrap_or(0) + } + + /// Checks whether the len is zero or not. + /// + /// # Returns + /// + /// * [bool] - Indicates whether the vector is or is not empty. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// assert(true == storage.vec.is_empty()); + /// + /// storage.vec.push(5); + /// + /// assert(false == storage.vec.is_empty()); + /// + /// storage.vec.clear(); + /// + /// assert(true == storage.vec.is_empty()); + /// } + /// ``` + #[storage(read)] + pub fn is_empty(self) -> bool { + read::(self.field_id, 0).unwrap_or(0) == 0 + } + + /// Sets the len to zero. + /// + /// # Number of Storage Accesses + /// + /// * Clears: `1` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// assert(0 == storage.vec.len()); + /// storage.vec.push(5); + /// assert(1 == storage.vec.len()); + /// storage.vec.clear(); + /// assert(0 == storage.vec.len()); + /// } + /// ``` + #[storage(write)] + pub fn clear(self) { + let _ = clear::(self.field_id, 0); + } + + /// Swaps two elements. + /// + /// # Arguments + /// + /// * `element1_index`: [u64] - The index of the first element. + /// * `element2_index`: [u64] - The index of the second element. + /// + /// # Reverts + /// + /// * If `element1_index` or `element2_index` is greater than the length of the vector. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `5` + /// * Writes: `2` + /// + /// # Examples + /// + /// ```sway + /// use std::storage::storage_vec::*; + /// + /// storage { + /// vec: StorageVec = StorageVec {} + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// + /// storage.vec.swap(0, 2); + /// assert(15 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(5 == storage.vec.get(2).unwrap()); + /// ``` + #[storage(read, write)] + pub fn swap(self, element1_index: u64, element2_index: u64) { + let len = read::(self.field_id, 0).unwrap_or(0); + assert(element1_index < len); + assert(element2_index < len); + + if element1_index == element2_index { + return; + } + + let key = sha256(self.field_id); + let element1_offset = offset_calculator::(element1_index); + let element2_offset = offset_calculator::(element2_index); + + let element1_value = read::(key, element1_offset).unwrap(); + + write::(key, element1_offset, read::(key, element2_offset).unwrap()); + write::(key, element2_offset, element1_value); + } + + /// Returns the first element of the vector, or `None` if it is empty. + /// + /// # Returns + /// + /// * [Option>] - Describes the raw location in storage of the value stored at + /// the start of the vector or zero if the vector is empty. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// assert(storage.vec.first().is_none()); + /// + /// storage.vec.push(5); + /// + /// assert(5 == storage.vec.first().unwrwap()); + /// } + /// ``` + #[storage(read)] + pub fn first(self) -> Option> { + let key = sha256(self.field_id); + match read::(self.field_id, 0).unwrap_or(0) { + 0 => None, + _ => Some(StorageKey::::new( + key, + 0, + sha256((0, key)) + )), + } + } + + /// Returns the last element of the vector, or `None` if it is empty. + /// + /// # Returns + /// + /// * [Option>] - Describes the raw location in storage of the value stored at + /// the end of the vector or zero if the vector is empty. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// assert(storage.vec.last().is_none()); + /// + /// storage.vec.push(5); + /// storage.vec.push(10); + /// + /// assert(10 == storage.vec.last().unwrap()); + /// } + /// ``` + #[storage(read)] + pub fn last(self) -> Option> { + let key = sha256(self.field_id); + match read::(self.field_id, 0).unwrap_or(0) { + 0 => None, + len => { + let offset = offset_calculator::(len - 1); + Some(StorageKey::::new( + key, + offset, + sha256((len - 1, key)) + )) + }, + } + } + + /// Reverses the order of elements in the vector, in place. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1 + (3 * (self.len() / 2))` + /// * Writes: `2 * (self.len() / 2)` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// storage.vec.reverse(); + /// + /// assert(15 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(5 == storage.vec.get(2).unwrap()); + /// } + /// ``` + #[storage(read, write)] + pub fn reverse(self) { + let len = read::(self.field_id, 0).unwrap_or(0); + + if len < 2 { + return; + } + + let key = sha256(self.field_id); + let mid = len / 2; + let mut i = 0; + while i < mid { + let i_offset = offset_calculator::(i); + let other_offset = offset_calculator::(len - i - 1); + + let element1_value = read::(key, i_offset).unwrap(); + + write::(key, i_offset, read::(key, other_offset).unwrap()); + write::(key, other_offset, element1_value); + + i += 1; + } + } + + /// Fills `self` with elements by cloning `value`. + /// + /// # Arguments + /// + /// * `value`: [V] - Value to copy to each element of the vector. + /// + /// # Number of Storage Accesses + /// + /// * Reads: `1 + self.len()` + /// * Writes: `self.len()` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.push(15); + /// storage.vec.fill(20); + /// + /// assert(20 == storage.vec.get(0).unwrap()); + /// assert(20 == storage.vec.get(1).unwrap()); + /// assert(20 == storage.vec.get(2).unwrap()); + /// } + /// ``` + #[storage(read, write)] + pub fn fill(self, value: V) { + let len = read::(self.field_id, 0).unwrap_or(0); + + let key = sha256(self.field_id); + let mut i = 0; + while i < len { + let offset = offset_calculator::(i); + write::(key, offset, value); + i += 1; + } + } + + /// Resizes `self` in place so that `len` is equal to `new_len`. + /// + /// # Additional Information + /// + /// If `new_len` is greater than `len`, `self` is extended by the difference, with each + /// additional slot being filled with `value`. If the `new_len` is less than `len`, `self` is + /// simply truncated. + /// + /// # Arguments + /// + /// * `new_len`: [u64] - The new length to expand or truncate to + /// * `value`: [V] - The value to fill into new slots if the `new_len` is greater than the current length + /// + /// # Number of Storage Accesses + /// + /// * Reads - `if new_len > self.len() { new_len - len + 2 } else { 2 }` + /// * Writes - `if new_len > self.len() { new_len - len + 1 } else { 1 }` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// storage.vec.push(5); + /// storage.vec.push(10); + /// storage.vec.resize(4, 20); + /// + /// assert(5 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(20 == storage.vec.get(2).unwrap()); + /// assert(20 == storage.vec.get(3).unwrap()); + /// + /// storage.vec.resize(2, 0); + /// + /// assert(5 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(None == storage.vec.get(2)); + /// assert(None == storage.vec.get(3)); + /// } + /// ``` + #[storage(read, write)] + pub fn resize(self, new_len: u64, value: V) { + let mut len = read::(self.field_id, 0).unwrap_or(0); + let key = sha256(self.field_id); + while len < new_len { + let offset = offset_calculator::(len); + write::(key, offset, value); + len += 1; + } + write::(self.field_id, 0, new_len); + } + + // TODO: This should be moved into the vec.sw file and `From> for Vec` + // implemented instead of this when https://github.com/FuelLabs/sway/issues/409 is resolved. + // Implementation will change from this: + // ```sway + // let my_vec = Vec::new(); + // storage.storage_vec.store_vec(my_vec); + // let other_vec = storage.storage_vec.load_vec(); + // ``` + // To this: + // ```sway + // let my_vec = Vec::new(); + // storage.storage_vec = my_vec.into(); + // let other_vec = Vec::from(storage.storage_vec); + // ``` + /// Stores a `Vec` as a `StorageVec`. + /// + /// # Additional Information + /// + /// This will overwrite any existing values in the `StorageVec`. + /// + /// # Arguments + /// + /// * `vec`: [Vec] - The vector to store in storage. + /// + /// # Number of Storage Accesses + /// + /// * Writes - `2` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// let mut vec = Vec::::new(); + /// vec.push(5); + /// vec.push(10); + /// vec.push(15); + /// + /// storage.vec.store_vec(vec); + /// + /// assert(5 == storage.vec.get(0).unwrap()); + /// assert(10 == storage.vec.get(1).unwrap()); + /// assert(15 == storage.vec.get(2).unwrap()); + /// } + /// ``` + #[storage(write)] + pub fn store_vec(self, vec: Vec) { + let slice = vec.as_raw_slice(); + // Get the number of storage slots needed based on the size of bytes. + let number_of_bytes = slice.number_of_bytes(); + let number_of_slots = (number_of_bytes + 31) >> 5; + let mut ptr = slice.ptr(); + + // The capacity needs to be a multiple of 32 bytes so we can + // make the 'quad' storage instruction store without accessing unallocated heap memory. + ptr = realloc_bytes(ptr, number_of_bytes, number_of_slots * 32); + + // Store `number_of_slots * 32` bytes starting at storage slot `key`. + let _ = __state_store_quad(sha256(self.field_id), ptr, number_of_slots); + + // Store the length, NOT the bytes. + // This differs from the existing `write_slice()` function to be compatible with `StorageVec`. + write::(self.field_id, 0, number_of_bytes / __size_of::()); + } + + /// Load a `Vec` from the `StorageVec`. + /// + /// # Returns + /// + /// * [Option>] - The vector constructed from storage or `None`. + /// + /// # Number of Storage Accesses + /// + /// * Reads - `2` + /// + /// # Examples + /// + /// ```sway + /// storage { + /// vec: StorageVec = StorageVec {}, + /// } + /// + /// fn foo() { + /// let mut vec = Vec::::new(); + /// vec.push(5); + /// vec.push(10); + /// vec.push(15); + /// + /// storage.vec.store_vec(vec); + /// let returned_vec = storage.vec.load_vec(); + /// + /// assert(5 == returned_vec.get(0).unwrap()); + /// assert(10 == returned_vec.get(1).unwrap()); + /// assert(15 == returned_vec.get(2).unwrap()); + /// } + /// ``` + #[storage(read)] + pub fn load_vec(self) -> Vec { + // Get the length of the slice that is stored. + match read::(self.field_id, 0).unwrap_or(0) { + 0 => Vec::new(), + len => { + // Get the number of storage slots needed based on the size. + let bytes = len * __size_of::(); + let number_of_slots = (bytes + 31) >> 5; + let ptr = alloc_bytes(number_of_slots * 32); + // Load the stored slice into the pointer. + let _ = __state_load_quad(sha256(self.field_id), ptr, number_of_slots); + Vec::from(asm(ptr: (ptr, bytes)) { ptr: raw_slice }) + } + } + } +} + +fn offset_calculator(offset: u64) -> u64 { + (offset * __size_of::()) / 8 +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/string.sw b/sway-lsp/tests/fixtures/fixtures-std/src/string.sw new file mode 100644 index 00000000000..a99cf5bbc11 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/string.sw @@ -0,0 +1,510 @@ +library; + +use ::assert::assert; +use ::bytes::Bytes; +use ::convert::From; +use ::hash::{Hash, Hasher}; +use ::option::Option; + + +/// A UTF-8 encoded growable string. +/// +/// # Additional Information +/// +/// WARNING: As this type is meant to be forward compatible with UTF-8, do *not* +/// add any mutation functionality or unicode input of any kind until `char` is +/// implemented, codepoints are *not* guaranteed to fall on byte boundaries +pub struct String { + /// The bytes representing the characters of the string. + bytes: Bytes, +} + +impl String { + /// Returns `Bytes` giving a UTF-8 representation of the string. + /// + /// # Returns + /// + /// * [Bytes] - A UTF-8 representation of the string. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let mut string = String::new(); + /// string.push(0u8); + /// let bytes = string.as_bytes(); + /// assert(bytes.len() == 1); + /// assert(bytes.get(0).unwrap() == 0u8); + /// } + /// ``` + pub fn as_bytes(self) -> Bytes { + self.bytes + } + + /// Gets the amount of memory on the heap allocated to the `String`. + /// + /// # Returns + /// + /// * `u64` - The number of characters the `String` can hold without reallocating. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let mut string = String::new(); + /// assert(string.capacity() == 0); + /// string.push(0u8); + /// assert(string.capacity() == 1); + /// string.push(1u8); + /// assert(string.capacity() == 2); + /// } + /// ``` + pub fn capacity(self) -> u64 { + self.bytes.capacity() + } + + /// Truncates this `String` to a length of zero, clearing all content. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let mut string = String::new(); + /// string.push(0u8); + /// assert(!string.is_empty()); + /// string.clear(); + /// assert(string.is_empty()); + /// } + /// ``` + pub fn clear(ref mut self) { + self.bytes.clear() + } + + /// Converts a vector of ASCII encoded bytes to a `String`. + /// + /// # Additional Information + /// + /// Each byte represents a single character, this supports ASCII but it does **not** support Unicode. + /// + /// # Arguments + /// + /// * `bytes` - ASCII bytes which will be converted into a `String`. + /// + /// # Returns + /// + /// * [String] - A `String` containing the ASCII encoded bytes. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let mut bytes = Bytes::new(); + /// bytes.push(0u8); + /// bytes.push(1u8); + /// let string = String::from_ascii(bytes); + /// } + /// ``` + pub fn from_ascii(bytes: Bytes) -> Self { + Self { + bytes, + } + } + + /// Converts a string slice containing ASCII encoded bytes to a `String` + /// + /// # Arguments + /// + /// * `s` - A string slice containing ASCII encoded bytes. + /// + /// # Returns + /// + /// * [String] - A `String` containing the ASCII encoded bytes. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let string = String::from_ascii_str("ABCDEF"); + /// } + /// ``` + pub fn from_ascii_str(s: str) -> Self { + let str_size = s.len(); + let str_ptr = s.as_ptr(); + + let mut bytes = Bytes::with_capacity(str_size); + bytes.len = str_size; + + str_ptr.copy_bytes_to(bytes.buf.ptr(), str_size); + + Self { + bytes + } + } + + /// Returns a `bool` indicating whether the `String` is empty. + /// + /// # Returns + /// + /// * [bool] - `true` if the `String` is empty, `false` otherwise. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let mut string = String::new(); + /// assert(string.is_empty()); + /// string.push(0u8); + /// assert(!string.is_empty()); + /// } + /// ``` + pub fn is_empty(self) -> bool { + self.bytes.is_empty() + } + + /// Constructs a new instance of the `String` type. + /// + /// # Returns + /// + /// * [String] - A new empty instance of the `String` type. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let string = String::new(); + /// string.push(0u8); + /// } + /// ``` + pub fn new() -> Self { + Self { + bytes: Bytes::new(), + } + } + + /// Constructs a new instance of the `String` type with the specified capacity. + /// + /// # Arguments + /// + /// * `capacity`: [u64] - The specified amount of bytes on the heap to be allocated for the `String`. + /// + /// # Returns + /// + /// * [String] - A new empty instance of the `String` type with the specified capacity. + /// + /// # Examples + /// + /// ```sway + /// use std::string::String; + /// + /// fn foo() { + /// let string = String::with_capacity(1); + /// string.push(0u8); // This will not reallocate + /// string.push(1u8); // This will reallocate + /// } + /// ``` + pub fn with_capacity(capacity: u64) -> Self { + Self { + bytes: Bytes::with_capacity(capacity), + } + } +} + +impl From for String { + fn from(b: Bytes) -> Self { + let mut string = Self::new(); + string.bytes = b; + string + } + + fn into(self) -> Bytes { + self.bytes + } +} + +impl AsRawSlice for String { + /// Returns a raw slice to all of the elements in the string. + fn as_raw_slice(self) -> raw_slice { + asm(ptr: (self.bytes.buf.ptr(), self.bytes.len)) { ptr: raw_slice } + } +} + +impl From for String { + fn from(slice: raw_slice) -> Self { + Self { + bytes: Bytes::from(slice), + } + } + + fn into(self) -> raw_slice { + asm(ptr: (self.bytes.buf.ptr(), self.bytes.len)) { ptr: raw_slice } + } +} + +impl Eq for String { + fn eq(self, other: Self) -> bool { + self.bytes == other.bytes + } +} + +impl Hash for String { + fn hash(self, ref mut state: Hasher) { + state.write(self.bytes); + } +} + +// Tests + +#[test] +fn string_test_as_bytes() { + let mut string = String::new(); + + let bytes = string.as_bytes(); + assert(bytes.len() == 0); + assert(bytes.capacity() == string.capacity()); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let string = String::from_ascii(bytes); + + let bytes = string.as_bytes(); + assert(bytes.len() == 1); + assert(bytes.capacity() == string.capacity()); +} + +#[test] +fn string_test_capacity() { + let mut string = String::new(); + + assert(string.capacity() == 0); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let string = String::from_ascii(bytes); + assert(string.capacity() == 1); +} + +#[test] +fn string_test_clear() { + let mut string = String::new(); + + assert(string.is_empty()); + + string.clear(); + assert(string.is_empty()); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let mut string = String::from_ascii(bytes); + assert(!string.is_empty()); + + string.clear(); + assert(string.is_empty()); +} + +#[test] +fn string_test_from() { + let mut bytes = Bytes::new(); + + bytes.push(0u8); + bytes.push(1u8); + bytes.push(2u8); + bytes.push(3u8); + bytes.push(4u8); + + let mut string_from_bytes = String::from(bytes); + let bytes = string_from_bytes.as_bytes(); + assert(bytes.len() == 5); + assert(bytes.capacity() == string_from_bytes.capacity()); + assert(bytes.get(0).unwrap() == 0u8); + assert(bytes.get(1).unwrap() == 1u8); + assert(bytes.get(2).unwrap() == 2u8); +} + +#[test] +fn string_test_from_raw_slice() { + let mut bytes = Bytes::new(); + + bytes.push(0u8); + bytes.push(1u8); + bytes.push(2u8); + bytes.push(3u8); + bytes.push(4u8); + + let raw_slice = bytes.as_raw_slice(); + let mut string_from_slice = String::from(raw_slice); + let bytes = string_from_slice.as_bytes(); + assert(bytes.len() == 5); + assert(bytes.get(0).unwrap() == 0u8); + assert(bytes.get(1).unwrap() == 1u8); + assert(bytes.get(2).unwrap() == 2u8); +} + +#[test] +fn string_test_from_ascii() { + let mut bytes = Bytes::new(); + + bytes.push(0u8); + bytes.push(1u8); + bytes.push(2u8); + bytes.push(3u8); + bytes.push(4u8); + + let mut string_from_ascii = String::from_ascii(bytes); + assert(bytes.capacity() == string_from_ascii.capacity()); + let bytes = string_from_ascii.as_bytes(); + assert(bytes.get(0).unwrap() == 0u8); + assert(bytes.get(1).unwrap() == 1u8); + assert(bytes.get(2).unwrap() == 2u8); +} + +#[test] +fn string_test_from_ascii_str() { + let mut string_from_ascii = String::from_ascii_str("ABCDEF"); + assert(string_from_ascii.capacity() == 6); + let bytes = string_from_ascii.as_bytes(); + assert(bytes.get(0).unwrap() == 65u8); + assert(bytes.get(1).unwrap() == 66u8); + assert(bytes.get(2).unwrap() == 67u8); + assert(bytes.get(3).unwrap() == 68u8); + assert(bytes.get(4).unwrap() == 69u8); + assert(bytes.get(5).unwrap() == 70u8); + assert(bytes.get(6).is_none()); +} + +#[test] +fn string_test_into_bytes() { + let mut string = String::new(); + + let bytes: Bytes = string.into(); + assert(bytes.len() == 0); + assert(bytes.capacity() == string.capacity()); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let string = String::from_ascii(bytes); + let bytes: Bytes = string.into(); + assert(bytes.len() == 1); + assert(bytes.capacity() == string.capacity()); + assert(bytes.get(0).unwrap() == 0u8); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + bytes.push(1u8); + let string = String::from_ascii(bytes); + let mut bytes: Bytes = string.into(); + assert(bytes.len() == 2); + assert(bytes.capacity() == string.capacity()); + assert(bytes.get(1).unwrap() == 1u8); +} + +#[test] +fn string_test_into_raw_slice() { + let mut string = String::new(); + + let raw_slice: raw_slice = string.into(); + assert(raw_slice.number_of_bytes() == 0); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let string = String::from_ascii(bytes); + let raw_slice = string.as_raw_slice(); + assert(raw_slice.number_of_bytes() == 1); + assert(raw_slice.ptr().read_byte() == 0u8); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + bytes.push(1u8); + let string = String::from_ascii(bytes); + let mut raw_slice = string.as_raw_slice(); + assert(raw_slice.number_of_bytes() == 2); + assert(raw_slice.ptr().add_uint_offset(1).read_byte() == 1u8); + + let mut raw_slice = string.as_raw_slice(); + assert(raw_slice.number_of_bytes() == 2); + assert(raw_slice.ptr().read_byte() == 0u8); +} + +#[test] +fn string_test_is_empty() { + let mut string = String::new(); + + assert(string.is_empty()); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + let string = String::from_ascii(bytes); + assert(!string.is_empty()); + + let mut bytes = Bytes::new(); + bytes.push(0u8); + bytes.push(1u8); + let mut string = String::from_ascii(bytes); + assert(!string.is_empty()); + + string.clear(); + assert(string.is_empty()); +} + +#[test] +fn string_test_new() { + let mut string = String::new(); + + assert(string.is_empty()); + assert(string.capacity() == 0); +} + +#[test] +fn string_test_with_capacity() { + let mut iterator = 0; + + while iterator < 16 { + let mut string = String::with_capacity(iterator); + assert(string.capacity() == iterator); + iterator += 1; + } + + let mut string = String::with_capacity(0); + assert(string.capacity() == 0); + + string.clear(); + assert(string.capacity() == 0); + let mut string = String::with_capacity(4); + + assert(string.capacity() == 4); +} + +#[test] +fn string_test_equal() { + let string1 = String::from_ascii_str("fuel"); + let string2 = String::from_ascii_str("fuel"); + let string3 = String::from_ascii_str("blazingly fast"); + + assert(string1 == string2); + assert(string1 != string3); +} + +#[test] +fn string_test_hash() { + use ::hash::sha256; + + let mut bytes = Bytes::new(); + bytes.push(0u8); + + let string = String::from(bytes); + + assert(sha256(string) == sha256(bytes)); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/token.sw b/sway-lsp/tests/fixtures/fixtures-std/src/token.sw new file mode 100644 index 00000000000..6ace432e985 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/token.sw @@ -0,0 +1,271 @@ +//! Functionality for performing common operations with tokens. +library; + +use ::address::Address; +use ::alias::SubId; +use ::call_frames::contract_id; +use ::contract_id::{AssetId, ContractId}; +use ::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL; +use ::identity::Identity; +use ::revert::revert; +use ::outputs::{Output, output_amount, output_count, output_type}; + +/// Mint `amount` coins of the current contract's `asset_id` and transfer them +/// to `to` by calling either `force_transfer_to_contract` or +/// `transfer_to_address`, depending on the type of `Identity`. +/// +/// # Additional Information +/// +/// If the `to` Identity is a contract, this will transfer coins to the contract even with no way to retrieve them +/// (i.e: no withdrawal functionality on the receiving contract), possibly leading to +/// the **_PERMANENT LOSS OF COINS_** if not used with care. +/// +/// # Arguments +/// +/// * `to`: [Identity] - The recipient identity. +/// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. +/// * `amount`: [u64] - The amount of tokens to mint. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, token::mint_to}; +/// +/// fn foo() { +/// let to_address = Identity::Address(Address::from(ZERO_B256)); +/// let to_contract_id = Identity::ContractId(ContractId::from(ZERO_B256)); +/// mint_to(to_address, ZERO_B256, 500); +/// mint_to(to_contract_id, ZERO_B256, 500); +/// } +/// ``` +pub fn mint_to(to: Identity, sub_id: SubId, amount: u64) { + mint(sub_id, amount); + transfer(to, AssetId::new(contract_id(), sub_id), amount); +} + +/// Mint `amount` coins of the current contract's `asset_id` and send them +/// UNCONDITIONALLY to the contract at `to`. +/// +/// # Additional Information +/// +/// This will transfer coins to a contract even with no way to retrieve them +/// (i.e: no withdrawal functionality on the receiving contract), possibly leading to +/// the **_PERMANENT LOSS OF COINS_** if not used with care. +/// +/// # Arguments +/// +/// * `to`: [ContractId] - The recipient contract. +/// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. +/// * `amount`: [u64] - The amount of tokens to mint. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, token::mint_to_contract}; +/// +/// fn foo() { +/// let to = ContractId::from(ZERO_B256); +/// mint_to_contract(to, ZERO_B256, 500); +/// } +/// ``` +pub fn mint_to_contract(to: ContractId, sub_id: SubId, amount: u64) { + mint(sub_id, amount); + force_transfer_to_contract(to, AssetId::new(contract_id(), sub_id), amount); +} + +/// Mint `amount` coins of the current contract's `asset_id` and send them to +/// the Address `to`. +/// +/// # Arguments +/// +/// * `to`: [Address] - The recipient address. +/// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. +/// * `amount`: [u64] - The amount of tokens to mint. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, token::mint_to_address}; +/// +/// fn foo() { +/// let to = Address::from(ZERO_B256); +/// mint_to_address(to, ZERO_B256, 500); +/// } +/// ``` +pub fn mint_to_address(to: Address, sub_id: SubId, amount: u64) { + mint(sub_id, amount); + transfer_to_address(to, AssetId::new(contract_id(), sub_id), amount); +} + +/// Mint `amount` coins of the current contract's `sub_id`. The newly minted tokens are owned by the current contract. +/// +/// # Arguments +/// +/// * `sub_id`: [SubId] - The sub identfier of the asset which to mint. +/// * `amount`: [u64] - The amount of tokens to mint. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, token::mint}; +/// +/// fn foo() { +/// mint(ZERO_B256, 500); +/// } +/// ``` +pub fn mint(sub_id: SubId, amount: u64) { + asm(r1: amount, r2: sub_id) { + mint r1 r2; + } +} + +/// Burn `amount` coins of the current contract's `sub_id`. Burns them from the balance of the current contract. +/// +/// # Arguments +/// +/// * `sub_id`: [SubId] - The sub identfier of the asset which to burn. +/// * `amount`: [u64] - The amount of tokens to burn. +/// +/// # Reverts +/// +/// * When the contract balance is less than `amount`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::ZERO_B256, token::burn}; +/// +/// fn foo() { +/// burn(ZERO_B256, 500); +/// } +/// ``` +pub fn burn(sub_id: SubId, amount: u64) { + asm(r1: amount, r2: sub_id) { + burn r1 r2; + } +} + +/// Transfer `amount` coins of the type `asset_id` and send them +/// to `to` by calling either `force_transfer_to_contract` or +/// `transfer_to_address`, depending on the type of `Identity`. +/// +/// # Additional Information +/// +/// If the `to` Identity is a contract this may transfer coins to the contract even with no way to retrieve them +/// (i.e. no withdrawal functionality on receiving contract), possibly leading +/// to the **_PERMANENT LOSS OF COINS_** if not used with care. +/// +/// # Arguments +/// +/// * `to`: [Identity] - The recipient identity. +/// * `asset_id`: [AssetId] - The token to transfer. +/// * `amount`: [u64] - The amount of tokens to transfer. +/// +/// # Reverts +/// +/// * When `amount` is greater than the contract balance for `asset_id`. +/// * When `amount` is equal to zero. +/// * When there are no free variable outputs when transferring to an `Address`. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::{BASE_ASSET_ID, ZERO_B256}, token::transfer}; +/// +/// fn foo() { +/// let to_address = Identity::Address(Address::from(ZERO_B256)); +/// let to_contract_id = Identity::ContractId(ContractId::from(ZERO_B256)); +/// transfer(to_address, BASE_ASSET_ID, 500); +/// transfer(to_contract_id, BASE_ASSET_ID, 500); +/// } +/// ``` +pub fn transfer(to: Identity, asset_id: AssetId, amount: u64) { + match to { + Identity::Address(addr) => transfer_to_address(addr, asset_id, amount), + Identity::ContractId(id) => force_transfer_to_contract(id, asset_id, amount), + }; +} + +/// UNCONDITIONAL transfer of `amount` coins of type `asset_id` to +/// the contract at `to`. +/// +/// # Additional Information +/// +/// This will transfer coins to a contract even with no way to retrieve them +/// (i.e. no withdrawal functionality on receiving contract), possibly leading +/// to the **_PERMANENT LOSS OF COINS_** if not used with care. +/// +/// # Arguments +/// +/// * `to`: [ContractId] - The recipient contract. +/// * `asset_id`: [AssetId] - The token to transfer. +/// * `amount`: [u64] - The amount of tokens to transfer. +/// +/// # Reverts +/// +/// * When `amount` is greater than the contract balance for `asset_id`. +/// * When `amount` is equal to zero. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::{BASE_ASSET_ID, ZERO_B256}, token::force_transfer_to_contract}; +/// +/// fn foo() { +/// let to_contract_id = ContractId::from(ZERO_B256); +/// force_transfer_to_contract(to_contract_id, BASE_ASSET_ID, 500); +/// } +/// ``` +pub fn force_transfer_to_contract(to: ContractId, asset_id: AssetId, amount: u64) { + asm(r1: amount, r2: asset_id, r3: to.value) { + tr r3 r1 r2; + } +} + +/// Transfer `amount` coins of type `asset_id` and send them to +/// the address `to`. +/// +/// # Arguments +/// +/// * `to`: [Address] - The recipient address. +/// * `asset_id`: [AssetId] - The token to transfer. +/// * `amount`: [u64] - The amount of tokens to transfer. +/// +/// # Reverts +/// +/// * When `amount` is greater than the contract balance for `asset_id`. +/// * When `amount` is equal to zero. +/// * When there are no free variable outputs. +/// +/// # Examples +/// +/// ```sway +/// use std::{constants::{BASE_ASSET_ID, ZERO_B256}, token::transfer_to_address}; +/// +/// fn foo() { +/// let to_address = Address::from(ZERO_B256); +/// transfer_to_address(to_address, BASE_ASSET_ID, 500); +/// } +/// ``` +pub fn transfer_to_address(to: Address, asset_id: AssetId, amount: u64) { + // maintain a manual index as we only have `while` loops in sway atm: + let mut index = 0; + + // If an output of type `OutputVariable` is found, check if its `amount` is + // zero. As one cannot transfer zero coins to an output without a panic, a + // variable output with a value of zero is by definition unused. + let number_of_outputs = output_count(); + while index < number_of_outputs { + if let Output::Variable = output_type(index) { + if output_amount(index) == 0 { + asm(r1: to.value, r2: index, r3: amount, r4: asset_id) { + tro r1 r2 r3 r4; + }; + return; + } + } + index += 1; + } + + revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/tx.sw b/sway-lsp/tests/fixtures/fixtures-std/src/tx.sw new file mode 100644 index 00000000000..c13d0a47d67 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/tx.sw @@ -0,0 +1,511 @@ +//! Transaction field getters. +library; + +use ::constants::ZERO_B256; +use ::revert::revert; + +// GTF Opcode const selectors +// +pub const GTF_TYPE = 0x001; +pub const GTF_SCRIPT_GAS_PRICE = 0x002; +pub const GTF_SCRIPT_GAS_LIMIT = 0x003; +pub const GTF_SCRIPT_MATURITY = 0x004; +pub const GTF_SCRIPT_SCRIPT_LENGTH = 0x005; +pub const GTF_SCRIPT_SCRIPT_DATA_LENGTH = 0x006; +pub const GTF_SCRIPT_INPUTS_COUNT = 0x007; +pub const GTF_SCRIPT_OUTPUTS_COUNT = 0x008; +pub const GTF_SCRIPT_WITNESSES_COUNT = 0x009; +pub const GTF_SCRIPT_RECEIPTS_ROOT = 0x00A; +pub const GTF_SCRIPT_SCRIPT = 0x00B; +pub const GTF_SCRIPT_SCRIPT_DATA = 0x00C; +pub const GTF_SCRIPT_INPUT_AT_INDEX = 0x00D; +pub const GTF_SCRIPT_OUTPUT_AT_INDEX = 0x00E; +pub const GTF_SCRIPT_WITNESS_AT_INDEX = 0x00F; + +pub const GTF_CREATE_GAS_PRICE = 0x010; +pub const GTF_CREATE_GAS_LIMIT = 0x011; +pub const GTF_CREATE_MATURITY = 0x012; +// pub const GTF_CREATE_BYTECODE_LENGTH = 0x013; +// pub const GTF_CREATE_BYTECODE_WITNESS_INDEX = 0x014; +// pub const GTF_CREATE_STORAGE_SLOTS_COUNT = 0x015; +pub const GTF_CREATE_INPUTS_COUNT = 0x016; +pub const GTF_CREATE_OUTPUTS_COUNT = 0x017; +pub const GTF_CREATE_WITNESSES_COUNT = 0x018; +// pub const GTF_CREATE_SALT = 0x019; +// pub const GTF_CREATE_STORAGE_SLOT_AT_INDEX = 0x01A; +pub const GTF_CREATE_INPUT_AT_INDEX = 0x01B; +pub const GTF_CREATE_OUTPUT_AT_INDEX = 0x01C; +pub const GTF_CREATE_WITNESS_AT_INDEX = 0x01D; + +pub const GTF_WITNESS_DATA_LENGTH = 0x301; +pub const GTF_WITNESS_DATA = 0x302; + +/// A transaction type. +pub enum Transaction { + /// A standard transaction, where execution is defined by a script. + Script: (), + /// A contract deployment transaction. + Create: (), +} + +/// Get the type of the current transaction. +/// Either `Transaction::Script` or `Transaction::Create`. +/// +/// # Returns +/// +/// * [Transaction] - The type of the current transaction. +/// +/// # Reverts +/// +/// * When the transaction type is unrecognized. This should never happen. +/// +/// # Example +/// +/// ```sway +/// use std::tx::tx_type; +/// +/// fn foo() { +/// let tx_type = tx_type(); +/// match tx_type { +/// Transaction::Script => { +/// log("Regular script transaction"); +/// }, +/// Transaction::Create => { +/// log("Contract deployment transaction"); +/// }, +/// } +/// } +/// ``` +pub fn tx_type() -> Transaction { + match __gtf::(0, GTF_TYPE) { + 0u8 => Transaction::Script, + 1u8 => Transaction::Create, + _ => revert(0), + } +} + +/// Get the transaction gas price for the transaction. +/// +/// # Returns +/// +/// * [u64] - The gas price for the transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_gas_price; +/// +/// fn foo() { +/// let gas_price = tx_gas_price(); +/// log(gas_price); +/// } +/// ``` +pub fn tx_gas_price() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_GAS_PRICE), + Transaction::Create => __gtf::(0, GTF_CREATE_GAS_PRICE), + } +} + +/// Get the transaction-script gas limit for the transaction. +/// +/// # Returns +/// +/// * [u64] - The gas limit for the transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_gas_limit; +/// +/// fn foo() { +/// let gas_limit = tx_gas_limit(); +/// log(gas_limit); +/// } +/// ``` +pub fn tx_gas_limit() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_GAS_LIMIT), + Transaction::Create => __gtf::(0, GTF_CREATE_GAS_LIMIT), + } +} + +/// Get the transaction maturity for the transaction. +/// +/// # Returns +/// +/// * [u32] - The maturity for the transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_maturity; +/// +/// fn foo() { +/// let maturity = tx_maturity(); +/// log(maturity); +/// } +/// ``` +pub fn tx_maturity() -> u32 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_MATURITY), + Transaction::Create => __gtf::(0, GTF_CREATE_MATURITY), + } +} + +/// Get the length of the script for the transaction. +/// +/// # Returns +/// +/// * [u64] - The script length for the transaction. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_length; +/// +/// fn foo() { +/// let script_length = tx_script_length(); +/// assert(script_length > 0); +/// } +/// ``` +pub fn tx_script_length() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_SCRIPT_LENGTH), + Transaction::Create => revert(0), + } +} + +/// Get the script data length for the transaction. +/// +/// # Returns +/// +/// * [u64] - The script data length for the transaction. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_data_length; +/// +/// fn foo() { +/// let script_data_length = tx_script_data_length(); +/// assert(script_data_length > 0); +/// } +/// ``` +pub fn tx_script_data_length() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_SCRIPT_DATA_LENGTH), + Transaction::Create => revert(0), + } +} + +/// Get the transaction witnesses count for the transaction. +/// +/// # Returns +/// +/// * [u64] - The witnesses count for the transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_witnesses_count; +/// +/// fn foo() { +/// let witnesses_count = tx_witnesses_count(); +/// log(witnesses_count); +/// } +/// ``` +pub fn tx_witnesses_count() -> u64 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), + Transaction::Create => __gtf::(0, GTF_CREATE_WITNESSES_COUNT), + } +} + +/// Get a pointer to the witness at index `index` for the transaction. +/// +/// # Arguments +/// +/// * `index` - The index of the witness to get the pointer for. +/// +/// # Returns +/// +/// * [u64] - The pointer to the witness at index `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_witness_pointer; +/// +/// fn foo() { +/// let witness_pointer = tx_witness_pointer(0); +/// log(witness_pointer); +/// } +/// ``` +pub fn tx_witness_pointer(index: u64) -> u64 { + match tx_type() { + Transaction::Script => __gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX), + Transaction::Create => __gtf::(index, GTF_CREATE_WITNESS_AT_INDEX), + } +} + +/// Get the length of the witness data at `index`. +/// +/// # Arguments +/// +/// * `index` - The index of the witness to get the data length for. +/// +/// # Returns +/// +/// * [u64] - The length of the witness data at `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_witness_data_length; +/// +/// fn foo() { +/// let witness_data_length = tx_witness_data_length(0); +/// log(witness_data_length); +/// } +/// ``` +pub fn tx_witness_data_length(index: u64) -> u64 { + __gtf::(index, GTF_WITNESS_DATA_LENGTH) +} + +/// Get the witness data at `index`. +/// +/// # Arguments +/// +/// * `index` - The index of the witness to get the data for. +/// +/// # Returns +/// +/// * [T] - The witness data at `index`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_witness_data; +/// +/// fn foo() { +/// let witness_data: u64 = tx_witness_data(0); +/// log(witness_data); +/// } +/// ``` +pub fn tx_witness_data(index: u64) -> T { + __gtf::(index, GTF_WITNESS_DATA).read::() +} + +/// Get the transaction receipts root. +/// +/// # Returns +/// +/// * [b256] - The transaction receipts root. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_receipts_root; +/// +/// fn foo() { +/// let receipts_root = tx_receipts_root(); +/// log(receipts_root); +/// } +/// ``` +pub fn tx_receipts_root() -> b256 { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_RECEIPTS_ROOT).read::(), + _ => revert(0), + } +} + +/// Get the transaction script start pointer. +/// +/// # Returns +/// +/// * [raw_ptr] - The transaction script start pointer. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_start_pointer; +/// +/// fn foo() { +/// let script_start_pointer = tx_script_start_pointer(); +/// log(script_start_pointer); +/// } +/// ``` +pub fn tx_script_start_pointer() -> raw_ptr { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_SCRIPT), + _ => revert(0), + } +} + +/// Get the transaction script data start pointer. +/// +/// # Returns +/// +/// * [raw_ptr] - The transaction script data start pointer. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_data_start_pointer; +/// +/// fn foo() { +/// let script_data_start_pointer = tx_script_data_start_pointer(); +/// log(script_data_start_pointer); +/// } +/// ``` +pub fn tx_script_data_start_pointer() -> raw_ptr { + match tx_type() { + Transaction::Script => __gtf::(0, GTF_SCRIPT_SCRIPT_DATA), + _ => { + // transaction-create has no script data length + revert(0); + } + } +} + +/// Get the script data, typed. +/// +/// # Additional Information +/// +/// **Unsafe.** +/// **Assumes the type is correct.** +/// +/// # Returns +/// +/// * [T] - The script data, typed. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_data; +/// +/// fn foo() { +/// let script_data: u64 = tx_script_data(); +/// log(script_data); +/// } +/// ``` +pub fn tx_script_data() -> T { + let ptr = tx_script_data_start_pointer(); + // TODO some safety checks on the input data? We are going to assume it is the right type for now. + ptr.read::() +} + +/// Get the script bytecode. +/// +/// # Additional Information +/// +/// Must be cast to a `u64` array, with sufficient length to contain the bytecode. +/// Bytecode will be padded to next whole word. +/// +/// # Returns +/// +/// * [T] - The script bytecode. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_bytecode; +/// +/// fn foo() { +/// let script_bytecode: [u64; 64] = tx_script_bytecode(); +/// log(script_bytecode); +/// } +/// ``` +pub fn tx_script_bytecode() -> T { + tx_script_start_pointer().read::() +} + +/// Get the hash of the script bytecode. +/// Reverts if not a transaction-script. +/// +/// # Returns +/// +/// * [b256] - The hash of the script bytecode. +/// +/// # Reverts +/// +/// * When the transaction type is of type `Transaction::Create`. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_script_bytecode_hash; +/// +/// fn foo() { +/// let script_bytecode_hash: b256 = tx_script_bytecode_hash(); +/// assert(script_bytecode_hash == 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef); +/// } +/// ``` +pub fn tx_script_bytecode_hash() -> b256 { + match tx_type() { + Transaction::Script => { + // Get the script memory details + let mut result_buffer = ZERO_B256; + let script_length = tx_script_length(); + let script_ptr = tx_script_start_pointer(); + + // Run the hash opcode for the script in memory + asm(hash: result_buffer, ptr: script_ptr, len: script_length) { + s256 hash ptr len; + hash: b256 + } + }, + _ => revert(0), + } +} + +const TX_ID_OFFSET = 0; + +/// Get the Transaction ID of the current transaction. +/// +/// # Returns +/// +/// * [b256] - The Transaction ID of the current transaction. +/// +/// # Examples +/// +/// ```sway +/// use std::tx::tx_id; +/// +/// fn foo() { +/// let tx_id: b256 = tx_id(); +/// log(tx_id); +/// } +/// ``` +pub fn tx_id() -> b256 { + asm(ptr: TX_ID_OFFSET) { ptr: raw_ptr }.read() +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/u128.sw b/sway-lsp/tests/fixtures/fixtures-std/src/u128.sw new file mode 100644 index 00000000000..62eaf70f349 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/u128.sw @@ -0,0 +1,555 @@ +//! A 128-bit unsigned integer type. +library; + +use ::assert::assert; +use ::convert::From; +use ::flags::{disable_panic_on_overflow, set_flags}; +use ::math::*; +use ::result::Result::{self, *}; + +/// The 128-bit unsigned integer type. +/// +/// # Additional Information +/// +/// Represented as two 64-bit components: `(upper, lower)`, where `value = (upper << 64) + lower`. +pub struct U128 { + /// The most significant 64 bits of the `U128`. + upper: u64, + /// The least significant 64 bits of the `U128`. + lower: u64, +} + +/// The error type used for `U128` type errors. +pub enum U128Error { + /// This error occurs when a `U128` is attempted to be downcast to a `u64` and the conversion would result in a loss of precision. + LossOfPrecision: (), +} + +impl From<(u64, u64)> for U128 { + fn from(components: (u64, u64)) -> Self { + Self { + upper: components.0, + lower: components.1, + } + } + + fn into(self) -> (u64, u64) { + (self.upper, self.lower) + } +} + +impl core::ops::Eq for U128 { + fn eq(self, other: Self) -> bool { + self.lower == other.lower && self.upper == other.upper + } +} + +impl core::ops::Ord for U128 { + fn gt(self, other: Self) -> bool { + self.upper > other.upper || self.upper == other.upper && self.lower > other.lower + } + + fn lt(self, other: Self) -> bool { + self.upper < other.upper || self.upper == other.upper && self.lower < other.lower + } +} + +// TODO this doesn't work? +// impl core::ops::OrdEq for U128 { +// } +impl u64 { + /// Performs addition between two `u64` values, returning a `U128`. + /// + /// # Additional Information + /// + /// Allows for addition between two `u64` values that would otherwise overflow. + /// + /// # Arguments + /// + /// * `right`: [u64] - The right-hand side of the addition. + /// + /// # Returns + /// + /// * [U128] - The result of the addition. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let x = u64::max(); + /// let y = u64::max(); + /// let z = x.overflowing_add(y); + /// + /// assert(z == U128 { upper: 1, lower: 18446744073709551614 }); + /// } + /// ``` + pub fn overflowing_add(self, right: Self) -> U128 { + let prior_flags = disable_panic_on_overflow(); + + let mut result = U128 { + upper: 0, + lower: 0, + }; + + asm(sum, overflow, left: self, right: right, result_ptr: result) { + // Add left and right. + add sum left right; + // Immediately copy the overflow of the addition from `$of` into + // `overflow` so that it's not lost. + move overflow of; + // Store the overflow into the first word of result. + sw result_ptr overflow i0; + // Store the sum into the second word of result. + sw result_ptr sum i1; + }; + + set_flags(prior_flags); + + result + } + + /// Performs multiplication between two `u64` values, returning a `U128`. + /// + /// # Additional Information + /// + /// Allows for multiplication between two `u64` values that would otherwise overflow. + /// + /// # Arguments + /// + /// * `right`: [u64] - The right-hand side of the multiplication. + /// + /// # Returns + /// + /// * [U128] - The result of the multiplication. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let x = u64::max(); + /// let y = u64::max(); + /// let z = x.overflowing_mul(y); + /// + /// assert(z == U128 { upper: 18446744073709551615, lower: 1 }); + /// } + /// ``` + pub fn overflowing_mul(self, right: Self) -> U128 { + let prior_flags = disable_panic_on_overflow(); + + let mut result = U128 { + upper: 0, + lower: 0, + }; + + asm(product, overflow, left: self, right: right, result_ptr: result) { + // Multiply left and right. + mul product left right; + // Immediately copy the overflow of the multiplication from `$of` into + // `overflow` so that it's not lost. + move overflow of; + // Store the overflow into the first word of result. + sw result_ptr overflow i0; + // Store the product into the second word of result. + sw result_ptr product i1; + }; + + set_flags(prior_flags); + + result + } +} + +impl U128 { + /// Initializes a new, zeroed `U128`. + /// + /// # Returns + /// + /// * [U128] - A new, zero value `U128`. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let new_u128 = U128::new(); + /// let zero_u128 = U128 { upper: 0, lower: 0 }; + /// + /// assert(new_u128 == zero_u128); + /// } + /// ``` + pub fn new() -> Self { + Self { + upper: 0, + lower: 0, + } + } + + /// Safely downcast to `u64` without loss of precision. + /// + /// # Additional Information + /// + /// If the `U128` is larger than `u64::max()`, an error is returned. + /// + /// # Returns + /// + /// * [Result] - The result of the downcast. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::{U128, U128Error}; + /// + /// fn foo() { + /// let zero_u128 = U128 { upper: 0, lower: 0 }; + /// let zero_u64 = zero_u128.as_u64().unwrap(); + /// + /// assert(zero_u64 == 0); + /// + /// let max_u128 = U128::max(); + /// let result = max_u128.as_u64(); + /// + /// assert(result.is_err())); + /// } + /// ``` + pub fn as_u64(self) -> Result { + match self.upper { + 0 => Ok(self.lower), + _ => Err(U128Error::LossOfPrecision), + } + } + + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [U128] - The smallest value that can be represented by this integer type, `0`. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let min_u128 = U128::min(); + /// let zero_u128 = U128 { upper: 0, lower: 0 }; + /// + /// assert(min_u128 == zero_u128); + /// } + /// ``` + pub fn min() -> Self { + Self { + upper: 0, + lower: 0, + } + } + + /// The largest value that can be represented by this type, + /// + /// # Returns + /// + /// * [U128] - The largest value that can be represented by this type, `2128 - 1`. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let max_u128 = U128::max(); + /// let maxed_u128 = U128 { upper: u64::max(), lower: u64::max() }; + /// + /// assert(max_u128 == maxed_u128); + /// } + /// ``` + pub fn max() -> Self { + Self { + upper: u64::max(), + lower: u64::max(), + } + } + + /// The size of this type in bits. + /// + /// # Returns + /// + /// * [u32] - The size of this type in bits, `128`. + /// + /// # Examples + /// + /// ```sway + /// use std::u128::U128; + /// + /// fn foo() { + /// let bits = U128::bits(); + /// + /// assert(bits == 128); + /// } + /// ``` + pub fn bits() -> u32 { + 128 + } +} + +impl core::ops::BitwiseAnd for U128 { + fn binary_and(self, other: Self) -> Self { + Self::from((self.upper & other.upper, self.lower & other.lower)) + } +} + +impl core::ops::BitwiseOr for U128 { + fn binary_or(self, other: Self) -> Self { + Self::from((self.upper | other.upper, self.lower | other.lower)) + } +} + +impl core::ops::Shift for U128 { + fn lsh(self, rhs: u64) -> Self { + // If shifting by at least the number of bits, then saturate with + // zeroes. + if rhs >= 128 { + return Self::new(); + } + + // If shifting by at least half the number of bits, then upper word can + // be discarded. + if rhs >= 64 { + return Self::from((self.lower << (rhs - 64), 0)); + } + + // If shifting by less than half the number of bits, then need to + // partially shift both upper and lower. + // Save highest bits of lower half. + let highest_lower_bits = self.lower >> (64 - rhs); + + let upper = (self.upper << rhs) + highest_lower_bits; + let lower = self.lower << rhs; + + Self::from((upper, lower)) + } + + fn rsh(self, rhs: u64) -> Self { + // If shifting by at least the number of bits, then saturate with + // zeroes. + if (rhs >= 128) { + return Self::new(); + } + + // If shifting by at least half the number of bits, then lower word can + // be discarded. + if (rhs >= 64) { + return Self::from((0, self.upper >> (rhs - 64))); + } + + // If shifting by less than half the number of bits, then need to + // partially shift both upper and lower. + // Save lowest bits of upper half. + let lowest_upper_bits = self.upper << (64 - rhs); + + let upper = self.upper >> rhs; + let lower = (self.lower >> rhs) + lowest_upper_bits; + + Self::from((upper, lower)) + } +} + +impl core::ops::Not for U128 { + fn not(self) -> Self { + Self { + upper: !self.upper, + lower: !self.lower, + } + } +} + +impl core::ops::Add for U128 { + /// Add a `U128` to a `U128`. Reverts on overflow. + fn add(self, other: Self) -> Self { + let mut upper_128 = self.upper.overflowing_add(other.upper); + + // If the upper overflows, then the number cannot fit in 128 bits, so panic. + assert(upper_128.upper == 0); + let lower_128 = self.lower.overflowing_add(other.lower); + + // If overflow has occurred in the lower component addition, carry. + // Note: carry can be at most 1. + if lower_128.upper > 0 { + upper_128 = upper_128.lower.overflowing_add(lower_128.upper); + } + + // If overflow has occurred in the upper component addition, panic. + assert(upper_128.upper == 0); + + Self { + upper: upper_128.lower, + lower: lower_128.lower, + } + } +} + +impl core::ops::Subtract for U128 { + /// Subtract a `U128` from a `U128`. Reverts of overflow. + fn subtract(self, other: Self) -> Self { + // If trying to subtract a larger number, panic. + assert(!(self < other)); + + let mut upper = self.upper - other.upper; + let mut lower = 0; + + // If necessary, borrow and carry for lower subtraction + if self.lower < other.lower { + lower = u64::max() - (other.lower - self.lower - 1); + upper -= 1; + } else { + lower = self.lower - other.lower; + } + + Self { upper, lower } + } +} +impl core::ops::Multiply for U128 { + /// Multiply a `U128` with a `U128`. Reverts of overflow. + fn multiply(self, other: Self) -> Self { + // in case both of the `U128` upper parts are bigger than zero, + // it automatically means overflow, as any `U128` value + // is upper part multiplied by 2 ^ 64 + lower part + assert(self.upper == 0 || other.upper == 0); + + let mut result = self.lower.overflowing_mul(other.lower); + if self.upper == 0 { + // panic in case of overflow + result.upper += self.lower * other.upper; + } else if other.upper == 0 { + // panic in case of overflow + result.upper += self.upper * other.lower; + } + + result + } +} + +impl core::ops::Divide for U128 { + /// Divide a `U128` by a `U128`. Reverts if divisor is zero. + fn divide(self, divisor: Self) -> Self { + let zero = Self::from((0, 0)); + + assert(divisor != zero); + + if self.upper == 0 && divisor.upper == 0 { + return Self::from((0, self.lower / divisor.lower)); + } + + let mut quotient = Self::new(); + let mut remainder = Self::new(); + let mut i = 128 - 1; + while true { + quotient <<= 1; + remainder <<= 1; + remainder.lower = remainder.lower | (self >> i).lower & 1; + // TODO use >= once OrdEq can be implemented. + if remainder > divisor || remainder == divisor { + remainder -= divisor; + quotient.lower = quotient.lower | 1; + } + + if i == 0 { + break; + } + + i -= 1; + } + + quotient + } +} + +impl Power for U128 { + fn pow(self, exponent: u32) -> Self { + let mut value = self; + let mut exp = exponent; + + if exp == 0 { + return Self::from((0, 1)); + } + + if exp == 1 { + // Manually clone `self`. Otherwise, we may have a `MemoryOverflow` + // issue with code that looks like: `x = x.pow(other)` + return Self::from((self.upper, self.lower)); + } + + while exp & 1 == 0 { + value = value * value; + exp >>= 1; + } + + if exp == 1 { + return value; + } + + let mut acc = value; + while exp > 1 { + exp >>= 1; + value = value * value; + if exp & 1 == 1 { + acc = acc * value; + } + } + acc + } +} + +impl Root for U128 { + /// Integer square root using [Newton's Method](https://en.wikipedia.org/wiki/Integer_square_root#Algorithm_using_Newton's_method). + fn sqrt(self) -> Self { + let zero = Self::from((0, 0)); + let mut x0 = self >> 1; + let mut s = self; + + if x0 != zero { + let mut x1 = (x0 + s / x0) >> 1; + + while x1 < x0 { + x0 = x1; + x1 = (x0 + self / x0) >> 1; + } + + return x0; + } else { + return s; + } + } +} + +impl BinaryLogarithm for U128 { + /// `log2` of `x` is the largest `n` such that `2^n <= x < 2^(n+1)`. + /// + /// * If `x` is smaller than `2^64`, we could just rely on the `log` method by setting + /// the base to 2. + /// * Otherwise, we can find the highest non-zero bit by taking the regular log of the upper + /// part of the `U128`, and then add 64. + fn log2(self) -> Self { + let zero = Self::from((0, 0)); + let mut res = zero; + // If trying to get a log2(0), panic, as infinity is not a number. + assert(self != zero); + if self.upper != 0 { + res = Self::from((0, self.upper.log(2) + 64)); + } else if self.lower != 0 { + res = Self::from((0, self.lower.log(2))); + } + res + } +} + +impl Logarithm for U128 { + fn log(self, base: Self) -> Self { + let self_log2 = self.log2(); + let base_log2 = base.log2(); + self_log2 / base_log2 + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/u256.sw b/sway-lsp/tests/fixtures/fixtures-std/src/u256.sw new file mode 100644 index 00000000000..1521889e87e --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/u256.sw @@ -0,0 +1,640 @@ +//! A 256-bit unsigned integer type. +library; + +use ::assert::assert; +use ::convert::From; +use ::result::Result::{self, *}; +use ::u128::U128; + +/// Left shift a `u64` and preserve the overflow amount if any. +fn lsh_with_carry(word: u64, shift_amount: u64) -> (u64, u64) { + let right_shift_amount = 64 - shift_amount; + let carry = word >> right_shift_amount; + let shifted = word << shift_amount; + (shifted, carry) +} + +/// Right shift a `u64` and preserve the overflow amount if any. +fn rsh_with_carry(word: u64, shift_amount: u64) -> (u64, u64) { + let left_shift_amount = 64 - shift_amount; + let carry = word << left_shift_amount; + let shifted = word >> shift_amount; + (shifted, carry) +} + +/// The 256-bit unsigned integer type. +/// +/// # Additional Information +/// +/// Represented as four 64-bit components: `(a, b, c, d)`, where `value = (a << 192) + (b << 128) + (c << 64) + d`. +#[deprecated(note = "use the built-in type `u256` instead")] +pub struct U256 { + /// The most significant 64 bits of the `U256`. + a: u64, + /// The 65-128th most significant bits of the `U256`. + b: u64, + /// The 129-192nd most significant bits of the `U256`. + c: u64, + /// The 193-256th most significant bits of the `U256`. + d: u64, +} + +/// The error type used for `U256` type errors. +pub enum U256Error { + /// This error occurs when a `U256` is attempted to be downcast to a `u64` or `u128` and the conversion would result in a loss of precision. + LossOfPrecision: (), +} + +impl From<(u64, u64, u64, u64)> for U256 { + #[allow(deprecated)] + fn from(components: (u64, u64, u64, u64)) -> Self { + Self { + a: components.0, + b: components.1, + c: components.2, + d: components.3, + } + } + + /// Function for extracting 4 `u64`s from a `U256`. + #[allow(deprecated)] + fn into(self) -> (u64, u64, u64, u64) { + (self.a, self.b, self.c, self.d) + } +} + +impl core::ops::Eq for U256 { + /// Function for comparing 2 `U256`s for equality. + #[allow(deprecated)] + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d + } +} + +impl U256 { + /// Initializes a new, zeroed `U256`. + /// + /// # Returns + /// + /// * [U256] - A new, zero value `U256`. + /// + /// # Examples + /// + /// ```sway + /// use std::u256::U256; + /// + /// fn foo() { + /// let new_u256 = U256::new(); + /// let zero_u256 = U256 { a: 0, b: 0, c: 0, d: 0 }; + /// + /// assert(new_u256 == zero_u256); + /// } + /// ``` + #[allow(deprecated)] + pub fn new() -> Self { + Self { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// Safely downcast to `u64` without loss of precision. + /// + /// # Additional Information + /// + /// If the `U256` is larger than `u64::max()`, an error will be returned. + /// + /// # Returns + /// + /// * [Result] - The `U256` as a `u64` or an error if the conversion would result in a loss of precision. + /// + /// # Examples + /// + /// ```sway + /// use std::u256::{U256, U256Error}; + /// + /// fn foo() { + /// let zero_u256 = U256 { a: 0, b: 0, c: 0, d: 0 }; + /// let zero_u64 = zero_u256.as_u64().unwrap(); + /// + /// assert(zero_u64 == 0); + /// + /// let max_u256 = U256::max(); + /// let result = U256.as_u64(); + /// + /// assert(result.is_err())) + /// } + /// ``` + #[allow(deprecated)] + pub fn as_u64(self) -> Result { + if self.a == 0 && self.b == 0 && self.c == 0 { + Ok(self.d) + } else { + Err(U256Error::LossOfPrecision) + } + } + + /// Safely downcast to `U128` without loss of precision. + /// + /// # Additional Information + /// + /// If the `U256` is larger than `U128::max()`, an error will be returned. + /// + /// # Returns + /// + /// * [Result] - The `U256` as a `U128` or an error if the conversion would result in a loss of precision. + /// + /// # Examples + /// + /// ```sway + /// use std::{u128::U128, u256::{U256, U256Error}}; + /// + /// fn foo() { + /// let zero_u256 = U256 { a: 0, b: 0, c: 0, d: 0 }; + /// let zero_u128 = zero_u256.as_u128().unwrap(); + /// + /// assert(zero_u128 == U128 { upper: 0, lower: 0 }); + /// + /// let max_u256 = U256::max(); + /// let result = U256.as_u64(); + /// + /// assert(result.is_err())) + /// } + /// ``` + #[allow(deprecated)] + pub fn as_u128(self) -> Result { + if self.a == 0 && self.b == 0 { + Ok(U128::from((self.c, self.d))) + } else { + Err(U256Error::LossOfPrecision) + } + } + + /// The smallest value that can be represented by this integer type. + /// + /// # Returns + /// + /// * [U256] - The smallest value that can be represented by this integer type, `0`. + /// + /// # Examples + /// + /// ```sway + /// use std::u256::U256; + /// + /// fn foo() { + /// let min_u256 = U256::min(); + /// let zero_u256 = U256 { a: 0, b: 0, c: 0, d: 0 }; + /// + /// assert(min_u256 == zero_u256); + /// } + /// ``` + #[allow(deprecated)] + pub fn min() -> Self { + Self { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// The largest value that can be represented by this type. + /// + /// # Returns + /// + /// * [U256] - The largest value that can be represented by this type, `2256 - 1`. + /// + /// # Examples + /// + /// ```sway + /// use std::u256::U256; + /// + /// fn foo() { + /// let max_u256 = U256::max(); + /// let maxed_u256 = U256 { a: u64::max(), b: u64::max(), c: u64::max(), d: u64::max() }; + /// + /// assert(max_u256 == maxed_u256); + /// } + /// ``` + #[allow(deprecated)] + pub fn max() -> Self { + Self { + a: u64::max(), + b: u64::max(), + c: u64::max(), + d: u64::max(), + } + } + + /// The size of this type in bits. + /// + /// # Returns + /// + /// * [u32] - The size of this type in bits, `256`. + /// + /// # Examples + /// + /// ```sway + /// use std::u256::U256; + /// + /// let bits = U256::bits(); + /// + /// assert(bits == 256); + /// ``` + #[allow(deprecated)] + pub fn bits() -> u32 { + 256 + } +} + +impl core::ops::Ord for U256 { + #[allow(deprecated)] + fn gt(self, other: Self) -> bool { + self.a > other.a || + (self.a == other.a && (self.b > other.b || + (self.b == other.b && (self.c > other.c || + (self.c == other.c && self.d > other.d)))) ) + } + + #[allow(deprecated)] + fn lt(self, other: Self) -> bool { + self.a < other.a || + (self.a == other.a && (self.b < other.b || + (self.b == other.b && (self.c < other.c || + (self.c == other.c && self.d < other.d)))) ) + } +} + +#[test] +fn test_u256_ord() { + assert(U256::from((0, 0, 0, 1)) < U256::from((0, u64::max(), 0, 0))); + assert(!(U256::from((0, 0, 0, 1)) > U256::from((0, u64::max(), 0, 0)))); + + assert(U256::from((0, u64::max(), 0, 0)) > U256::from((0, 0, 0, 1))); + assert(!(U256::from((0, u64::max(), 0, 0)) < U256::from((0, 0, 0, 1)))); + + assert(U256::max() > U256::from((0, 0, u64::max(), u64::max()))); + assert(!(U256::max() < U256::from((0, 0, u64::max(), u64::max())))); + assert(U256::from((0, 0, u64::max(), u64::max())) < U256::max()); + assert(!(U256::from((0, 0, u64::max(), u64::max())) > U256::max())); +} + +impl core::ops::BitwiseAnd for U256 { + #[allow(deprecated)] + fn binary_and(self, other: Self) -> Self { + let (value_word_1, value_word_2, value_word_3, value_word_4) = self.into(); + let (other_word_1, other_word_2, other_word_3, other_word_4) = other.into(); + let word_1 = value_word_1 & other_word_1; + let word_2 = value_word_2 & other_word_2; + let word_3 = value_word_3 & other_word_3; + let word_4 = value_word_4 & other_word_4; + Self::from((word_1, word_2, word_3, word_4)) + } +} + +impl core::ops::BitwiseOr for U256 { + #[allow(deprecated)] + fn binary_or(self, other: Self) -> Self { + let (value_word_1, value_word_2, value_word_3, value_word_4) = self.into(); + let (other_word_1, other_word_2, other_word_3, other_word_4) = other.into(); + let word_1 = value_word_1 | other_word_1; + let word_2 = value_word_2 | other_word_2; + let word_3 = value_word_3 | other_word_3; + let word_4 = value_word_4 | other_word_4; + Self::from((word_1, word_2, word_3, word_4)) + } +} + +impl core::ops::BitwiseXor for U256 { + #[allow(deprecated)] + fn binary_xor(self, other: Self) -> Self { + let (value_word_1, value_word_2, value_word_3, value_word_4) = self.into(); + let (other_word_1, other_word_2, other_word_3, other_word_4) = other.into(); + let word_1 = value_word_1 ^ other_word_1; + let word_2 = value_word_2 ^ other_word_2; + let word_3 = value_word_3 ^ other_word_3; + let word_4 = value_word_4 ^ other_word_4; + Self::from((word_1, word_2, word_3, word_4)) + } +} + +impl core::ops::Shift for U256 { + #[allow(deprecated)] + fn lsh(self, shift_amount: u64) -> Self { + let (word_1, word_2, word_3, word_4) = self.into(); + let mut w1 = 0; + let mut w2 = 0; + let mut w3 = 0; + let mut w4 = 0; + + let w = shift_amount / 64; // num of whole words to shift in addition to b + let b = shift_amount % 64; // num of bits to shift within each word + if w == 0 { + let (shifted_2, carry_2) = lsh_with_carry(word_2, b); + w1 = (word_1 << b) + carry_2; + let (shifted_3, carry_3) = lsh_with_carry(word_3, b); + w2 = shifted_2 + carry_3; + let (shifted_4, carry_4) = lsh_with_carry(word_4, b); + w3 = shifted_3 + carry_4; + w4 = shifted_4; + } else if w == 1 { + let (shifted_3, carry_3) = lsh_with_carry(word_3, b); + w1 = (word_2 << b) + carry_3; + let (shifted_4, carry_4) = lsh_with_carry(word_4, b); + w2 = shifted_3 + carry_4; + w3 = shifted_4; + } else if w == 2 { + let (shifted_4, carry_4) = lsh_with_carry(word_4, b); + w1 = (word_3 << b) + carry_4; + w2 = shifted_4; + } else if w == 3 { + w1 = word_4 << b; + } + + Self::from((w1, w2, w3, w4)) + } + + #[allow(deprecated)] + fn rsh(self, shift_amount: u64) -> Self { + let (word_1, word_2, word_3, word_4) = self.into(); + let mut w1 = 0; + let mut w2 = 0; + let mut w3 = 0; + let mut w4 = 0; + + let w = shift_amount / 64; // num of whole words to shift in addition to b + let b = shift_amount % 64; // num of bits to shift within each word + if w == 0 { + let (shifted_3, carry_3) = rsh_with_carry(word_3, b); + w4 = (word_4 >> b) + carry_3; + let (shifted_2, carry_2) = rsh_with_carry(word_2, b); + w3 = shifted_3 + carry_2; + let (shifted_1, carry_1) = rsh_with_carry(word_1, b); + w2 = shifted_2 + carry_1; + w1 = shifted_1; + } else if w == 1 { + let (shifted_2, carry_2) = rsh_with_carry(word_2, b); + w4 = (word_3 >> b) + carry_2; + let (shifted_1, carry_1) = rsh_with_carry(word_1, b); + w3 = shifted_2 + carry_1; + w2 = shifted_1; + } else if w == 2 { + let (shifted_1, carry_1) = rsh_with_carry(word_1, b); + w4 = (word_2 >> b) + carry_1; + w3 = shifted_1; + } else if w == 3 { + w4 = word_1 >> b; + }; + + Self::from((w1, w2, w3, w4)) + } +} + +impl core::ops::Not for U256 { + #[allow(deprecated)] + fn not(self) -> Self { + Self { + a: !self.a, + b: !self.b, + c: !self.c, + d: !self.d, + } + } +} + +impl core::ops::Add for U256 { + /// Add a `U256` to a `U256`. Reverts on overflow. + #[allow(deprecated)] + fn add(self, other: Self) -> Self { + let (word_1, word_2, word_3, word_4) = self.into(); + let (other_word_1, other_word_2, other_word_3, other_word_4) = other.into(); + + let mut overflow = 0; + let mut local_res = U128::from((0, word_4)) + U128::from((0, other_word_4)); + let result_d = local_res.lower; + overflow = local_res.upper; + + local_res = U128::from((0, word_3)) + U128::from((0, other_word_3)) + U128::from((0, overflow)); + let result_c = local_res.lower; + overflow = local_res.upper; + + local_res = U128::from((0, word_2)) + U128::from((0, other_word_2)) + U128::from((0, overflow)); + let result_b = local_res.lower; + overflow = local_res.upper; + + local_res = U128::from((0, word_1)) + U128::from((0, other_word_1)) + U128::from((0, overflow)); + let result_a = local_res.lower; + // panic on overflow + assert(local_res.upper == 0); + Self::from((result_a, result_b, result_c, result_d)) + } +} + +impl core::ops::Subtract for U256 { + /// Subtract a `U256` from a `U256`. Reverts of overflow. + #[allow(deprecated)] + fn subtract(self, other: Self) -> Self { + if self == other { + return Self::min(); + } else if other == Self::min() { + // Manually clone `self`. Otherwise, we may have a `MemoryOverflow` + // issue with code that looks like: `x = x - other` + return Self::from((self.a, self.b, self.c, self.d)); + } + // If trying to subtract a larger number, panic. + assert(self > other); + let (word_1, word_2, word_3, word_4) = self.into(); + let (other_word_1, other_word_2, other_word_3, other_word_4) = other.into(); + + let mut result_a = word_1 - other_word_1; + let mut result_b = 0; + if word_2 < other_word_2 { + result_b = u64::max() - (other_word_2 - word_2 - 1); + // we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs, + // which we ruled out at the beginning of the function. + result_a -= 1; + } else { + result_b = word_2 - other_word_2; + } + let mut result_c = 0; + if word_3 < other_word_3 { + result_c = u64::max() - (other_word_3 - word_3 - 1); + if result_b > 0 { + result_b -= 1; + } else { + // we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs, + // which we ruled out at the beginning of the function. + result_a -= 1; + result_b = u64::max(); + } + } else { + result_c = word_3 - other_word_3; + } + + let mut result_d = 0; + if word_4 < other_word_4 { + result_d = u64::max() - (other_word_4 - word_4 - 1); + if result_c > 0 { + result_c -= 1; + } else { + if result_b > 0 { + result_b -= 1; + } else { + // we assume that result_a > 0, as in case result_a <= 0 means that lhs of the operation is smaller than rhs, + // which we ruled out at the beginning of the function. + result_a -= 1; + result_b = u64::max(); + } + result_c = u64::max(); + } + } else { + result_d = word_4 - other_word_4; + } + + Self::from((result_a, result_b, result_c, result_d)) + } +} + +impl core::ops::Multiply for U256 { + /// Multiply a `U256` with a `U256`. Reverts on overflow. + fn multiply(self, other: Self) -> Self { + // Both upper words cannot be non-zero simultaneously. Otherwise, overflow is guaranteed. + assert(self.a == 0 || other.a == 0); + + if self.a != 0 { + // If `self.a` is non-zero, all words of `other`, except for `d`, should be zero. + // Otherwise, overflow is guaranteed. + assert(other.b == 0 && other.c == 0); + Self::from((self.a * other.d, 0, 0, 0)) + } else if other.a != 0 { + // If `other.a` is non-zero, all words of `self`, except for `d`, should be zero. + // Otherwise, overflow is guaranteed. + assert(self.b == 0 && self.c == 0); + Self::from((other.a * self.d, 0, 0, 0)) + } else { + if self.b != 0 { + // If `self.b` is non-zero, `other.b` has to be zero. Otherwise, overflow is + // guaranteed because: + // `other.b * 2 ^ (64 * 2) * self.b * 2 ^ (62 ^ 2) > 2 ^ (64 * 4)` + assert(other.b == 0); + let result_b_d = self.b.overflowing_mul(other.d); + let result_c_d = self.c.overflowing_mul(other.d); + let result_d_c = self.d.overflowing_mul(other.c); + let result_d_d = self.d.overflowing_mul(other.d); + + let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into(); + let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into(); + + let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into(); + + let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper).into(); + let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into(); + let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into(); + + Self::from(( + self.b * other.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0, + b, + c, + result_d_d.lower, + )) + } else if other.b != 0 { + // If `other.b` is nonzero, `self.b` has to be zero. Otherwise, overflow is + // guaranteed because: + // `other.b * 2 ^ (64 * 2) * self.b * 2 ^ (62 ^ 2) > 2 ^ (64 * 4)`. + assert(self.b == 0); + let result_b_d = other.b.overflowing_mul(self.d); + let result_c_d = other.c.overflowing_mul(self.d); + let result_d_c = other.d.overflowing_mul(self.c); + let result_d_d = other.d.overflowing_mul(self.d); + + let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into(); + let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into(); + + let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into(); + + let (overflow_of_b_to_a_1, mut b) = result_b_d.lower.overflowing_add(result_c_d.upper).into(); + let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into(); + let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into(); + + Self::from(( + other.b * self.c + result_b_d.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0, + b, + c, + result_d_d.lower, + )) + } else { + // note, that `self.a`, `self.b`, `other.a`, `other.b` are all equal to 0 + let result_c_c = other.c.overflowing_mul(self.c); + let result_c_d = self.c.overflowing_mul(other.d); + let result_d_c = self.d.overflowing_mul(other.c); + let result_d_d = self.d.overflowing_mul(other.d); + + let (overflow_of_c_to_b_1, mut c) = result_d_d.upper.overflowing_add(result_c_d.lower).into(); + + let (mut overflow_of_c_to_b_2, c) = c.overflowing_add(result_d_c.lower).into(); + + let (overflow_of_b_to_a_0, overflow_of_c_to_b_2) = overflow_of_c_to_b_1.overflowing_add(overflow_of_c_to_b_2).into(); + + let (overflow_of_b_to_a_1, mut b) = result_c_c.lower.overflowing_add(result_c_d.upper).into(); + let (overflow_of_b_to_a_2, b) = b.overflowing_add(result_d_c.upper).into(); + let (overflow_of_b_to_a_3, b) = b.overflowing_add(overflow_of_c_to_b_2).into(); + + Self::from(( + // as overflow for a means overflow for the whole number, we are adding as is, not using `overflowing_add` + result_c_c.upper + overflow_of_b_to_a_3 + overflow_of_b_to_a_2 + overflow_of_b_to_a_1 + overflow_of_b_to_a_0, + b, + c, + result_d_d.lower, + )) + } + } + } +} + +impl core::ops::Divide for U256 { + /// Divide a `U256` by a `U256`. Reverts if divisor is zero. + #[allow(deprecated)] + fn divide(self, divisor: Self) -> Self { + let zero = Self::from((0, 0, 0, 0)); + let one = Self::from((0, 0, 0, 1)); + + assert(divisor != zero); + + if self.a == 0 + && self.b == 0 + && divisor.a == 0 + && divisor.b == 0 + { + let res = U128::from((self.c, self.d)) / U128::from((divisor.c, divisor.d)); + return Self::from((0, 0, res.upper, res.lower)); + } + + let mut quotient = Self::from((0, 0, 0, 0)); + let mut remainder = Self::from((0, 0, 0, 0)); + + let mut i = 256 - 1; + + while true { + quotient <<= 1; + remainder <<= 1; + + let _m = self & (one << i); + remainder.d = remainder.d | (self >> i).d & 1; + // TODO use >= once OrdEq can be implemented. + if remainder > divisor || remainder == divisor { + remainder -= divisor; + quotient.d = quotient.d | 1; + } + + if i == 0 { + break; + } + + i -= 1; + } + + quotient + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/vec.sw b/sway-lsp/tests/fixtures/fixtures-std/src/vec.sw new file mode 100644 index 00000000000..17eac92738f --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/vec.sw @@ -0,0 +1,623 @@ +//! A vector type for dynamically sized arrays outside of storage. +library; + +use ::alloc::{alloc, realloc}; +use ::assert::assert; +use ::option::Option::{self, *}; +use ::convert::From; + +struct RawVec { + ptr: raw_ptr, + cap: u64, +} + +impl RawVec { + /// Create a new `RawVec` with zero capacity. + /// + /// # Returns + /// + /// * [RawVec] - A new `RawVec` with zero capacity. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::RawVec; + /// + /// fn foo() { + /// let vec = RawVec::new(); + /// } + /// ``` + pub fn new() -> Self { + Self { + ptr: alloc::(0), + cap: 0, + } + } + + /// Creates a `RawVec` (on the heap) with exactly the capacity for a + /// `[T; capacity]`. This is equivalent to calling `RawVec::new` when + /// `capacity` is zero. + /// + /// # Arguments + /// + /// * `capacity`: [u64] - The capacity of the `RawVec`. + /// + /// # Returns + /// + /// * [RawVec] - A new `RawVec` with zero capacity. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::RawVec; + /// + /// fn foo() { + /// let vec = RawVec::with_capacity(5); + /// } + /// ``` + pub fn with_capacity(capacity: u64) -> Self { + Self { + ptr: alloc::(capacity), + cap: capacity, + } + } + + /// Gets the pointer of the allocation. + /// + /// # Returns + /// + /// * [raw_ptr] - The pointer of the allocation. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::RawVec; + /// + /// fn foo() { + /// let vec = RawVec::new(); + /// let ptr = vec.ptr(); + /// let end = ptr.add::(0); + /// end.write(5); + /// assert(end.read::() == 5); + /// } + pub fn ptr(self) -> raw_ptr { + self.ptr + } + + /// Gets the capacity of the allocation. + /// + /// # Returns + /// + /// * [u64] - The capacity of the allocation. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::RawVec; + /// + /// fn foo() { + /// let vec = RawVec::with_capacity(5); + /// let cap = vec.capacity(); + /// assert(cap == 5); + /// } + pub fn capacity(self) -> u64 { + self.cap + } + + /// Grow the capacity of the vector by doubling its current capacity. The + /// `realloc` function allocates memory on the heap and copies the data + /// from the old allocation to the new allocation. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::RawVec; + /// + /// fn foo() { + /// let mut vec = RawVec::new(); + /// vec.grow(); + /// assert(vec.capacity() == 1); + /// vec.grow(); + /// assert(vec.capacity() == 2); + /// } + pub fn grow(ref mut self) { + let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap }; + + self.ptr = realloc::(self.ptr, self.cap, new_cap); + self.cap = new_cap; + } +} + +/// A contiguous growable array type, written as `Vec`, short for 'vector'. +pub struct Vec { + buf: RawVec, + len: u64, +} + +impl Vec { + /// Constructs a new, empty `Vec`. + /// + /// # Additional Information + /// + /// The vector will not allocate until elements are pushed onto it. + /// + /// # Returns + /// + /// * [Vec] - A new, empty `Vec`. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// // allocates when an element is pushed + /// vec.push(5); + /// } + /// ``` + pub fn new() -> Self { + Self { + buf: RawVec::new(), + len: 0, + } + } + + /// Constructs a new, empty `Vec` with the specified capacity. + /// + /// # Additional Information + /// + /// The vector will be able to hold exactly `capacity` elements without + /// reallocating. If `capacity` is zero, the vector will not allocate. + /// + /// It is important to note that although the returned vector has the + /// *capacity* specified, the vector will have a zero *length*. + /// + /// # Arguments + /// + /// * `capacity`: [u64] - The capacity of the `Vec`. + /// + /// # Returns + /// + /// * [Vec] - A new, empty `Vec` with the specified capacity. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::with_capacity(2); + /// // does not allocate + /// vec.push(5); + /// // does not re-allocate + /// vec.push(10); + /// // allocates + /// vec.push(15); + /// } + /// ``` + pub fn with_capacity(capacity: u64) -> Self { + Self { + buf: RawVec::with_capacity(capacity), + len: 0, + } + } + + /// Appends an element at the end of the collection. + /// + /// # Arguments + /// + /// * `value`: [T] - The value to be pushed onto the end of the collection. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// let last_element = vec.pop().unwrap(); + /// assert(last_element == 5); + /// } + ///``` + pub fn push(ref mut self, value: T) { + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.capacity() { + self.buf.grow(); + }; + + // Get a pointer to the end of the buffer, where the new element will + // be inserted. + let end = self.buf.ptr().add::(self.len); + + // Write `value` at pointer `end` + end.write::(value); + + // Increment length. + self.len += 1; + } + + /// Gets the capacity of the allocation. + /// + /// # Returns + /// + /// * [u64] - The capacity of the allocation. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::with_capacity(5); + /// let cap = vec.capacity(); + /// assert(cap == 5); + /// } + /// ``` + pub fn capacity(self) -> u64 { + self.buf.cap + } + + /// Clears the vector, removing all values. + /// + /// Note that this method has no effect on the allocated capacity + /// of the vector. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.clear() + /// assert(vec.is_empty()); + /// } + /// ``` + pub fn clear(ref mut self) { + self.len = 0; + } + + /// Fetches the element stored at `index` + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be fetched. + /// + /// # Returns + /// + /// * [Option] - The element stored at `index`, or `None` if `index` is out of bounds. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.push(10); + /// vec.push(15); + /// let item = vec.get(1).unwrap(); + /// assert(item == 10); + /// let res = vec.get(10); + /// assert(res.is_none()); // index out of bounds + /// } + /// ``` + pub fn get(self, index: u64) -> Option { + // First check that index is within bounds. + if self.len <= index { + return None; + }; + + // Get a pointer to the desired element using `index` + let ptr = self.buf.ptr().add::(index); + + // Read from `ptr` + Some(ptr.read::()) + } + + /// Returns the number of elements in the vector, also referred to + /// as its `length`. + /// + /// # Returns + /// + /// * [u64] - The length of the vector. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// assert(vec.len() == 1); + /// vec.push(10); + /// assert(vec.len() == 2); + /// } + /// ``` + pub fn len(self) -> u64 { + self.len + } + + /// Returns whether the vector is empty. + /// + /// # Returns + /// + /// * [bool] - `true` if the vector is empty, `false` otherwise. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// assert(vec.is_empty()); + /// vec.push(5); + /// assert(!vec.is_empty()); + /// } + /// ``` + pub fn is_empty(self) -> bool { + self.len == 0 + } + + /// Removes and returns the element at position `index` within the vector, + /// shifting all elements after it to the left. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be removed. + /// + /// # Returns + /// + /// * [T] - The element that was removed. + /// + /// # Reverts + /// + /// * If `index >= self.len` + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.push(10); + /// vec.push(15); + /// let item = vec.remove(1); + /// assert(item == 10); + /// assert(vec.get(0).unwrap() == 5); + /// assert(vec.get(1).unwrap() == 15); + /// assert(vec.get(2).is_none()); + /// } + /// ``` + pub fn remove(ref mut self, index: u64) -> T { + assert(index < self.len); + + let buf_start = self.buf.ptr(); + + // Read the value at `index` + let ptr = buf_start.add::(index); + let ret = ptr.read::(); + + // Shift everything down to fill in that spot. + let mut i = index; + if self.len > 1 { + while i < self.len { + let ptr = buf_start.add::(i); + ptr.add::(1).copy_to::(ptr, 1); + i += 1; + } + } + + // Decrease length. + self.len -= 1; + ret + } + + /// Inserts an element at position `index` within the vector, shifting all + /// elements after it to the right. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index at which to insert the element. + /// + /// * `element`: [T] - The element to be inserted. + /// + /// # Reverts + /// + /// * If `index > self.len` + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.push(10); + /// + /// vec.insert(1, 15); + /// + /// assert(vec.get(0).unwrap() == 5); + /// assert(vec.get(1).unwrap() == 15); + /// assert(vec.get(2).unwrap() == 10); + /// } + /// ``` + pub fn insert(ref mut self, index: u64, element: T) { + assert(index <= self.len); + + // If there is insufficient capacity, grow the buffer. + if self.len == self.buf.cap { + self.buf.grow(); + } + + let buf_start = self.buf.ptr(); + + // The spot to put the new value + let index_ptr = buf_start.add::(index); + + // Shift everything over to make space. + let mut i = self.len; + while i > index { + let ptr = buf_start.add::(i); + ptr.sub::(1).copy_to::(ptr, 1); + i -= 1; + } + + // Write `element` at pointer `index` + index_ptr.write::(element); + + // Increment length. + self.len += 1; + } + + /// Removes the last element from a vector and returns it. + /// + /// # Returns + /// + /// * [Option] - The last element of the vector, or `None` if the vector is empty. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// + /// let res = vec.pop(); + /// assert(res.is_none()); + /// + /// vec.push(5); + /// let res = vec.pop(); + /// assert(res.unwrap() == 5); + /// assert(vec.is_empty()); + /// } + /// ``` + pub fn pop(ref mut self) -> Option { + if self.len == 0 { + return None; + } + self.len -= 1; + Some(self.buf.ptr().add::(self.len).read::()) + } + + /// Swaps two elements. + /// + /// # Arguments + /// + /// * `element1_index`: [u64] - The index of the first element. + /// * `element2_index`: [u64] - The index of the second element. + /// + /// # Reverts + /// + /// * If `element1_index` or `element2_index` is greater than or equal to the length of vector. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.push(10); + /// + /// vec.swap(0, 1); + /// + /// assert(vec.get(0).unwrap() == 10); + /// assert(vec.get(1).unwrap() == 5); + /// } + /// ``` + pub fn swap(ref mut self, element1_index: u64, element2_index: u64) { + assert(element1_index < self.len); + assert(element2_index < self.len); + + if element1_index == element2_index { + return; + } + + let element1_ptr = self.buf.ptr().add::(element1_index); + let element2_ptr = self.buf.ptr().add::(element2_index); + + let element1_val: T = element1_ptr.read::(); + element2_ptr.copy_to::(element1_ptr, 1); + element2_ptr.write::(element1_val); + } + + /// Updates an element at position `index` with a new element `value`. + /// + /// # Arguments + /// + /// * `index`: [u64] - The index of the element to be set. + /// * `value`: [T] - The value of the element to be set. + /// + /// # Reverts + /// + /// * If `index` is greater than or equal to the length of vector. + /// + /// # Examples + /// + /// ```sway + /// use std::vec::Vec; + /// + /// fn foo() { + /// let vec = Vec::new(); + /// vec.push(5); + /// vec.push(10); + /// + /// vec.set(0, 15); + /// + /// assert(vec.get(0).unwrap() == 15); + /// assert(vec.get(1).unwrap() == 10); + /// } + /// ``` + pub fn set(ref mut self, index: u64, value: T) { + assert(index < self.len); + + let index_ptr = self.buf.ptr().add::(index); + + index_ptr.write::(value); + } +} + +impl AsRawSlice for Vec { + fn as_raw_slice(self) -> raw_slice { + raw_slice::from_parts::(self.buf.ptr(), self.len) + } +} + +impl From for Vec { + fn from(slice: raw_slice) -> Self { + let buf = RawVec { + ptr: slice.ptr(), + cap: slice.len::(), + }; + Self { + buf, + len: buf.cap, + } + } + + fn into(self) -> raw_slice { + asm(ptr: (self.buf.ptr(), self.len)) { ptr: raw_slice } + } +} + +#[test()] +fn test_vec_with_len_1() { + let mut ve: Vec = Vec::new(); + assert(ve.len == 0); + ve.push(1); + assert(ve.len == 1); + let _ = ve.remove(0); + assert(ve.len == 0); +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/vm.sw b/sway-lsp/tests/fixtures/fixtures-std/src/vm.sw new file mode 100644 index 00000000000..575be8e0b29 --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/vm.sw @@ -0,0 +1,4 @@ +//! VM-specific utilities. +library; + +pub mod evm; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm.sw b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm.sw new file mode 100644 index 00000000000..f53bf2e502f --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm.sw @@ -0,0 +1,5 @@ +//! EVM-specific utilities. +library; + +pub mod evm_address; +pub mod ecr; diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/ecr.sw b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/ecr.sw new file mode 100644 index 00000000000..2a1bb375afa --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/ecr.sw @@ -0,0 +1,54 @@ +//! Helper functions to verify EVM signatures. +library; + +use ::b512::B512; +use ::registers::error; +use ::ecr::{ec_recover, EcRecoverError}; +use ::hash::*; +use ::result::Result::{self, *}; +use ::vm::evm::evm_address::EvmAddress; + +/// Recover the EVM address derived from the private key used to sign a message. +/// Returns a `Result` to let the caller choose an error handling strategy. +/// +/// # Arguments +/// +/// * `signature`: [B512] - The signature generated by signing a message hash. +/// * `msg_hash`: [b256] - The signed data. +/// +/// # Returns +/// +/// * [Result] - The recovered evm address or an error. +/// +/// # Examples +/// +/// ```sway +/// use std::{vm::evm::{evm_address::EvmAddress, ecr::ec_recover_evm_address}, b512::B512}; +/// +/// fn foo() { +/// let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c; +/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; +/// let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323; +/// let evm_address = EvmAddress::from(0x7AAE2D980BE4C3275C72CE5B527FA23FFB97B766966559DD062E2B78FD9D3766); +/// let signature: B512 = B512::from((hi, lo)); +/// // A recovered evm address. +/// let result_address = ec_recover_evm_address(signature, msg_hash).unwrap(); +/// assert(result_address == evm_address); +/// } +/// ``` +pub fn ec_recover_evm_address( + signature: B512, + msg_hash: b256, +) -> Result { + let pub_key_result = ec_recover(signature, msg_hash); + + match pub_key_result { + Result::Err(e) => Result::Err(e), + _ => { + let pub_key = pub_key_result.unwrap(); + // Note that EVM addresses are derived from the Keccak256 hash of the pubkey (not sha256) + let pubkey_hash = keccak256(((pub_key.bytes)[0], (pub_key.bytes)[1])); + Ok(EvmAddress::from(pubkey_hash)) + } + } +} diff --git a/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/evm_address.sw b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/evm_address.sw new file mode 100644 index 00000000000..da01bba6edd --- /dev/null +++ b/sway-lsp/tests/fixtures/fixtures-std/src/vm/evm/evm_address.sw @@ -0,0 +1,37 @@ +//! A wrapper around the `b256` type to help enhance type-safety. +library; + +use ::intrinsics::size_of_val; +use ::convert::From; + +/// The `EvmAddress` type, a struct wrapper around the inner `b256` value. +pub struct EvmAddress { + /// The underlying evm address data. + value: b256, +} + +impl core::ops::Eq for EvmAddress { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} + +/// Functions for casting between the `b256` and `EvmAddress` types. +impl From for EvmAddress { + fn from(bits: b256) -> Self { + // An EVM address is only 20 bytes, so the first 12 are set to zero + // Create a mutable local copy of `bits` + let mut local_bits = bits; + asm(r1: local_bits) { + mcli r1 i12; + }; + + Self { + value: local_bits, + } + } + + fn into(self) -> b256 { + self.value + } +} diff --git a/sway-lsp/tests/fixtures/renaming/Forc.toml b/sway-lsp/tests/fixtures/renaming/Forc.toml index 6b55bb7ecd3..54c4a570478 100644 --- a/sway-lsp/tests/fixtures/renaming/Forc.toml +++ b/sway-lsp/tests/fixtures/renaming/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "renaming" [dependencies] -std = { path = "../../../../sway-lib-std" } +std = { path = "../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/runnables/Forc.toml b/sway-lsp/tests/fixtures/runnables/Forc.toml index 49dcb847cd4..afc5bbc21fa 100644 --- a/sway-lsp/tests/fixtures/runnables/Forc.toml +++ b/sway-lsp/tests/fixtures/runnables/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "script_multi_test" [dependencies] -std = { path = "../../../../sway-lib-std" } +std = { path = "../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/abi/Forc.toml b/sway-lsp/tests/fixtures/tokens/abi/Forc.toml index 39f55d52723..707526d4e4a 100644 --- a/sway-lsp/tests/fixtures/tokens/abi/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/abi/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "abi" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/consts/Forc.toml b/sway-lsp/tests/fixtures/tokens/consts/Forc.toml index 6b721796259..0753c2940ca 100644 --- a/sway-lsp/tests/fixtures/tokens/consts/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/consts/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "consts" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/enums/Forc.toml b/sway-lsp/tests/fixtures/tokens/enums/Forc.toml index 64f5911f772..6041f0d78b4 100644 --- a/sway-lsp/tests/fixtures/tokens/enums/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/enums/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "enums" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/fields/Forc.toml b/sway-lsp/tests/fixtures/tokens/fields/Forc.toml index 7384cc145e2..ec956e4c606 100644 --- a/sway-lsp/tests/fixtures/tokens/fields/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/fields/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "fields" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/functions/Forc.toml b/sway-lsp/tests/fixtures/tokens/functions/Forc.toml index bf90be0771f..60614bde81f 100644 --- a/sway-lsp/tests/fixtures/tokens/functions/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/functions/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "functions" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/impls/Forc.toml b/sway-lsp/tests/fixtures/tokens/impls/Forc.toml index 22d0f9c4df8..0868899575b 100644 --- a/sway-lsp/tests/fixtures/tokens/impls/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/impls/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "impls" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/matches/Forc.toml b/sway-lsp/tests/fixtures/tokens/matches/Forc.toml index 0ef64faf03d..bdde1aa975c 100644 --- a/sway-lsp/tests/fixtures/tokens/matches/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/matches/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "matches" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/modules/Forc.toml b/sway-lsp/tests/fixtures/tokens/modules/Forc.toml index 21fbf2607e5..310c32f5d0a 100644 --- a/sway-lsp/tests/fixtures/tokens/modules/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/modules/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "turbofish" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/paths/Forc.toml b/sway-lsp/tests/fixtures/tokens/paths/Forc.toml index ed69a023d35..46be2951369 100644 --- a/sway-lsp/tests/fixtures/tokens/paths/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/paths/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "paths" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/storage/Forc.toml b/sway-lsp/tests/fixtures/tokens/storage/Forc.toml index bad2ce107c6..6c87c2e502e 100644 --- a/sway-lsp/tests/fixtures/tokens/storage/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/storage/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "storage" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/structs/Forc.toml b/sway-lsp/tests/fixtures/tokens/structs/Forc.toml index 850fa8cc16c..7b67a0b2dd8 100644 --- a/sway-lsp/tests/fixtures/tokens/structs/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/structs/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "structs" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/traits/Forc.toml b/sway-lsp/tests/fixtures/tokens/traits/Forc.toml index c96a65ec797..f397f0806b2 100644 --- a/sway-lsp/tests/fixtures/tokens/traits/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/traits/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "traits" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/turbofish/Forc.toml b/sway-lsp/tests/fixtures/tokens/turbofish/Forc.toml index ed2b95f9803..a926364fe73 100644 --- a/sway-lsp/tests/fixtures/tokens/turbofish/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/turbofish/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "turbofish" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/variables/Forc.toml b/sway-lsp/tests/fixtures/tokens/variables/Forc.toml index 89f0c3a8d4e..1718c4dc22b 100644 --- a/sway-lsp/tests/fixtures/tokens/variables/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/variables/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "variables" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/fixtures/tokens/where_clause/Forc.toml b/sway-lsp/tests/fixtures/tokens/where_clause/Forc.toml index 5c70eeb4c0f..f7157b110e3 100644 --- a/sway-lsp/tests/fixtures/tokens/where_clause/Forc.toml +++ b/sway-lsp/tests/fixtures/tokens/where_clause/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "where_clause" [dependencies] -std = { path = "../../../../../sway-lib-std" } +std = { path = "../../fixtures-std" } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 7ff63d609f5..eb94a2ae8e2 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -157,7 +157,7 @@ async fn go_to_definition_for_fields() { def_line: 81, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // Option lsp::definition_check(&server, &opt_go_to); @@ -212,7 +212,7 @@ async fn go_to_definition_inside_turbofish() { def_line: 81, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // option.sw lsp::definition_check(&server, &opt_go_to); @@ -232,7 +232,7 @@ async fn go_to_definition_inside_turbofish() { def_line: 61, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/result.sw", + def_path: "fixtures-std/src/result.sw", }; // result.sw lsp::definition_check(&server, &res_go_to); @@ -289,7 +289,7 @@ async fn go_to_definition_for_matches() { def_line: 81, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // Option lsp::definition_check(&server, &go_to); @@ -307,7 +307,7 @@ async fn go_to_definition_for_matches() { def_line: 85, def_start_char: 4, def_end_char: 8, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // Some lsp::definition_check(&server, &go_to); @@ -322,7 +322,7 @@ async fn go_to_definition_for_matches() { def_line: 83, def_start_char: 4, def_end_char: 8, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // None lsp::definition_check(&server, &go_to); @@ -416,7 +416,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/lib.sw", + def_path: "fixtures-std/src/lib.sw", }; // std lsp::definition_check(&server, &go_to); @@ -432,7 +432,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // option lsp::definition_check(&server, &go_to); @@ -444,7 +444,7 @@ async fn go_to_definition_for_paths() { def_line: 81, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // Option lsp::definition_check(&server, &go_to); @@ -457,7 +457,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/vm.sw", + def_path: "fixtures-std/src/vm.sw", }; // vm lsp::definition_check(&server, &go_to); @@ -469,7 +469,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/vm/evm.sw", + def_path: "fixtures-std/src/vm/evm.sw", }; // evm lsp::definition_check(&server, &go_to); @@ -481,7 +481,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/vm/evm/evm_address.sw", + def_path: "fixtures-std/src/vm/evm/evm_address.sw", }; // evm_address lsp::definition_check(&server, &go_to); @@ -493,7 +493,7 @@ async fn go_to_definition_for_paths() { def_line: 7, def_start_char: 11, def_end_char: 21, - def_path: "sway-lib-std/src/vm/evm/evm_address.sw", + def_path: "fixtures-std/src/vm/evm/evm_address.sw", }; // EvmAddress lsp::definition_check(&server, &go_to); @@ -636,7 +636,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/assert.sw", + def_path: "fixtures-std/src/assert.sw", }; // assert lsp::definition_check(&server, &go_to); @@ -648,7 +648,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-core/src/lib.sw", + def_path: "fixtures-core/src/lib.sw", }; // core lsp::definition_check(&server, &go_to); @@ -660,7 +660,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-core/src/primitives.sw", + def_path: "fixtures-core/src/primitives.sw", }; // primitives lsp::definition_check(&server, &go_to); @@ -711,7 +711,7 @@ async fn go_to_definition_for_paths() { def_line: 0, def_start_char: 0, def_end_char: 0, - def_path: "sway-lib-std/src/constants.sw", + def_path: "fixtures-std/src/constants.sw", }; // constants lsp::definition_check(&server, &go_to); @@ -725,7 +725,7 @@ async fn go_to_definition_for_paths() { def_line: 33, def_start_char: 10, def_end_char: 19, - def_path: "sway-lib-std/src/constants.sw", + def_path: "fixtures-std/src/constants.sw", }; // ZERO_B256 lsp::definition_check(&server, &go_to); @@ -738,7 +738,7 @@ async fn go_to_definition_for_paths() { def_line: 17, def_start_char: 11, def_end_char: 14, - def_path: "sway-lib-core/src/primitives.sw", + def_path: "fixtures-core/src/primitives.sw", }; // u64::min() lsp::definition_check(&server, &go_to); @@ -750,7 +750,7 @@ async fn go_to_definition_for_paths() { def_line: 247, def_start_char: 11, def_end_char: 14, - def_path: "sway-lib-core/src/primitives.sw", + def_path: "fixtures-core/src/primitives.sw", }; // b256::min() lsp::definition_check(&server, &go_to); @@ -870,12 +870,12 @@ async fn go_to_definition_for_variables() { go_to.def_line = 61; go_to.def_start_char = 9; go_to.def_end_char = 15; - go_to.def_path = "sway-lib-std/src/result.sw"; + go_to.def_path = "fixtures-std/src/result.sw"; lsp::definition_check_with_req_offset(&server, &mut go_to, 56, 22); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 31); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 60); go_to.def_line = 81; - go_to.def_path = "sway-lib-std/src/option.sw"; + go_to.def_path = "fixtures-std/src/option.sw"; lsp::definition_check_with_req_offset(&server, &mut go_to, 56, 28); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 39); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 68); @@ -909,7 +909,7 @@ async fn go_to_definition_for_consts() { def_line: 8, def_start_char: 11, def_end_char: 21, - def_path: "sway-lib-std/src/contract_id.sw", + def_path: "fixtures-std/src/contract_id.sw", }; lsp::definition_check(&server, &contract_go_to); @@ -961,7 +961,7 @@ async fn go_to_definition_for_consts() { go_to.def_line = 81; go_to.def_start_char = 9; go_to.def_end_char = 15; - go_to.def_path = "sway-lib-std/src/option.sw"; + go_to.def_path = "fixtures-std/src/option.sw"; lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 17); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 24); lsp::definition_check_with_req_offset(&server, &mut go_to, 11, 38); @@ -1052,7 +1052,7 @@ async fn go_to_definition_for_structs() { def_line: 81, def_start_char: 9, def_end_char: 15, - def_path: "sway-lib-std/src/option.sw", + def_path: "fixtures-std/src/option.sw", }; // Type Params lsp::definition_check(&server, &go_to);