diff --git a/API.md b/API.md index e378b10a..2de4df06 100644 --- a/API.md +++ b/API.md @@ -63,6 +63,9 @@ When deploying contracts, you should use the latest released version of Solidity * [\.storeUint()](#tvmbuilderstoreuint) * [Store little-endian integers](#store-little-endian-integers) * [\.storeRef()](#tvmbuilderstoreref) + * [StringBuilder](#stringbuilder) + * [\.append()](#stringbuilderappend) + * [\.toString()](#stringbuildertostring) * [optional(T)](#optionalt) * [constructing an optional](#constructing-an-optional) * [\.hasValue()](#optionalthasvalue) @@ -74,11 +77,20 @@ When deploying contracts, you should use the latest released version of Solidity * [variant](#variant) * [variant.isUint()](#variantisuint) * [variant.toUint()](#varianttouint) - * [vector(Type)](#vectortype) - * [\.push(Type)](#vectortypepushtype) - * [\.pop()](#vectortypepop) - * [\.length()](#vectortypelength) - * [\.empty()](#vectortypeempty) + * [vector(T)](#vectort) + * [\.push()](#vectortpush) + * [\.pop()](#vectortpop) + * [\.last()](#vectortlast) + * [\.operator[]](#vectortoperator) + * [\.length()](#vectortlength) + * [\.empty()](#vectortempty) + * [stack(T)](#stackt) + * [\.push()](#stacktpush) + * [\.pop()](#stacktpop) + * [\.top()](#stackttop) + * [\.empty()](#stacktempty) + * [\.sort()](#stacktsort) + * [\.reverse()](#stacktreverse) * [TVM specific control structures](#tvm-specific-control-structures) * [Range-based for loop](#range-based-for-loop) * [repeat](#repeat) @@ -88,7 +100,16 @@ When deploying contracts, you should use the latest released version of Solidity * [Integers](#integers) * [\.cast()](#integercast) * [bitSize() and uBitSize()](#bitsize-and-ubitsize) - * [varInt and varUint](#varint-and-varuint) + * [Quiet arithmetic](#quiet-arithmetic) + * [qintN and quintN](#qintn-and-quintn) + * [qbool](#qbool) + * [Keyword NaN](#keyword-nan) + * [\.isNaN()](#tisnan) + * [\.get()](#tget) + * [\.getOr()](#tgetor) + * [\.getOrDefault()](#tgetordefault) + * [\.toOptional()](#ttooptional) + * [varint and varuint](#varint-and-varuint) * [struct](#struct) * [struct constructor](#struct-constructor) * [\.unpack()](#structunpack) @@ -165,8 +186,9 @@ When deploying contracts, you should use the latest released version of Solidity * [Libraries](#libraries) * [Function call via library name](#function-call-via-library-name) * [Function call via object](#function-call-via-object) + [Free function call via object](#free-function-call-via-object) * [Pragmas](#pragmas) - * [pragma ever-solidity](#pragma-ever-solidity) + * [pragma tvm-solidity](#pragma-tvm-solidity) * [pragma copyleft](#pragma-copyleft) * [pragma ignoreIntOverflow](#pragma-ignoreintoverflow) * [pragma AbiHeader](#pragma-abiheader) @@ -195,7 +217,6 @@ When deploying contracts, you should use the latest released version of Solidity * [emit](#emit) * [return](#return) * [External function calls](#external-function-calls) - * [Synchronous calls](#synchronous-calls) * [Delete variables](#delete-variables) * [API functions and members](#api-functions-and-members) * [Type information](#type-information) @@ -251,9 +272,9 @@ When deploying contracts, you should use the latest released version of Solidity * [math.abs()](#mathabs) * [math.modpow2()](#mathmodpow2) * [math.divr() math.divc()](#mathdivr-mathdivc) + * [math.divmod()](#mathdivmod) * [math.muldiv() math.muldivr() math.muldivc()](#mathmuldiv-mathmuldivr-mathmuldivc) * [math.muldivmod()](#mathmuldivmod) - * [math.divmod()](#mathdivmod) * [math.sign()](#mathsign) * [**tx** namespace](#tx-namespace) * [tx.logicaltime](#txlogicaltime) @@ -278,7 +299,6 @@ When deploying contracts, you should use the latest released version of Solidity * [abi.codeSalt()](#abicodesalt) * [abi.setCodeSalt()](#abisetcodesalt) * [abi.functionId()](#abifunctionid) - * [abi.encodeExtMsg()](#abiencodeextmsg) * [abi.encodeIntMsg()](#abiencodeintmsg) * [**gosh** namespace](#gosh-namespace) * [gosh.diff and gosh.zipDiff](#goshdiff-and-goshzipdiff) @@ -380,7 +400,7 @@ if (cell == TvmCell()) { // check whether `cell` is empty ##### \.depth() ```TVMSolidity -.depth() returns(uint16); +.depth() returns (uint16); ``` Returns the depth **d** of the `TvmCell` **c**. If **c** has no references, then **d** = 0; @@ -1037,7 +1057,7 @@ See also: [\.loadZeroes(), \.loadOnes() and \.l ##### \.storeInt() ```TVMSolidity -.storeInt(int256 value, uint9 bitSize); +.storeInt(int257 value, uint9 bitSize); ``` Stores a signed integer **value** with given **bitSize** in the `TvmBuilder`. @@ -1073,6 +1093,43 @@ Stores the little-endian integer. Stores `TvmBuilder b`/`TvmCell c`/`TvmSlice s` in the reference of the `TvmBuilder`. +#### StringBuilder + +A mutable sequence of characters. `StringBuilder` allows creating a string from `bytes1` and `string` in a gas-efficient way. Example: + +```TVMSolidity +StringBuilder b; +b.append(bytes1("-")); +b.append(bytes1("0"), 10); +b.append("1234"); +string s = b.toString(); // s == "-00000000001234" +``` + +##### \.append() + +```TVMSolidity +(1) +.append(bytes1); +(2) +.append(bytes1, uint31 n); +(3) +.append(string); +``` + +(1) Appends `bytes1` to the sequence. + +(2) Appends `bytes1` `n` times to the sequence. + +(3) Appends `string` to the sequence. + +##### \.toString() + +```TVMSolidity +.toString(); +``` + +Returns a string representing the data in this sequence. + #### optional(T) The template optional type manages an optional contained value, i.e. a value that may or may not be present. @@ -1157,74 +1214,196 @@ Checks whether `` holds `uint` type. Converts `` to `uint` type if it's possible. Otherwise, throws an exception with code `77`. -#### vector(Type) +#### vector(T) -`vector(Type)` is a template container type capable of storing an arbitrary set of values of a -single type, pretty much like dynamic-sized array. -Two major differences are that `vector(Type)`: +`vector(T)` is a template container type for storing values of a same type, pretty much like [dynamic-sized array](#arrays). -1. is much more efficient than a dynamic-sized array; -2. has a lifespan of a smart-contract execution, so it can't be neither passed nor returned as an -external function call parameter, nor stored in a state variable. +Two major differences are that `vector(T)`: +1. is much more efficient than a [dynamic-sized array](#arrays); +2. has a lifespan of a smart-contract execution, so it can't be neither passed nor returned as an external function call parameter, nor stored in a state variable. -**Note:** `vector` implementation based on `TVM Tuple` type, and it has a limited -length of 255 * 255 = 65025 values. +**Note:** `vector` implementation based on `TVM Tuple` type, and it has a limited length of 255 values. -##### \.push(Type) +Example: ```TVMSolidity -.push(Type obj); +vector(int) arr; + +arr.push(100); // arr == [100] +arr.push(200); // arr == [100, 200] +arr.push(300); // arr == [100, 200, 300] + +uint8 len = arr.length(); // len == 3 + +int value1 = arr[1]; // value1 == 200 + +arr[1] = 222; // arr == [100, 222, 300] + +int last = arr.last(); // last == 300, arr == [100, 222, 300] + +last = arr.pop(); // last == 300, arr == [100, 222] +last = arr.pop(); // last == 222, arr == [100] +last = arr.pop(); // last == 100, arr == [] + +bool isEmpty = arr.empty(); // isEmpty == true ``` -Appends **obj** to the `vector`. +##### \.push() ```TVMSolidity -vector(uint) vect; -uint a = 11; -vect.push(a); -vect.push(111); +.push(T element); ``` -##### \.pop() +Appends **element** to the `vector`. + +##### \.pop() ```TVMSolidity -.pop() returns (Type); +.pop() returns (T); ``` Pops the last value from the `vector` and returns it. +##### \.last() + ```TVMSolidity -vector(uint) vect; -... -uint a = vect.pop(); +.last() returns (T); ``` -##### \.length() +Returns the last value from the `vector`. + +##### \.operator[] ```TVMSolidity -.length() returns (uint8); +.operator[](uint index) returns (T); ``` -Returns length of the `vector`. +Returns the value located at the `index` position. If `index` is not within the range of the container, an exception is thrown. + +##### \.length() ```TVMSolidity -vector(uint) vect; -... -uint8 len = vect.length(); +.length() returns (uint8); ``` -##### \.empty() +Returns length of the `vector`. + +##### \.empty() ```TVMSolidity -.empty() returns (bool); +.empty() returns (bool); ``` Checks whether the `vector` is empty. +##### stack(T) + +`stack` represents a last-in-first-out (LIFO) stack of items. The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, a method to test for whether the stack is empty. + ```TVMSolidity -vector(uint) vect; -... -bool is_empty = vect.empty(); +stack(int) st; +st.push(100); +st.push(200); +bool isEmpty = st.empty(); // isEmpty == false + +int item = st.top(); // item == 200, st == [100, 200] +st.top() += 25; // st == [100, 225] +item = st.top(); // item == 225, st == [100, 225] + +item = st.pop(); // item == 225, st == [100] +item = st.pop(); // item == 100, st == [] + +isEmpty = st.empty(); // isEmpty == true +``` + +##### \.push() + +```TVMSolidity +.push(T item) +``` + +Pushes an item onto the top of this stack. + +##### \.pop() + +```TVMSolidity +.pop() returns (T) +``` + +Removes the item at the top of this stack and returns that item as the value of this function. + +##### \.top() + +```TVMSolidity +.top() returns (ref T) +``` + +Returns reference at the item at the top of this stack without removing it from the stack. Example: + +```TVMSolidity +stack(int) st; +st.push(200); +st.top() += 25; // st == [225] +int item = st.top(); // item = 225, st == [225] +``` + +##### \.empty() + +```TVMSolidity +.empty() returns (bool) +``` + +Checks whether the `stack` is empty. + +##### \.sort() + +```TVMSolidity +.sort(function(Type, Type) internal pure returns(bool) isLess) +``` + +Sorts the specified stack into ascending order. Example: + +```TVMSolidity +struct Point { + int x; + int y; +} + +function less(Point a, Point b) private pure returns(bool) { + return a.x < b.x || a.x == b.x && a.y < b.y; +} + +function testPoints() public pure { + stack(Point) st; + st.push(Point(20, 40)); + st.push(Point(10, 10)); + st.push(Point(20, 30)); + st.sort(less); + Point p; + p = st.pop(); // p == Point(10, 10) + p = st.pop(); // p == Point(20, 30) + p = st.pop(); // p == Point(20, 40) +} +``` + +##### \.reverse() + +```TVMSolidity +.reverse() +``` + +Reverses the order of the elements in the specified stack. Example: + +```TVMSolidity +stack(int) st; +st.push(100); +st.push(200); +st.push(300); +int value = st.top(); // value == 300 +st.reverse(); +value = st.pop(); // value == 100 +value = st.pop(); // value == 200 +value = st.pop(); // value == 300 ``` ### TVM specific control structures @@ -1388,16 +1567,15 @@ See also: [pragma ignoreIntOverflow](#pragma-ignoreintoverflow). #### Integers -``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uintN`` and ``intN`` -where ``N`` is a number from ``1`` to ``256`` in steps of 1 denotes the number of bits. ``uint`` and ``int`` -are aliases for ``uint256`` and ``int256``, respectively. +`int` / `uint`: Signed and unsigned integers of various sizes. +Keywords `uint1` to `uint256` in steps of 1 (unsigned of 1 up to 256 bits) and `int1` to `int257`. `uint` and `int` are aliases for `uint256` and `int257`, respectively. Operators: -* Comparison: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) -* Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) +* Comparison: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`) +* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation) +* Shift operators: `<<` (left shift), `>>` (right shift) +* Arithmetic operators: `+`, `-`, unary `-`, `*`, `/`, `%` (modulo), `**` (exponentiation) ##### \.cast() @@ -1460,23 +1638,179 @@ uint16 s = uBitSize(1); // s == 1 uint16 s = uBitSize(0); // s == 0 ``` -#### varInt and varUint +#### Quiet arithmetic + +Operations with `qintN` / `quintN` return `NaN` instead of throwing integer overflow exceptions if the results do not fit in type, or if one of their arguments is a `NaN`. + +##### qintN and quintN + +`qint` / `quint`: Signed and unsigned integers of various sizes. +Keywords `quint1` to `quint256` in steps of 1 (unsigned of 1 up to 256 bits) and `qint1` to `qint257`. `quint` and `qint` are aliases for `quint256` and `qint257`, respectively. + +Operators: + * Comparison: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to [qbool](#qbool)) + * Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation) + * Shift operators: `<<` (left shift), `>>` (right shift) + * Arithmetic operators: `+`, `-`, unary `-`, `*`, `/`, `%` (modulo), `**` (exponentiation) + +If one operand of bitwise “or” (`|`) is equal to −1, the result +is always −1, even if the other argument is a `NaN`; + +If one operand of bitwise “and” (`&`) is equal to 0, the result +is always 0, even if the other argument is a `NaN`; + +If one operand of `==` (`!=`) is equal to `NaN`, the result +is always `NaN`. + +All [math namespace](#math-namespace) functions support quiet arithmetic. + +##### qbool + +`qbool` can have 3 values: `true`, `false` and `NaN`. + +Operators: + * `!` (logical negation) + * `&&` (logical conjunction, “and”) + * `||` (logical disjunction, “or”) + * `==` (equality) + * `!=` (inequality) + +The operators `||` and `&&` apply the common short-circuiting rules. This means that in the expression `f(x) || g(y)`, if `f(x)` evaluates to true, `g(y)` will not be evaluated even if it may have side-effects. + +If one operand of logical “or” (`|`) is equal to `true` , the result +is always `true`, even if the other argument is a `NaN`; + +If one operand of logical “and” (`&`) is equal to `false`, the result +is always `false`, even if the other argument is a `NaN`; + +If one operand of `==` (`!=`) is equal to `NaN`, the result +is always `NaN`. + +```TVMSolidity +function f(quint32 a, quint32 b, quint32 c, quint32 d) private { + qbool less = a * b < c * d; + if (less.isNaN()) { + // ... + } else { + bool l = less.get(); + // ... + } +} +``` + +##### Keyword NaN + +The `NaN` constant returns a nan (Not a Number) value. This value is not a legal number. Example: +```TVMSolidity +qint32 x = NaN; +qbool y = NaN; +``` + +##### \.isNaN() + +```TVMSolidity +.isNaN() returns (bool) +``` + +Checks whether `` is `NaN`. `T` is `qintN`, `quintN` or `qbool`. Example: + +```TVMSolidity +function checkOverflow(quint32 a, quint32 b) private pure returns(bool) { + quint32 s = a + b; + return s.isNaN(); +} +``` + +##### \.get() + +```TVMSolidity +.get() returns (T2) +``` + +Returns "non-quiet" integer. If `` is `NaN`, then throws an exception. `T` is `qintN`, `quintN` or `qbool`. Example: + +```TVMSolidity +function f(quint32 a, quint32 b) private pure { + quint32 s = a + b; + if (!s.isNaN()) { + uint32 ss = s.get(); + // ... + } +} +``` + +##### \.getOr() + +```TVMSolidity +.getOr(T default) returns (T2) +``` + +Returns "non-quiet" integer. If `` is `NaN`, then returns `default`. `T` is `qintN`, `quintN` or `qbool`. Example: + +```TVMSolidity +function f(quint32 a, quint32 b) private pure { + quint32 s = a + b; + uint32 ss = s.getOr(42); // ss is equal to `a + b` or 42 + // ... +} +``` + +##### \.getOrDefault() -`varInt`/`varInt16`/`varInt32`/`varUint`/`varUint16`/`coins`/`varUint32` are kinds of [Integer](#integers) +```TVMSolidity +.getOrDefault() returns (T2) +``` + +Returns "non-quiet" integer. If `` is `NaN`, then returns default value. `T` is `qintN`, `quintN` or `qbool`. Example: + +```TVMSolidity +function f(quint32 a, quint32 b) private pure { + quint32 s = a + b; + uint32 ss = s.getOrDefault(); // ss is equal to `a + b` or 0 + // ... +} +``` + +##### \.toOptional() + +```TVMSolidity +.toOptional() returns (toOptional(T2)) +``` + +Returns optional integer. If `` is `NaN`, then returns [null](#keyword-null). `T` is `qintN`, `quintN` or `qbool`. Example: + +```TVMSolidity +function f(quint32 a, quint32 b) private pure { + quint32 s = a + b; + optional(uint32) ss = s.toOptional(); // ss is equal to `a + b` or null + // ... +} +``` + +#### varint and varuint + +`varint`/`varint16`/`varint32`/`varuint`/`varuint16`/`coins`/`varuint32` are kinds of [Integer](#integers) types. But they are serialized/deserialized according to [their TLB schemes](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L112). These schemes are effective if you want to store or send integers, and they usually have small size. Use these types only if you are sure. -`varInt` is equal to `varInt32`, `varInt32` - `int248` , `varInt16` - `int120`. -`varUint` is equal to `varUint32`, `varUint32` - `uint248` , `varUint16` - `uint120`. -`coins` is an alias for `varUint16`. +`varint` is equal to `varint32`, `varint32` - `int248` , `varint16` - `int120`. +`varuint` is equal to `varuint32`, `varuint32` - `uint248` , `varuint16` - `uint120`. +`coins` is an alias for `varuint16`. Example: ```TVMSolidity -mapping(uint => varInt) m_map; // use `varInt` as mapping value only if values have small size +mapping(uint => varint) m_map; // use `varint` as mapping value only if values have small size m_map[10] = 15; ``` +Operators: + +* Comparison: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`) +* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation) +* Shift operators: `<<` (left shift), `>>` (right shift) +* Arithmetic operators: `+`, `-`, unary `-`, `*`, `/`, `%` (modulo), `**` (exponentiation) + #### struct Structs are custom defined types that can group several variables. @@ -1957,7 +2291,7 @@ Returns the `address` value of **addr_std** or **addr_var** if **addr_var** has ##### \.balance ```TVMSolidity -address(this).balance returns (varUint16); +address(this).balance returns (varuint16); ``` Returns balance of the current contract account in nanoevers. @@ -1965,7 +2299,7 @@ Returns balance of the current contract account in nanoevers. ##### \.currencies ```TVMSolidity -address(this).currencies returns (mapping(uint32 => varUint32)); +address(this).currencies returns (mapping(uint32 => varuint32)); ``` Returns currencies on the balance of the current contract account. @@ -2038,14 +2372,14 @@ Example: ##### \.transfer() ```TVMSolidity -
.transfer(varUint16 value, bool bounce, uint16 flag, TvmCell body, mapping(uint32 => varUint32) currencies, TvmCell stateInit); +
.transfer(varuint16 value, bool bounce, uint16 flag, TvmCell body, mapping(uint32 => varuint32) currencies, TvmCell stateInit); ``` Sends an internal outbound message to the `address`. Function parameters: -* `value` (`varUint16`) - amount of nanoevers sent attached to the message. Note: the sent value is +* `value` (`varuint16`) - amount of nanoevers sent attached to the message. Note: the sent value is withdrawn from the contract's balance even if the contract has been called by internal inbound message. -* `currencies` (`mapping(uint32 => varUint32)`) - additional currencies attached to the message. Defaults to +* `currencies` (`mapping(uint32 => varuint32)`) - additional currencies attached to the message. Defaults to an empty set. * `bounce` (`bool`) - if it's set and transaction (generated by the internal outbound message) falls (only at the computing phase, not at the action phase!), then funds will be returned. Otherwise, (flag isn't @@ -2059,7 +2393,7 @@ format, a cell underflow [exception](#tvm-exception-codes) at the computing phas See [here](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L148). Normally, `stateInit` is used in 2 cases: to deploy the contract or to unfreeze the contract. -All parameters can be omitted, except ``value``. +All parameters can be omitted, except `value`. Possible values of parameter `flag`: @@ -2087,7 +2421,7 @@ uint128 value = ...; bool bounce = ...; uint16 flag = ...; TvmCell body = ...; -mapping(uint32 => varUint32) c = ...; +mapping(uint32 => varuint32) c = ...; TvmCell stateInit = ...; // sequential order of parameters addr.transfer(value); @@ -2477,7 +2811,7 @@ Example of using library in the manner `LibName.func(a, b, c)`: ```TVMSolidity // file MathHelper.sol -pragma solidity >= 0.6.0; +pragma solidity >= 0.72.0; // Library declaration library MathHelper { @@ -2494,7 +2828,7 @@ library MathHelper { // file MyContract.sol -pragma solidity >= 0.6.0; +pragma solidity >= 0.72.0; import "MathHelper.sol"; @@ -2516,7 +2850,7 @@ See ([TVM][1] - A.2.3.2). **But if a library function is called like `obj.func(b, c)`, then the first argument `obj` is passed by reference.** It's similar to the `self` variable in Python. -The directive `using A for B;` can be used to attach library functions +The directive [using A for B;](https://docs.soliditylang.org/en/latest/contracts.html#using-for) can be used to attach library functions (from the library `A`) to any type (`B`) in the context of the contract. These functions will receive the object they were called for as their first parameter. @@ -2527,7 +2861,7 @@ Example of using library in the manner `obj.func(b, c)`: ```TVMSolidity // file ArrayHelper.sol -pragma solidity >= 0.6.0; +pragma solidity >= 0.72.0; library ArrayHelper { // Delete value from the `array` at `index` position @@ -2541,7 +2875,7 @@ library ArrayHelper { // file MyContract.sol -pragma solidity >= 0.6.0; +pragma solidity >= 0.72.0; import "ArrayHelper.sol"; @@ -2551,7 +2885,7 @@ contract MyContract { uint[] array; - constructor() public { + constructor() { array = [uint(100), 200, 300]; } @@ -2564,27 +2898,37 @@ contract MyContract { } ``` -### Import +#### Free function call via object -TVM Solidity compiler allows user to import remote files using link starting with `http`. -If import file name starts with `http`, then compiler tries to download the file using this -link and saves it to the folder `.solc_imports`. If compiler fails to create this folder or -to download the file, then an error is emitted. +Free functions can be called via object as well as library functions. Use directive [using A for B;](https://docs.soliditylang.org/en/latest/contracts.html#using-for). Example: -**Note**: to import file from GitHub, one should use link to the raw version of the file. +```TVMSolidity +pragma tvm-solidity >= 0.72.0; -Example: +// Delete value from the `array` at `index` position +function del(uint[] array, uint index) { + for (uint i = index; i + 1 < array.length; ++i){ + array[i] = array[i + 1]; + } + array.pop(); +} -```TVMSolidity -pragma ever-solidity >= 0.35.0; -pragma AbiHeader expire; -pragma AbiHeader pubkey; +contract MyContract { + // Attach function `del` to the type `uint[]` + using {del} for uint[]; + + uint[] public array; -import "https://github.com/tonlabs/debots/raw/9c6ca72b648fa51962224ec0d7ce91df2a0068c1/Debot.sol"; -import "https://github.com/tonlabs/debots/raw/9c6ca72b648fa51962224ec0d7ce91df2a0068c1/Terminal.sol"; + constructor() { + array = [uint(100), 200, 300]; + } -contract HelloDebot is Debot { - ... + function deleteElement(uint index) public { + // Free function call via object. + // Note: free function `del` has 2 arguments: + // array is passed by reference and index is passed by value + array.del(index); + } } ``` @@ -2596,13 +2940,13 @@ the pragma to all your files if you want to enable it in your whole project. If you import another file, the pragma from that file is not automatically applied to the importing file. -#### pragma ever-solidity +#### pragma tvm-solidity ```TVMSolidity -pragma ever-solidity >= 0.35.5; // Check if the compiler version is greater or equal than 0.35.5 -pragma ever-solidity ^ 0.35.5; // Check if the compiler version is greater or equal than 0.35.5 and less than 0.36.0 -pragma ever-solidity < 0.35.5; // Check if the compiler version is less than 0.35.5 -pragma ever-solidity >= 0.35.5 < 0.35.7; // Check if the compiler version is equal to either 0.35.5 or 0.35.6 +pragma tvm-solidity >= 0.35.5; // Check if the compiler version is greater or equal than 0.35.5 +pragma tvm-solidity ^ 0.35.5; // Check if the compiler version is greater or equal than 0.35.5 and less than 0.36.0 +pragma tvm-solidity < 0.35.5; // Check if the compiler version is less than 0.35.5 +pragma tvm-solidity >= 0.35.5 < 0.35.7; // Check if the compiler version is equal to either 0.35.5 or 0.35.6 ``` Used to restrict source file compilation to the particular compiler versions. @@ -3060,7 +3404,7 @@ function sum(uint a, uint b) private inline returns (uint) { #### Assembly -To make inline assembler you should mark free function as `assembly`. Function body must contain lines of assembler code separated by commas. +To make inline assembler you should mark [free function](https://docs.soliditylang.org/en/latest/contracts.html#functions) as `assembly`. Function body must contain lines of assembler code separated by commas. It is up to user to set correct mutability (`pure`, `view` or default), return parameters of the function and so on. @@ -3223,7 +3567,7 @@ contract Caller { IContract(addr).f{value: 10 ever, flag: 3}(123); IContract(addr).f{value: 10 ever, bounce: true}(123); IContract(addr).f{value: 1 micro, bounce: false, flag: 128}(123); - mapping(uint32 => varUint32) cc; + mapping(uint32 => varuint32) cc; cc[12] = 1000; IContract(addr).f{value: 10 ever, currencies:cc}(123); } @@ -3277,42 +3621,6 @@ See also: and [4.1_CurrencyExchange.sol](https://github.com/tonlabs/samples/blob/master/solidity/4.1_CurrencyExchange.sol) * [return](#return) -#### Synchronous calls - -TVM Solidity compiler allows user to perform synchronous calls. To do it user should call a remote contract -function with `.await` suffix. Example: - -```TVMSolidity -interface IContract { - function getNum(uint a) external responsible returns (uint) ; -} - -contract Caller { - function call(address addr) public pure { - ... - uint res = IContract(addr).getNum(123).await; - require(res == 124, 101); - ... - } -} -``` - -When function `call` is called this code: - -1) Executes code before the `.await` call; -2) Generates and sends an internal message to **IContract** contract with address **addr**; -3) Saves current state of the contract including continuation, that is currently executed; -4) Waits for the answer from the remote contract; -5) If an internal message is received from the required address, contract unpacks the state and continues function execution. - -Such await calls can be used everywhere in the code (e.g. inside a cycle), but be aware that usage of such -construction means that your contract function could possibly execute in more than one transaction. -Should be mentioned, that execution after the await call will spend money, that were attached to the responce -message. It means, that for correct work the receiver contract should make an accept after the await call, -or be sure that remote contract attaches enough currency for further execution. - -**Note**: This feature was designed to be used in debots, in usual contracts use it at your own risk. - ### Delete variables As in classic Solidity `delete` operation assigns the initial value for the type to a variable. @@ -3403,7 +3711,7 @@ Returns: ##### msg.value ```TVMSolidity -msg.value (varUint16) +msg.value (varuint16) ``` Returns: @@ -3415,7 +3723,7 @@ Returns: ##### msg.currencies ```TVMSolidity -msg.currencies (mapping(uint32 => varUint32)) +msg.currencies (mapping(uint32 => varuint32)) ``` Collections of arbitrary currencies contained in the balance of @@ -3453,7 +3761,7 @@ Returns [the whole message](https://github.com/ton-blockchain/ton/blob/master/cr ##### msg.forwardFee ```TVMSolidity -msg.forwardFee (varUint16) +msg.forwardFee (varuint16) ``` Returns: @@ -3463,7 +3771,7 @@ Returns: ##### msg.importFee ```TVMSolidity -msg.importFee (varUint16) +msg.importFee (varuint16) ``` Returns: @@ -3703,7 +4011,7 @@ integer index **paramNumber** as a `TvmCell` and a boolean status. ```TVMSolidity tvm.rawReserve(uint value, uint8 flag); -tvm.rawReserve(uint value, mapping(uint32 => varUint32) currency, uint8 flag); +tvm.rawReserve(uint value, mapping(uint32 => varuint32) currency, uint8 flag); ``` Creates an output action that reserves **reserve** nanoevers. It is roughly equivalent to @@ -3908,9 +4216,9 @@ address newWallet = new SimpleWallet{ The following options can be used with both `stateInit` and `code`: -* `value` (`varUint16`) - funds attached to the outbound internal message, that creates new account. +* `value` (`varuint16`) - funds attached to the outbound internal message, that creates new account. This value must be set. -* `currencies` (`mapping(uint32 => varUint32)`) - currencies attached to the outbound internal message. +* `currencies` (`mapping(uint32 => varuint32)`) - currencies attached to the outbound internal message. Defaults to an empty set. * `bounce` (`bool`) - if it's set and deploy falls (only at the computing phase, not at the action phase!), then funds will be returned. Otherwise, (flag isn't set or deploying terminated successfully) @@ -4068,7 +4376,7 @@ It will happen because the transaction will fail at the action phase. #### **math** namespace -`T` is an integer, [variable integer](#varint-and-varuint) or fixed point type in the `math.*` functions where applicable. +`T` is an integer, [variable integer](#varint-and-varuint), [qintN and quintN](#qintn-and-quintn) or fixed point type in the `math.*` functions where applicable. Fixed point type is not applicable for `math.modpow2()`, `math.muldiv[r|c]()`, `math.muldivmod()` and `math.divmod()`. ##### math.min() math.max() @@ -4123,10 +4431,11 @@ Computes the `value mod 2^power`. Note: `power` should be a constant integer. Example: ```TVMSolidity +uint a = math.modpow2(21, 4); // a == 5 because 21 % (2**4) == 21 % 16 == 5 + uint constant pow = 10; uint val = 1026; -uint a = math.modpow2(21, 4); // a == 5 -uint b = math.modpow2(val, pow); // b == 2 +uint b = math.modpow2(val, pow); // b == 2 because 1026 % (2**10) == 1026 % 1024 == 2 ``` ##### math.divr() math.divc() @@ -4149,6 +4458,27 @@ fixed32x2 a = 0.25; fixed32x2 res = math.divc(a, 2); // res == 0.13 ``` +##### math.divmod() + +```TVMSolidity +math.divmod(T a, T b) returns (T /*result*/, T /*remainder*/); +``` + +This function divides the first number by the second and returns the result and the +remainder. Result is rounded to the floor. + +Example: + +```TVMSolidity +uint a = 11; +uint b = 3; +(uint d, uint r) = math.divmod(a, b); // (d, r) == (3, 2) + +int e = -11; +int f = 3; +(int h, int p) = math.divmod(e, f); // (h, p) == (-3, 2) +``` + ##### math.muldiv() math.muldivr() math.muldivc() ```TVMSolidity @@ -4191,27 +4521,6 @@ int g = 2; (int h, int p) = math.muldivmod(e, f, g); // (h, p) == (-2, 1) ``` -##### math.divmod() - -```TVMSolidity -math.divmod(T a, T b) returns (T /*result*/, T /*remainder*/); -``` - -This function divides the first number by the second and returns the result and the -remainder. Result is rounded to the floor. - -Example: - -```TVMSolidity -uint a = 11; -uint b = 3; -(uint d, uint r) = math.divmod(a, b); // (d, r) == (3, 2) - -int e = -11; -int f = 3; -(int h, int p) = math.divmod(e, f); // (h, p) == (-3, 2) -``` - ##### math.sign() ```TVMSolidity @@ -4244,7 +4553,7 @@ Returns the logical time of the current transaction. ##### tx.storageFee ```TVMSolidity -tx.storageFee (varUint16); +tx.storageFee (varuint16); ``` Returns the storage fee paid in the current transaction. [Capabilities](#tvm-capabilities) required: `CapStorageFeeToTvm`. @@ -4280,22 +4589,26 @@ parameters) each time before using the pseudorandom number generator. ##### rnd.next ```TVMSolidity -rnd.next([Type limit]) returns (Type); +(1) +rnd.next() returns (uint); +(2) +rnd.next(T limit) returns (T); ``` Generates a new pseudo-random number. -1) Returns `uint256` number. -2) If the first argument `limit > 0`, then function returns the value in the +(1) Returns `uint256` number. + +(2) If the first argument `limit > 0`, then function returns the value in the range `0..limit-1`. Else if `limit < 0`, then the returned value lies in range `limit..-1`. Else if `limit == 0`, then it returns `0`. Example: ```TVMSolidity -// 1) +// (1) uint256 r0 = rnd.next(); // 0..2^256-1 -// 2) +// (2) uint8 r1 = rnd.next(100); // 0..99 int8 r2 = rnd.next(int8(100)); // 0..99 int8 r3 = rnd.next(int8(-100)); // -100..-1 @@ -4416,7 +4729,7 @@ Same as [abi.encodeData()](#abiencodedata) but generate data in the format that File with old contracts that was compiled by compiler < 0.72.0: ```TVMSolidity -pragma ever-solidity >= 0.72.0; // set new version +pragma tvm-solidity >= 0.72.0; // set new version contract SimpleContractA { uint static m_x0; @@ -4433,7 +4746,7 @@ contract SimpleContractB is SimpleContractA { File with new contracts: ```TVMSolidity -pragma ever-solidity >= 0.72.0; +pragma tvm-solidity >= 0.72.0; contract ContractCreator { function deploy(uint pubkey, TvmCell code) public pure returns (address) { TvmCell data = abi.encodeOldDataInit({ @@ -4588,7 +4901,7 @@ uint16 dataDepth = data.depth(); uint256 hash = abi.stateInitHash(codeHash, dataHash, codeDepth, dataDepth); ``` -See also [internal doc](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/docs/internal/stateInit_hash.md) to read more about this +See also [internal doc](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/docs/internal/stateInit_hash.md) to read more about this function mechanics. ##### abi.encodeBody() @@ -4707,132 +5020,6 @@ See example of how to use this function: * [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) -##### abi.encodeExtMsg() - -```TVMSolidity -abi.encodeExtMsg({ - dest: address, - time: uint64, - expire: uint32, - call: {functionIdentifier [, list of function arguments]}, - sign: bool, - pubkey: optional(uint256), - callbackId: (uint32 | functionIdentifier), - onErrorId: (uint32 | functionIdentifier), - stateInit: TvmCell, - signBoxHandle: optional(uint32), - abiVer: uint8, - flags: uint8 -}) -returns (TvmCell); -``` - -Function should be used only offchain and intended to be used only in debot contracts. -Allows creating an external inbound message, that calls the **func** function of the -contract on address **destination** with specified function arguments. - -Mandatory parameters that are used to form a src field that is used for debots: - -* `callbackId` - identifier of the callback function. -* `onErrorId` - identifier of the function that is called in case of an error. -* `signBoxHandle` - handle of the sign box entity, that engine will use to sign the message. - -These parameters are stored in addr_extern and placed to the src field of the message. -Message is of type [ext_in_msg_info](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L127) -and src address is of type [addr_extern](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L101) -but stores special data: - -* callback id - 32 bits; -* on error id - 32 bits; -* abi version - 8 bits; Can be specified manually and contain full abi version in little endian half bytes (e.g. version = "2.3" -> abiVer: 0x32) -* header mask - 3 bits in such order: time, expire, pubkey; -* optional value signBoxHandle - 1 bit (whether value exists) + \[32 bits\]; -* control flags byte - 8 bits. - Currently used bits: - 1 - override time (dengine will replace time value with current time) - 2 - override exp (dengine will replace time value with actual expire value) - 4 - async call (dengine must send message and don't wait for the result) - -Other function parameters define fields of the message: - -* `time` - message creation timestamp. Used for replay attack protection, encoded as 64 bit Unix -time in milliseconds. -* `expire` - Unix time (in seconds, 32 bit) after that message should not be processed by contract. -* `pubkey` - public key from key pair used for signing the message body. This parameter is optional -and can be omitted. -* `sign` - constant bool flag that shows whether message should contain signature. If set to -**true**, message is generated with signature field filled with zeroes. This parameter is optional -and can be omitted (in this case is equal to **false**). - -User can also attach stateInit to the message using `stateInit` parameter. - -Function throws an exception with code 64 if function is called with wrong parameters (pubkey is set -and has value, but sign is false or omitted). - -Example: - -```TVMSolidity - -interface Foo { - function bar(uint a, uint b) external pure; -} - -contract Test { - - TvmCell public m_cell; - - function generate0() public { - tvm.accept(); - address addr = address.makeAddrStd(0, 0x0123456789012345678901234567890123456789012345678901234567890123); - m_cell = abi.encodeExtMsg({callbackId: 0, onErrorId: 0, dest: addr, time: 0x123, expire: 0x12345, call: {Foo.bar, 111, 88}}); - } - - function generate1() public { - tvm.accept(); - optional(uint) pubkey; - address addr = address.makeAddrStd(0, 0x0123456789012345678901234567890123456789012345678901234567890123); - m_cell = abi.encodeExtMsg({callbackId: 0, onErrorId: 0, dest: addr, time: 0x123, expire: 0x12345, call: {Foo.bar, 111, 88}, pubkey: pubkey}); - } - - function generate2() public { - tvm.accept(); - optional(uint) pubkey; - pubkey.set(0x95c06aa743d1f9000dd64b75498f106af4b7e7444234d7de67ea26988f6181df); - address addr = address.makeAddrStd(0, 0x0123456789012345678901234567890123456789012345678901234567890123); - optional(uint32) signBox; - signBox.set(0x12333112); - m_cell = abi.encodeExtMsg({callbackId: 0, onErrorId: 0, dest: addr, time: 0x1771c58ef9a, expire: 0x600741e4, call: {Foo.bar, 111, 88}, pubkey: pubkey, sign: true, signBoxHandle: signBox}); - } - -} -``` - -External inbound message can also be built and sent with construction similar to remote contract -call. It requires suffix ".extMsg" and call options similar to `buildExtMsg` function call. -**Note**: this type of call should be used only offchain in debot contracts. - -```TVMSolidity -interface Foo { - function bar(uint a, uint b) external pure; -} - -contract Test { - - function test7() public { - address addr = address.makeAddrStd(0, 0x0123456789012345678901234567890123456789012345678901234567890123); - Foo(addr).bar{expire: 0x12345, time: 0x123}(123, 45).extMsg; - optional(uint) pubkey; - optional(uint32) signBox; - Foo(addr).bar{expire: 0x12345, time: 0x123, pubkey: pubkey}(123, 45).extMsg; - Foo(addr).bar{expire: 0x12345, time: 0x123, pubkey: pubkey, sign: true}(123, 45).extMsg; - pubkey.set(0x95c06aa743d1f9000dd64b75498f106af4b7e7444234d7de67ea26988f6181df); - Foo(addr).bar{expire: 0x12345, time: 0x123, pubkey: pubkey, sign: true}(123, 45).extMsg; - Foo(addr).bar{expire: 0x12345, time: 0x123, sign: true, signBoxHandle: signBox}(123, 45).extMsg; - Foo(addr).bar{expire: 0x12345, time: 0x123, sign: true, signBoxHandle: signBox, abiVer: 0x32, flags: 0x07}(123, 45).extMsg; - } -} -``` - ##### abi.encodeIntMsg() ```TVMSolidity @@ -4841,7 +5028,7 @@ abi.encodeIntMsg({ value: uint128, call: {function, [callbackFunction,] arg0, arg1, arg2, ...}, bounce: bool, - currencies: mapping(uint32 => varUint32), + currencies: mapping(uint32 => varuint32), stateInit: TvmCell }) returns (TvmCell); @@ -5050,20 +5237,17 @@ Solidity runtime error codes: * **61** - Deploying `StateInit` has no public key in `data` field. * **62** - Reserved for internal usage. * **63** - See [\.get()](#optionaltget). - * **64** - `abi.encodeExtMsg()` call with wrong parameters. See [abi.encodeExtMsg()](#abiencodeextmsg). * **67** - See [gasToValue](#gastovalue) and [valueToGas](#valuetogas). * **68** - There is no config parameter 20 or 21. * **69** - Zero to the power of zero calculation (`0**0` in TVM Solidity style or `0^0`). * **70** - `string` method `substr` was called with substr longer than the whole string. * **71** - Function marked by `externalMsg` was called by internal message. * **72** - Function marked by `internalMsg` was called by external message. - * **74** - Await answer message has wrong source address. - * **75** - Await answer message has wrong function id. * **76** - Public function was called before constructor. * **77** - It's impossible to convert `variant` type to target type. See [variant.toUint()](#varianttouint). * **78** - There's no private function with the function id. * **79** - You are deploying contract that uses [pragma upgrade func/oldsol](#pragma-upgrade-funcoldsol). Use the -contract only for updating another contracts. + * **80** - See [\.get()](#tget). ### Division and rounding diff --git a/Cargo.lock b/Cargo.lock index 3eabf46f..bca6f52d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", @@ -149,15 +149,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "regex-automata", @@ -232,9 +232,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -244,12 +244,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cfg-if" @@ -259,9 +256,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -282,9 +279,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -292,9 +289,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -304,14 +301,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", ] [[package]] @@ -358,9 +355,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -428,14 +425,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", ] [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -550,9 +547,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "float-cmp" @@ -586,9 +583,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -609,9 +606,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -624,9 +621,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -665,15 +662,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -700,15 +697,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "miniz_oxide" @@ -727,9 +724,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", "num-complex", @@ -817,7 +814,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.6", + "hermit-abi 0.3.9", "libc", ] @@ -838,9 +835,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "owned-alloc" @@ -860,9 +857,9 @@ dependencies = [ [[package]] name = "platforms" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "ppv-lite86" @@ -902,18 +899,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -977,7 +974,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.14", ] [[package]] @@ -991,9 +988,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -1003,9 +1000,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1014,9 +1011,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-demangle" @@ -1047,29 +1044,29 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -1117,13 +1114,13 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "sold" -version = "0.73.0" +version = "0.74.0" dependencies = [ "assert_cmd", "atty", @@ -1163,9 +1160,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -1186,9 +1183,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -1224,8 +1221,8 @@ dependencies = [ [[package]] name = "ton_abi" -version = "2.4.14" -source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.4.14#df0aacbd2059f5bf16c7b29000b32a957a8440e2" +version = "2.4.25" +source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.4.25#860adc6d485c18782fb7e1c12aa4e4a660e9b095" dependencies = [ "base64 0.10.1", "byteorder", @@ -1244,8 +1241,8 @@ dependencies = [ [[package]] name = "ton_block" -version = "1.9.122" -source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.122#4330c36880e3bef543e18f4f6af1bb323da97b2a" +version = "1.9.141" +source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.141#c4b2b6a1b8469a52da1276231d93f9f6c11f77bc" dependencies = [ "failure", "hex 0.4.3", @@ -1258,8 +1255,8 @@ dependencies = [ [[package]] name = "ton_labs_assembler" -version = "1.4.39" -source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.4.39#fc2ac6d6f8ed1ceda60373de2188e844a88f0ea3" +version = "1.4.52" +source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.4.52#5c9e46a58d2d0589ca95ab54ab85380ded112173" dependencies = [ "clap", "failure", @@ -1275,8 +1272,8 @@ dependencies = [ [[package]] name = "ton_types" -version = "2.0.32" -source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.32#adc33ce8001ba52de4de9c2168e1f8e6a83ef5e0" +version = "2.0.39" +source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.39#8032c3f19c0e9eaea150340af6ebaf1a33d822ae" dependencies = [ "aes-ctr", "base64 0.13.1", @@ -1303,8 +1300,8 @@ dependencies = [ [[package]] name = "ton_vm" -version = "1.9.6" -source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.9.6#fd7864b934e89c8c3578efcd5575e6cf116a9b03" +version = "1.9.22" +source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.9.22#ca670283e11119f9da253660e056e41b825eedc3" dependencies = [ "ed25519 1.5.3", "ed25519-dalek 1.0.1", @@ -1392,9 +1389,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1402,24 +1399,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1427,22 +1424,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "winapi" @@ -1486,13 +1483,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -1501,45 +1499,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "x25519-dalek" @@ -1570,5 +1574,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.58", ] diff --git a/Changelog.md b/Changelog.md index 97c557a1..edcb113f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,31 @@ +### 0.74.0 (2024-04-11) + +Breaking changes: + * Now `int` is alias for `int257`, not for `int256`. That's why `*loadInt*` functions return `int257` and `*storeInt*` functions take `int257`. + * Deleted debots supporting. + * Now [vector](API.md#vectort) can contain at least 255 elements. + +Bugfixes: + * Fixed minor bugs in TypeChecker. + * Fixed compilation fail when you have private and public functions with the same name in the contract. + * Fixed another minor bugs. + +Compiler features: + * Supported [quiet arithmetic](./API.md#quiet-arithmetic). + * Supported [StringBuilder](./API.md#stringbuilder). + * Supported [int257](API.md#integers). + * Supported [\.last()](API.md#vectortlast). + * Supported [stack(T)](API.md#stackt). + * Supported unary operators (`++`, `--`, `-`, `delete` and `~`) for [varint and varuint](API.md#varint-and-varuint). + * Supported [Free function call via object](API.md#free-function-call-via-object). + +Other changes: + * Renamed some types. Old types are available and marked as deprecated. Renaming: + * `varInt` -> `varint` + * `varUint` -> `varuint` + * `varIntM` -> `varintM` + * `varUintM` -> `varuintM` + ### 0.73.0 (2024-02-12) Update compiler frontend (from original version 0.8.17 to 0.8.24). Full changelog can be found [here](./compiler/Changelog.md). @@ -53,7 +81,7 @@ Other changes: ### 0.72.0 (2023-10-31) -Use [sold](https://github.com/tonlabs/TON-Solidity-Compiler/tree/master/sold) to compile contracts. If you used `solc`+`tvm_linker`, then use `solc`+[asm](https://github.com/tonlabs/ever-assembler). Generated `*.code` files have some another format. +Use [sold](https://github.com/tonlabs/TVM-Solidity-Compiler/tree/master/sold) to compile contracts. If you used `solc`+`tvm_linker`, then use `solc`+[asm](https://github.com/tonlabs/ever-assembler). Generated `*.code` files have some another format. Breaking changes: * The conversion for integer type is only allowed when there is at most one change in sign, width or type-category (`int`, `address`, `bytesNN`, etc.). To perform multiple changes, use multiple conversions. See [Solidity v0.8.0 Breaking Changes](https://docs.soliditylang.org/en/v0.8.17/080-breaking-changes.html#new-restrictions). For example, to convert `int8 x;` to `uint` you can use at least two ways: 1) `uint(uint8(x))`, 2) `uint(int(x))`. @@ -240,7 +268,7 @@ Compiler features: ### 0.64.0 (2022-08-18) -Fixed build [sold](https://github.com/tonlabs/TON-Solidity-Compiler/tree/master/sold) for Windows and macOS. +Fixed build [sold](https://github.com/tonlabs/TVM-Solidity-Compiler/tree/master/sold) for Windows and macOS. Compiler features: * Supported [ABI v2.3](https://github.com/tonlabs/ton-labs-abi/blob/master/docs/ABI_2.3_spec.md). diff --git a/README.md b/README.md index 017496ac..644b0c6a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # The TVM Solidity compiler -[![GitHub](https://img.shields.io/github/license/tonlabs/TON-Solidity-Compiler?style=for-the-badge)](./LICENSE) +[![GitHub](https://img.shields.io/github/license/tonlabs/TVM-Solidity-Compiler?style=for-the-badge)](./LICENSE) [![Everscale](https://custom-icon-badges.demolab.com/badge/-everscale-13173e?style=for-the-badge&logoColor=yellow&logo=everscale)](https://everscale.network/) @@ -12,13 +12,13 @@ Port of the Solidity smart-contract [compiler](https://github.com/ethereum/solid ## TVM Solidity API reference -[API documentation is here](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/API.md) +[API documentation is here](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/API.md) ## Build and Install ### Sold driver -We recommend using `sold` to compile smart-contracts. Documentation is available at [README.md](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/sold/README.md). +We recommend using `sold` to compile smart-contracts. Documentation is available at [README.md](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/sold/README.md). ### Building compiler @@ -27,8 +27,8 @@ Original Instructions about how to build and install the Solidity compiler can b #### Ubuntu Linux ```shell -git clone https://github.com/tonlabs/TON-Solidity-Compiler -cd TON-Solidity-Compiler +git clone https://github.com/tonlabs/TVM-Solidity-Compiler +cd TVM-Solidity-Compiler sh ./compiler/scripts/install_deps.sh mkdir build cd build @@ -42,8 +42,8 @@ Install Visual Studio Build Tools 2019, Git bash, cmake. Run Developer PowerShell for VS 2019 ```shell -git clone https://github.com/tonlabs/TON-Solidity-Compiler -cd TON-Solidity-Compiler +git clone https://github.com/tonlabs/TVM-Solidity-Compiler +cd TVM-Solidity-Compiler compiler\scripts\install_deps.ps1 mkdir build cd build @@ -57,7 +57,7 @@ cmake --build . --config Release -- /m * [Code samples](https://github.com/tonlabs/samples/tree/master/solidity) in TVM Solidity * [tonos-cli](https://github.com/tonlabs/tonos-cli) command line interface for TVM compatible blockchains * Example of usage `tonos-cli` for working (deploying, calling etc.) with TVM compatible blockchains can be found there: [Write smart contract in Solidity](https://docs.ton.dev/86757ecb2/p/950f8a-write-smart-contract-in-solidity) - * [Changelog](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/Changelog_TON.md) + * [Changelog](https://github.com/tonlabs/TVM-Solidity-Compiler/blob/master/Changelog_TON.md) ## License [GNU GENERAL PUBLIC LICENSE Version 3](./LICENSE) diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index d042cb0e..3bd64b9e 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.73.0") +set(PROJECT_VERSION "0.74.0") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/compiler/liblangutil/Token.cpp b/compiler/liblangutil/Token.cpp index 60fcfbeb..7ab33f59 100644 --- a/compiler/liblangutil/Token.cpp +++ b/compiler/liblangutil/Token.cpp @@ -84,6 +84,19 @@ void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _fi "No elementary type " + std::string(TokenTraits::toString(_baseType)) + std::to_string(_first) + "." ); } + else if (_baseType == Token::QUIntM || _baseType == Token::QIntM) + { + unsigned bitLength = _baseType == Token::QUIntM ? 256 : 257; + solAssert(_second == 0, "There should not be a second size argument to type " + std::string(TokenTraits::toString(_baseType)) + "."); + solAssert( + _first <= bitLength, + "No elementary type " + std::string(TokenTraits::toString(_baseType)) + std::to_string(_first) + "." + ); + } + else if (_baseType == Token::QBool) + { + // all right + } else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) { solAssert( @@ -91,7 +104,8 @@ void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _fi "No elementary type " + std::string(TokenTraits::toString(_baseType)) + std::to_string(_first) + "x" + std::to_string(_second) + "." ); } - else if (_baseType == Token::VarUintM || _baseType == Token::VarIntM) + else if (_baseType == Token::VarUintM || _baseType == Token::VarIntM || + _baseType == Token::VaruintM || _baseType == Token::VarintM) { solAssert(_first == 16 || _first == 32, ""); } @@ -193,12 +207,18 @@ std::tuple fromIdentifierOrKeyword(std::strin int m = parseSize(positionM, positionX); Token keyword = keywordByName(baseType); - - if (keyword == Token::VarUint || keyword == Token::VarInt) { + if (keyword == Token::VarUint || keyword == Token::VarInt || + keyword == Token::Varuint || keyword == Token::Varint) { if (m == 16 || m == 32) { if (keyword == Token::VarUint) return std::make_tuple(Token::VarUintM, m, 0); - return std::make_tuple(Token::VarIntM, m, 0); + if (keyword == Token::Varuint) + return std::make_tuple(Token::VaruintM, m, 0); + if (keyword == Token::VarInt) + return std::make_tuple(Token::VarIntM, m, 0); + if (keyword == Token::Varint) + return std::make_tuple(Token::VarintM, m, 0); + solUnimplemented(""); } } else if (keyword == Token::Bytes) @@ -217,6 +237,21 @@ std::tuple fromIdentifierOrKeyword(std::strin return std::make_tuple(Token::IntM, m, 0); } } + else if (keyword == Token::QUInt || keyword == Token::QInt) + { + int bitLength = keyword == Token::QUInt ? 256 : 257; + if (0 < m && m <= bitLength && positionX == _literal.end()) + { + if (keyword == Token::QUInt) + return std::make_tuple(Token::QUIntM, m, 0); + else + return std::make_tuple(Token::QIntM, m, 0); + } + } + else if (keyword == Token::QBool) + { + return std::make_tuple(Token::QBool, 0, 0); + } else if (keyword == Token::UFixed || keyword == Token::Fixed) { if ( diff --git a/compiler/liblangutil/Token.h b/compiler/liblangutil/Token.h index 5515e18b..35f415ee 100644 --- a/compiler/liblangutil/Token.h +++ b/compiler/liblangutil/Token.h @@ -144,7 +144,6 @@ namespace solidity::langutil K(Anonymous, "anonymous", 0) \ K(As, "as", 0) \ K(Assembly, "assembly", 0) \ - K(Await, "await", 0) \ K(Break, "break", 0) \ K(Catch, "catch", 0) \ K(Constant, "constant", 0) \ @@ -157,7 +156,6 @@ namespace solidity::langutil K(Emit, "emit", 0) \ K(Event, "event", 0) \ K(External, "external", 0) \ - K(ExtMsg, "extMsg", 0) \ K(Fallback, "fallback", 0) \ K(onBounce, "onBounce", 0) \ K(For, "for", 0) \ @@ -180,6 +178,7 @@ namespace solidity::langutil K(NoStorage, "nostorage", 0) \ K(Optional, "optional", 0) \ K(TvmVector, "vector", 0) \ + K(TvmStack, "stack", 0) \ K(Override, "override", 0) \ K(Payable, "payable", 0) \ K(Public, "public", 0) \ @@ -239,6 +238,9 @@ namespace solidity::langutil /* type keywords*/ \ K(Int, "int", 0) \ K(UInt, "uint", 0) \ + K(QInt, "qint", 0) \ + K(QUInt, "quint", 0) \ + K(QBool, "qbool", 0) \ K(Bytes, "bytes", 0) \ K(String, "string", 0) \ K(Address, "address", 0) \ @@ -246,19 +248,26 @@ namespace solidity::langutil K(TvmCell, "TvmCell", 0) \ K(TvmSlice, "TvmSlice", 0) \ K(TvmBuilder, "TvmBuilder", 0) \ + K(StringBuilder, "StringBuilder", 0) \ K(Variant, "variant", 0) \ K(Fixed, "fixed", 0) \ K(UFixed, "ufixed", 0) \ T(IntM, "intM", 0) \ T(UIntM, "uintM", 0) \ + T(QIntM, "qintM", 0) \ + T(QUIntM, "quintM", 0) \ T(BytesM, "bytesM", 0) \ T(FixedMxN, "fixedMxN", 0) \ T(UFixedMxN, "ufixedMxN", 0) \ K(VarInt, "varInt", 0) \ + K(Varint, "varint", 0) \ T(VarIntM, "varIntM", 0) \ + T(VarintM, "varintM", 0) \ K(VarUint, "varUint", 0) \ - K(coins, "coins", 0) \ + K(Varuint, "varuint", 0) \ T(VarUintM, "varUintM", 0) \ + T(VaruintM, "varuintM", 0) \ + K(coins, "coins", 0) \ T(TypesEnd, nullptr, 0) /* used as type enum end marker */ \ \ /* Literals */ \ @@ -293,6 +302,7 @@ namespace solidity::langutil K(Mutable, "mutable", 0) \ K(NullLiteral, "null", 0) \ K(EmptyMap, "emptyMap", 0) \ + K(TVMNaN, "NaN", 0) \ K(Of, "of", 0) \ K(Partial, "partial", 0) \ K(Promise, "promise", 0) \ diff --git a/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp b/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp index ca4319cd..d6703235 100644 --- a/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/compiler/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -294,7 +294,16 @@ void DeclarationTypeChecker::endVisit(TvmVector const& _tvmVector) return; TypeName const& type = _tvmVector.type(); - _tvmVector.annotation().type = TypeProvider::tvmtuple(type.annotation().type); + _tvmVector.annotation().type = TypeProvider::tvmVector(type.annotation().type); +} + +void DeclarationTypeChecker::endVisit(TvmStack const& _tvmStack) +{ + if (_tvmStack.annotation().type) + return; + + TypeName const& type = _tvmStack.type(); + _tvmStack.annotation().type = TypeProvider::tvmStack(type.annotation().type); } void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) @@ -380,6 +389,12 @@ bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor) for (ASTPointer const& function: _usingFor.functionsOrLibrary()) if (auto functionDefinition = dynamic_cast(function->annotation().referencedDeclaration)) { + if (functionDefinition->isInlineAssembly()) + m_errorReporter.typeError( + 1167_error, + function->location(), + "Only file-level functions (not assembly) and library functions can be attached to a type in a \"using\" statement." + ); if (!functionDefinition->isFree() && !( dynamic_cast(functionDefinition->scope()) && dynamic_cast(functionDefinition->scope())->isLibrary() diff --git a/compiler/libsolidity/analysis/DeclarationTypeChecker.h b/compiler/libsolidity/analysis/DeclarationTypeChecker.h index 2f1407c0..b9b40385 100644 --- a/compiler/libsolidity/analysis/DeclarationTypeChecker.h +++ b/compiler/libsolidity/analysis/DeclarationTypeChecker.h @@ -58,6 +58,7 @@ class DeclarationTypeChecker: private ASTConstVisitor void endVisit(Mapping const& _mapping) override; void endVisit(Optional const& _optional) override; void endVisit(TvmVector const& _tvmVector) override; + void endVisit(TvmStack const& _tvmStack) override; void endVisit(ArrayTypeName const& _typeName) override; void endVisit(VariableDeclaration const& _variable) override; bool visit(EnumDefinition const& _enum) override; diff --git a/compiler/libsolidity/analysis/SyntaxChecker.cpp b/compiler/libsolidity/analysis/SyntaxChecker.cpp index 96f47219..5787de9b 100644 --- a/compiler/libsolidity/analysis/SyntaxChecker.cpp +++ b/compiler/libsolidity/analysis/SyntaxChecker.cpp @@ -55,7 +55,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) SemVerVersion recommendedVersion{std::string(VersionString)}; if (!recommendedVersion.isPrerelease()) errorString += - " Consider adding \"pragma ever-solidity ^" + + " Consider adding \"pragma tvm-solidity ^" + std::to_string(recommendedVersion.major()) + std::string(".") + std::to_string(recommendedVersion.minor()) + @@ -141,7 +141,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) SemVerVersion recommendedVersion{std::string(VersionString)}; std::string errorString = "It's deprecated." - " Consider adding \"pragma ever-solidity ^" + + " Consider adding \"pragma tvm-solidity ^" + std::to_string(recommendedVersion.major()) + std::string(".") + std::to_string(recommendedVersion.minor()) + @@ -151,7 +151,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) " to set a version of the compiler."; m_errorReporter.warning(6413_error, _pragma.location(), errorString); } - else if (_pragma.literals()[0] == "ever" || _pragma.literals()[0] == "ton") // ever-solidity + else if (_pragma.literals()[0] == "ever" || _pragma.literals()[0] == "ton" || _pragma.literals()[0] == "tvm") { if (m_versionPragma.has_value()) { m_errorReporter.fatalTypeError( @@ -480,6 +480,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) // Handled in experimental::SyntaxRestrictor instead. return true; + if (!_function.isFree() && _function.isInlineAssembly()) + m_errorReporter.fatalTypeError(7229_error, _function.location(), "Only free functions can be marked as \"assembly\"."); + if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { std::string suggestedVisibility = diff --git a/compiler/libsolidity/analysis/TypeChecker.cpp b/compiler/libsolidity/analysis/TypeChecker.cpp index 24d85434..55adc553 100644 --- a/compiler/libsolidity/analysis/TypeChecker.cpp +++ b/compiler/libsolidity/analysis/TypeChecker.cpp @@ -1945,9 +1945,14 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) // By default use the type we'd expect from correct code. This way we can continue analysis // of other expressions in a sensible way in case of a non-fatal error. + Type const* boolType; + if (commonType->category() == Type::Category::QInteger || commonType->category() == Type::Category::QBool) + boolType = TypeProvider::qBool(); + else + boolType = TypeProvider::boolean(); Type const* resultType = TokenTraits::isCompareOp(_operation.getOperator()) ? - TypeProvider::boolean() : + boolType : commonType; if (operatorDefinition) @@ -2285,43 +2290,95 @@ void TypeChecker::checkNeedCallback(FunctionType const* callee, ASTNode const& n } } -void TypeChecker::typeCheckTvmEncodeArg(Type const* type, Expression const& node) { +TypeChecker::Result TypeChecker::canStoreToBuilder(Type const* type, bool _topType) { switch (type->category()) { - case Type::Category::RationalNumber: - m_errorReporter.typeError( - 6332_error, - node.location(), - "Cannot perform encoding for a literal." - " Please convert it to an explicit type first." - ); - break; - case Type::Category::Address: - case Type::Category::Array: - case Type::Category::Bool: - case Type::Category::Contract: - case Type::Category::Enum: - case Type::Category::FixedBytes: - case Type::Category::Integer: - case Type::Category::Mapping: - case Type::Category::StringLiteral: - case Type::Category::Struct: - case Type::Category::TvmBuilder: - case Type::Category::TvmCell: - case Type::Category::TvmSlice: - case Type::Category::VarInteger: - break; - case Type::Category::Optional: + case Type::Category::Function: { + // External functions are not supported yet. + return {true, std::nullopt}; + } + case Type::Category::Tuple: { + auto tupleType = dynamic_cast(type); + for (Type const* comp : tupleType->components()) { - Type const *valueType = dynamic_cast(type)->valueType(); - typeCheckTvmEncodeArg(valueType, node); - break; + auto res = canStoreToBuilder(comp, false); + if (!res.ok) + return {false, res.location.has_value() ? res.location : std::nullopt}; } - default: + return {true, std::nullopt}; + } + case Type::Category::RationalNumber: + return {false, std::nullopt}; + case Type::Category::Struct: + { + auto structType = dynamic_cast(type); + StructDefinition const& structDefinition = structType->structDefinition(); + for (auto const& memberDecl : structDefinition.members()) { + auto res = canStoreToBuilder(memberDecl->type(), false); + if (!res.ok) + return {false, res.location.has_value() ? res.location : memberDecl->location()}; + } + return {true, std::nullopt}; + } + case Type::Category::Optional: + { + Type const *valueType = dynamic_cast(type)->valueType(); + auto res = canStoreToBuilder(valueType, false); + if (!res.ok) + return {false, res.location.has_value() ? res.location : std::nullopt}; + return {true, std::nullopt}; + } + case Type::Category::TvmBuilder: + case Type::Category::TvmSlice: + return {_topType, std::nullopt}; + case Type::Category::Address: + case Type::Category::Array: + case Type::Category::Bool: + case Type::Category::Contract: + case Type::Category::Enum: + case Type::Category::FixedBytes: + case Type::Category::FixedPoint: + case Type::Category::Integer: + case Type::Category::Mapping: + case Type::Category::StringLiteral: + case Type::Category::TvmCell: + case Type::Category::VarInteger: + return {true, std::nullopt}; + default: + return {false, std::nullopt}; + } +} + +void TypeChecker::typeCheckTvmEncodeArg(Type const* type, SourceLocation const& _location, + std::string const& errMsg, bool isStateVar) { + auto res = canStoreToBuilder(type, true); + if (isStateVar && (type->category() == Type::Category::TvmSlice || type->category() == Type::Category::TvmBuilder)) + res.ok = false; + if (res.ok) + return; + switch (type->category()) { + case Type::Category::RationalNumber: + m_errorReporter.typeError( + 6332_error, + _location, + "Cannot perform storing for a literal." + " Please convert it to an explicit type first." + ); + break; + default: + if (res.location) m_errorReporter.typeError( - 9100_error, - node.location(), - "Encoding for a " + node.annotation().type->toString(true) + " isn't supported." + 2562_error, + _location, + SecondarySourceLocation().append("Declaration of unsupported member:", res.location.value()), + errMsg ); + else + m_errorReporter.typeError( + 6820_error, + _location, + errMsg + ); + break; } } @@ -2329,7 +2386,8 @@ void TypeChecker::typeCheckTvmEncodeFunctions(FunctionCall const& _functionCall) std::vector> const &arguments = _functionCall.arguments(); for (const auto & argument : arguments) { auto const &argType = type(*argument); - typeCheckTvmEncodeArg(argType, *argument); + std::string const errText = "Storing for \"" + argType->toString(true) + "\" is not supported."; + typeCheckTvmEncodeArg(argType, argument->location(), errText, false); } } @@ -2721,7 +2779,6 @@ void TypeChecker::typeCheckFunctionGeneralChecks( ma->memberName() == "encodeData" || ma->memberName() == "encodeOldDataInit" || ma->memberName() == "buildExtMsg" || - ma->memberName() == "encodeExtMsg" || ma->memberName() == "encodeIntMsg" || ma->memberName() == "buildIntMsg") isFunctionWithDefaultValues = true; @@ -3015,8 +3072,6 @@ void TypeChecker::typeCheckFunctionGeneralChecks( auto functionCallOpt = dynamic_cast(&_functionCall.expression()); if (functionCallOpt) { - bool isExternalInboundMessage = _functionCall.isExtMsg(); - std::vector arr; auto fold = [&]() { std::string s; @@ -3031,9 +3086,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( }; const bool isNewExpression = dynamic_cast(&functionCallOpt->expression()) != nullptr; - if (isExternalInboundMessage) { - arr = {"time", "expire", "call", "sign", "pubkey", "abiVer", "callbackId", "onErrorId", "stateInit","signBoxHandle", "flags"}; - } else if (isNewExpression) { + if (isNewExpression) { arr = {"stateInit", "code", "pubkey", "varInit", "splitDepth", "wid", "value", "currencies", "bounce", "flag"}; } else { arr = {"value", "currencies", "bounce", "flag", "callback"}; @@ -3045,18 +3098,14 @@ void TypeChecker::typeCheckFunctionGeneralChecks( std::string const &name = *(names[i]); if (std::find(arr.begin(), arr.end(), name) == arr.end()) { m_errorReporter.typeError( - 4187_error, - functionCallOpt->location(), - "Unknown option \"" + name + "\". " + - "Valid options are " + fold() + "." + 4187_error, + functionCallOpt->location(), + "Unknown option \"" + name + "\". " + + "Valid options are " + fold() + "." ); } - if (name == "pubkey") { - if (isExternalInboundMessage) - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint256())); - else - expectType(*options[i], *TypeProvider::uint256()); - } + if (name == "pubkey") + expectType(*options[i], *TypeProvider::uint256()); } auto getId = [&](std::string const & param) { auto it = find_if(names.begin(), names.end(), [&](auto el){ @@ -3066,32 +3115,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks( }; auto callback = getId("callback"); - if (callback != -1 && _functionCall.isAwait()) - m_errorReporter.typeError( - 8276_error, - _functionCall.location(), - R"("callback" option can't be set for await call.)" - ); - auto callbackId = getId("callbackId"); - auto onErrorId = getId("onErrorId"); auto expressionFunctionType = dynamic_cast(type(functionCallOpt->expression())); - if (!_functionCall.isExtMsg() && callback == -1 && - !expressionFunctionType->returnParameterTypes().empty() && - !_functionCall.isAwait()) { + if (callback == -1 && !expressionFunctionType->returnParameterTypes().empty()) checkNeedCallback(expressionFunctionType, *functionCallOpt); - } - if (isExternalInboundMessage && - (callbackId == -1 || onErrorId == -1)) { - m_errorReporter.typeError( - 3365_error, - functionCallOpt->location(), - R"("callbackId" and "onErrorId" options must be set.)" - ); - } - - } - } FunctionDefinition const* @@ -3290,97 +3317,6 @@ void TypeChecker::checkCallList( } } -void TypeChecker::checkBuildExtMsg(FunctionCall const& _functionCall) { - std::vector> const& arguments = _functionCall.arguments(); - std::vector> const &argumentNames = _functionCall.names(); - auto findName = [&](const ASTString& optName) { - auto it = std::find_if(argumentNames.begin(), argumentNames.end(), - [&](const ASTPointer &name) { - return *name == optName; - }); - return it == argumentNames.end() ? -1 : it - argumentNames.begin(); - }; - - - for (const std::string name: {"dest", "call", "callbackId", "onErrorId"}) { - int index = findName(name); - if (index == -1){ - m_errorReporter.fatalTypeError( - 2651_error, - _functionCall.location(), - "Parameter \"" + name + "\" must be set." - ); - } - } - - if ( - int SignIndex = findName("sign"); - SignIndex != -1 - ){ - auto const& ann = arguments[SignIndex]->annotation(); - if (ann.type->category() != Type::Category::Bool || !*ann.isPure) { - m_errorReporter.typeError( - 9588_error, - arguments[SignIndex]->location(), - "\"sign\" parameter must have a constant boolean type." - ); - } - } - - FunctionDefinition const *remoteFunction{}; - int indexCall = findName("call"); - if (indexCall != -1) { - auto callList = dynamic_cast(arguments.at(indexCall).get()); - if (callList) { - std::vector params; - params.push_back(callList->function()); - for (const ASTPointer& p : callList->arguments()) { - params.push_back(p.get()); - } - checkCallList(params, _functionCall, true); - remoteFunction = checkPubFunctionOrContractTypeAndGetDefinition(*callList->function()); - } - } - - int callbackIndex = findName("callbackId"); - int errorIndex = findName("onErrorId"); - FunctionDefinition const* callBackFunction{}; - FunctionDefinition const* errorFunction{}; - for (std::string name : std::vector{"callbackId", "onErrorId"}) { - if ( - int nameIndex = findName(name); - nameIndex != -1 - ) { - auto const& ann = arguments.at(nameIndex)->annotation(); - auto intType = dynamic_cast(ann.type->mobileType()); - if (name == "callbackId") { - callBackFunction = checkPubFunctionAndGetDefinition(*arguments.at(nameIndex)); - } - if (name == "onErrorId") { - errorFunction = checkPubFunctionAndGetDefinition(*arguments.at(nameIndex)); - } - if (!(callBackFunction || (intType && !intType->isSigned() && intType->numBits() <= 32))) { - m_errorReporter.typeError( - 1693_error, - arguments[nameIndex]->location(), - std::string{} + - "Invalid type for argument in function call. " + - "Expected functionID of uint32 type or just function name (ContractName.functionName or functionName)." - ); - } - } - } - - - if (callbackIndex != -1 && remoteFunction != nullptr && callBackFunction != nullptr) { - checkRemoteAndCallBackFunctions(remoteFunction, callBackFunction, arguments.at(callbackIndex)->location()); - } - - if (errorFunction) { - checkOnErrorId(errorFunction, arguments.at(errorIndex)->location()); - } -} - void TypeChecker::checkRemoteAndCallBackFunctions( FunctionDefinition const* calleeDefinition, FunctionDefinition const* callbackFunc, @@ -3424,27 +3360,6 @@ void TypeChecker::checkRemoteAndCallBackFunctions( } } -void TypeChecker::checkOnErrorId(FunctionDefinition const* errorFunction, langutil::SourceLocation const& _location) { - auto badOnErrorFunction = [&]() { - m_errorReporter.typeError( - 2992_error, - _location, - SecondarySourceLocation().append("Parameters of the error function:", errorFunction->location()), - "Error function must take (uint32 sdkError, uint32 exitCode)." - ); - }; - std::vector> const& params = errorFunction->parameters(); - if (params.size() != 2) { - badOnErrorFunction(); - } - for (ASTPointer const& v : params) { - auto intType = dynamic_cast(v->type()); - if (!intType || intType->numBits() != 32) { - badOnErrorFunction(); - } - } -} - bool TypeChecker::visit(FunctionCall const& _functionCall) { std::vector> const& arguments = _functionCall.arguments(); @@ -3523,7 +3438,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) functionType->kind() == FunctionType::Kind::ByteArrayPush ) isLValue = functionType->parameterTypes().empty(); - else if (functionType->kind() == FunctionType::Kind::OptionalGet) + else if (functionType->kind() == FunctionType::Kind::OptionalGet || + functionType->kind() == FunctionType::Kind::TVMStackTop) isLValue = true; break; @@ -3565,7 +3481,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) funcCallAnno.isLValue = isLValue; - auto checkArgNumAndIsInteger = [&]( + auto checkArgNumAndIsIntOrVarInt = [&]( std::vector> const& arguments, size_t arguments_cnt, const std::function& cmpOperator, @@ -3573,7 +3489,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ){ if (!cmpOperator(arguments.size(), arguments_cnt)) { m_errorReporter.fatalTypeError( - 8309_error, + 1482_error, _functionCall.location(), errorMsg ); @@ -3584,7 +3500,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) Type::Category cat = t->mobileType()->category(); if (cat != Type::Category::Integer && cat != Type::Category::VarInteger) { m_errorReporter.fatalTypeError( - 4283_error, + 5329_error, arg->location(), "Expected an integer or variable integer type." ); @@ -3592,7 +3508,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } }; - auto checkArgQtyAndIsIntOrVarIntOrFixedPoint = [&]( + auto checkArgNumAndIsIntOrQintOrVarInt = [&]( std::vector> const& arguments, size_t arguments_cnt, const std::function& cmpOperator, @@ -3600,21 +3516,50 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ){ if (!cmpOperator(arguments.size(), arguments_cnt)) { m_errorReporter.fatalTypeError( - 1040_error, + 4529_error, _functionCall.location(), errorMsg ); } + for (const auto & arg : arguments) { + auto t = arg->annotation().type; + Type::Category cat = t->mobileType()->category(); + if (cat != Type::Category::Integer && cat != Type::Category::VarInteger && cat != Type::Category::QInteger) { + m_errorReporter.fatalTypeError( + 5640_error, + arg->location(), + "Expected an integer, qinteger or variable integer type." + ); + } + } + }; + + auto checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint = [&]( + std::vector> const& arguments, + size_t arguments_cnt, + const std::function& cmpOperator, + const std::string& errorMsg + ){ + if (!cmpOperator(arguments.size(), arguments_cnt)) + m_errorReporter.fatalTypeError( + 9838_error, + _functionCall.location(), + errorMsg + ); + for (const auto & arg : arguments) { Type::Category cat = arg->annotation().type->mobileType()->category(); - if (cat != Type::Category::Integer && cat != Type::Category::FixedPoint && cat != Type::Category::VarInteger) { + if (cat != Type::Category::Integer && + cat != Type::Category::QInteger && + cat != Type::Category::FixedPoint && + cat != Type::Category::VarInteger + ) m_errorReporter.fatalTypeError( 5943_error, arg->location(), - "Expected integer, variable integer or fixed point type." + "Expected integer, qinteger, variable integer or fixed point type." ); - } } }; @@ -3876,7 +3821,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::RndNext: { - checkArgNumAndIsInteger(arguments, 1, std::less_equal<>(), "Expected at most one argument."); + checkArgNumAndIsIntOrVarInt(arguments, 1, std::less_equal<>(), "Expected at most one argument."); if (arguments.empty()) { returnTypes.push_back(TypeProvider::uint256()); } else { @@ -3889,7 +3834,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::MathMin: case FunctionType::Kind::MathMax: { - checkArgQtyAndIsIntOrVarIntOrFixedPoint(arguments, 2, std::greater_equal<>(), "Expected at least two arguments."); + checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint(arguments, 2, std::greater_equal<>(), "Expected at least two arguments."); Type const* result = getCommonType(arguments); paramTypes = TypePointers(arguments.size(), result); returnTypes.push_back(result); @@ -3897,7 +3842,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::MathMinMax: { - checkArgQtyAndIsIntOrVarIntOrFixedPoint(arguments, 2, std::equal_to<>(), "Expected two arguments."); + checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint(arguments, 2, std::equal_to<>(), "Expected two arguments."); Type const* result = getCommonType(arguments); paramTypes = TypePointers(arguments.size(), result); returnTypes.push_back(result); @@ -3907,7 +3852,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::MathDivR: case FunctionType::Kind::MathDivC: { - checkArgQtyAndIsIntOrVarIntOrFixedPoint(arguments, 2, std::equal_to<>(), "Expected two arguments."); + checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint(arguments, 2, std::equal_to<>(), "Expected two arguments."); checkAllAreNotFractions(arguments); Type const* result = getCommonType(arguments); paramTypes = TypePointers(arguments.size(), result); @@ -3916,7 +3861,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::MathDivMod: { - checkArgNumAndIsInteger(arguments, 2, std::equal_to<>(), "Expected two arguments."); + checkArgNumAndIsIntOrQintOrVarInt(arguments, 2, std::equal_to<>(), "Expected two arguments."); Type const* result = getCommonType(arguments); paramTypes.push_back(result); paramTypes.push_back(result); @@ -3927,7 +3872,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::MathMulDiv: case FunctionType::Kind::MathMulDivMod: { - checkArgNumAndIsInteger(arguments, 3, std::equal_to<>(), "Expected three arguments."); + checkArgNumAndIsIntOrQintOrVarInt(arguments, 3, std::equal_to<>(), "Expected three arguments."); Type const* result = getCommonType(arguments); paramTypes.push_back(result); paramTypes.push_back(result); @@ -3940,17 +3885,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::MathAbs: { - checkArgQtyAndIsIntOrVarIntOrFixedPoint(arguments, 1, std::equal_to<>(), "Expected one argument."); + checkArgQtyAndIsIntOrQIntOrVarIntOrFixedPoint(arguments, 1, std::equal_to<>(), "Expected one argument."); Type const* argType = arguments.at(0)->annotation().type->mobileType(); Type::Category cat = argType->category(); paramTypes.push_back(argType); - if (cat == Type::Category::Integer || cat == Type::Category::VarInteger) { + if (cat == Type::Category::Integer || cat == Type::Category::VarInteger || cat == Type::Category::QInteger) { IntegerType const* intType; if (cat == Type::Category::Integer) intType = dynamic_cast(argType); else if (cat == Type::Category::VarInteger) intType = &dynamic_cast(argType)->asIntegerType(); + else if (cat == Type::Category::QInteger) + intType = dynamic_cast(argType)->asIntegerType(); else solUnimplemented(""); if (!intType->isSigned()) @@ -3965,7 +3912,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) arguments.at(0)->location(), "Too low type." ); - returnTypes.push_back(TypeProvider::integer(intType->numBits() - 1, IntegerType::Modifier::Unsigned)); + if (cat == Type::Category::QInteger) + returnTypes.push_back(TypeProvider::qInteger(intType->numBits() - 1, IntegerType::Modifier::Unsigned)); + else + returnTypes.push_back(TypeProvider::integer(intType->numBits() - 1, IntegerType::Modifier::Unsigned)); } else if (cat == Type::Category::FixedPoint) { auto fixed = dynamic_cast(argType); if (!fixed->isSigned()) @@ -3979,24 +3929,37 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) fixed->fractionalDigits(), FixedPointType::Modifier::Unsigned )); - } + } else + solUnimplemented(""); break; } case FunctionType::Kind::MathModpow2: { - checkArgNumAndIsInteger(arguments, 2, std::equal_to<>(), "Expected two arguments."); + checkArgNumAndIsIntOrQintOrVarInt(arguments, 2, std::equal_to<>(), "Expected two arguments."); bool isConst = *arguments[1]->annotation().isPure; if (!isConst) { m_errorReporter.fatalTypeError( - 8650_error, - arguments.at(1)->location(), - "Expected a constant integer type but got " + - arguments[1]->annotation().type->toString() + "." + 8650_error, + arguments.at(1)->location(), + "Expected a constant integer type but got " + + arguments[1]->annotation().type->toString() + "." ); } + returnTypes.push_back(arguments.at(0)->annotation().type->mobileType()); break; } + case FunctionType::Kind::MathSign: + { + checkArgNumAndIsIntOrQintOrVarInt(arguments, 1, std::equal_to<>(), "Expected one argument."); + Type const* argType = arguments.at(0)->annotation().type; + paramTypes.emplace_back(argType->mobileType()); + if (argType->category() == Type::Category::QInteger) + returnTypes.emplace_back(TypeProvider::qInteger(2, IntegerType::Modifier::Signed)); + else + returnTypes.emplace_back(TypeProvider::integer(2, IntegerType::Modifier::Signed)); + break; + } case FunctionType::Kind::ABIEncode: case FunctionType::Kind::TVMBuilderStore: { @@ -4020,7 +3983,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else { auto arg0Type = arguments[0]->annotation().type; if (keyType->category() == Type::Category::Integer) { - checkArgNumAndIsInteger(arguments, 1, std::equal_to<>(), "Expected one argument."); + checkArgNumAndIsIntOrVarInt(arguments, 1, std::equal_to<>(), "Expected one argument."); } else if (!arg0Type->isImplicitlyConvertibleTo(*keyType)) { auto errorMsg = "Type " + arg0Type->toString() + @@ -4119,14 +4082,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } - case FunctionType::Kind::ABIBuildExtMsg: { - checkHasNamedParams(); - checkBuildExtMsg(_functionCall); - - typeCheckFunctionCall(_functionCall, functionType, {"callbackId", "onErrorId"}); - returnTypes = functionType->returnParameterTypes(); - break; - } case FunctionType::Kind::ABIEncodeStateInit: { typeCheckFunctionCall(_functionCall, functionType); returnTypes = m_evmVersion.supportsReturndata() ? @@ -4285,9 +4240,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::TVMHash: { - if (arguments.size() != 1) { + if (arguments.size() != 1) m_errorReporter.typeError(1218_error, _functionCall.location(), "Expected one argument."); - } auto type = arguments[0]->annotation().type->mobileType(); auto cat = type->category(); if (!isByteArrayOrString(type) && cat != Type::Category::TvmCell && cat != Type::Category::TvmSlice) { @@ -4301,6 +4255,30 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes.push_back(TypeProvider::uint256()); break; } + case FunctionType::Kind::TVMStackSort: { + if (arguments.size() != 1) + m_errorReporter.typeError(9470_error, _functionCall.location(), "Expected one argument."); + auto type = arguments[0]->annotation().type; + auto funType = to(type); + auto ma = dynamic_cast(&_functionCall.expression()); + auto const valueType = to(ma->expression().annotation().type)->valueType(); + if (!funType || + funType->parameterTypes().size() != 2 || + *funType->parameterTypes().at(0) != *valueType || + *funType->parameterTypes().at(1) != *valueType || + funType->returnParameterTypes().size() != 1 || + *funType->returnParameterTypes().at(0) != *TypeProvider::boolean() + ) { + auto valueStr = valueType->toString(); + m_errorReporter.typeError( + 6306_error, + arguments[0]->location(), + "Expected \"function(" + valueStr + ", " + valueStr + ") private/internal returns(bool)\" type." + ); + } + paramTypes.push_back(type); + break; + } case FunctionType::Kind::IntCast: { if (arguments.size() != 1) m_errorReporter.fatalTypeError(5461_error, _functionCall.location(), "Expected one argument."); @@ -4324,9 +4302,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) default: { typeCheckFunctionCall(_functionCall, functionType); - if (functionType->kind() != FunctionType::Kind::External || _functionCall.isAwait()) { + if (functionType->kind() != FunctionType::Kind::External) returnTypes = functionType->returnParameterTypes(); - } break; } } @@ -4352,31 +4329,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (functionType != nullptr && dynamic_cast(&_functionCall.expression()) == nullptr && - functionType->kind() == FunctionType::Kind::External && !_functionCall.isAwait()) + functionType->kind() == FunctionType::Kind::External) { checkNeedCallback(functionType, _functionCall); } - if (_functionCall.isAwait()) { - if (functionType == nullptr || functionType->kind() != FunctionType::Kind::External) { - m_errorReporter.fatalTypeError( - 5451_error, - _functionCall.location(), - "\".await\" is supported only for external function calls." - ); - } - if (functionType) { - auto fd = dynamic_cast(&functionType->declaration()); - if (fd && !fd->isResponsible()) { - m_errorReporter.fatalTypeError( - 9054_error, - _functionCall.location(), - "\".await\" is supported only for responsible functions." - ); - } - } - } - return false; } @@ -4411,24 +4368,17 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) int setVarInit = -1; int setWid = -1; int setCallback = -1; - int setSign = -1; - int setExpire = -1; - int setTime = -1; - int setOnError = -1; - int setSignHandler = -1; - int setAbiVer = -1; - int setFlags = -1; FunctionType::Kind kind = expressionFunctionType->kind(); bool isLib = false; - if (expressionFunctionType->hasDeclaration()) { - if (auto func = dynamic_cast(&expressionFunctionType->declaration())) { - if (func->annotation().contract->isLibrary()) { + if (expressionFunctionType->hasDeclaration()) + if (auto func = dynamic_cast(&expressionFunctionType->declaration())) + { + ContractDefinition const* contract = func->annotation().contract; + if (contract != nullptr && contract->isLibrary()) isLib = true; - } } - } if ( !isLib && kind != FunctionType::Kind::Creation && @@ -4466,7 +4416,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) if (isNewExpression) arr = {"stateInit", "code", "pubkey", "varInit", "splitDepth", "wid", "value", "currencies", "bounce", "flag"}; else - arr = {"time", "expire", "call", "sign", "pubkey", "abiVer", "callbackId", "onErrorId", "stateInit", "signBoxHandle", "value", "currencies", "bounce", "flag", "callback", "flags"}; + arr = {"call", "pubkey", "stateInit", "value", "currencies", "bounce", "flag", "callback"}; auto fold = [&](){ std::string s; for (size_t i = 0; i < arr.size(); ++i) { @@ -4488,68 +4438,11 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) "Unknown option \"" + name + "\". " + "Valid options are " + fold() + "." ); - // check type of option - } else if (name == "sign") { - expectType(*options[i], *TypeProvider::boolean()); - setCheckOption(setSign, "sign", i); - if (!*options[i]->annotation().isPure) - m_errorReporter.typeError( - 5289_error, - _functionCallOptions.location(), - R"(Option "sign" can be specified only with constant bool value.)"); } else if (name == "pubkey") { setCheckOption(setPubkey, "pubkey", i); - } else if (name == "signBoxHandle") { - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint(32))); - setCheckOption(setSignHandler, "signBoxHandle", i); - } else if (name == "abiVer") { - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint(8))); - setCheckOption(setAbiVer, "abiVer", i); - } else if (name == "flags") { - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint(8))); - setCheckOption(setFlags, "flags", i); } else if (name == "stateInit") { expectType(*options[i], *TypeProvider::optional(TypeProvider::tvmcell())); setCheckOption(setStateInit, "stateInit", i); - } else if (name == "callbackId") { - ASTPointer const& exp = options.at(i); - exp->accept(*this); - FunctionDefinition const* callBackFunction = checkPubFunctionAndGetDefinition(*exp); - auto intType = dynamic_cast(type(*exp.get())->mobileType()); - if (!(callBackFunction || (intType && !intType->isSigned() && intType->numBits() <= 32))) { - m_errorReporter.typeError( - 3344_error, - options.at(i)->location(), - "Expected functionID of uint32 type or just function name (ContractName.functionName or functionName)." - ); - } - auto remoteFunction = dynamic_cast(&expressionFunctionType->declaration()); - if (callBackFunction) { - checkRemoteAndCallBackFunctions(remoteFunction, callBackFunction, options.at(i)->location()); - } - setCheckOption(setCallback, "callbackId", i); - } else if (name == "onErrorId") { - ASTPointer const& exp = options.at(i); - exp->accept(*this); - FunctionDefinition const* errorFunDef = checkPubFunctionAndGetDefinition(*exp); - auto intType = dynamic_cast(type(*exp.get())->mobileType()); - if (!(errorFunDef || (intType && !intType->isSigned() && intType->numBits() <= 32))) { - m_errorReporter.typeError( - 7946_error, - options.at(i)->location(), - "Expected functionID of uint32 type or just function name (ContractName.functionName or functionName)." - ); - } - if (errorFunDef) { - checkOnErrorId(errorFunDef, options.at(i)->location()); - } - setCheckOption(setOnError, "onErrorId", i); - } else if (name == "expire") { - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint(32))); - setCheckOption(setExpire, "expire", i); - } else if (name == "time") { - expectType(*options[i], *TypeProvider::optional(TypeProvider::uint(64))); - setCheckOption(setTime, "time", i); } else if (name == "bounce") { expectType(*options[i], *TypeProvider::boolean()); setCheckOption(setBounce, "bounce", i); @@ -5064,7 +4957,7 @@ bool TypeChecker::visit(IndexAccess const& _access) expectType(*index, *TypeProvider::uint256()); } resultType = actualType.valueType(); - isLValue = false; + isLValue = true; break; } default: diff --git a/compiler/libsolidity/analysis/TypeChecker.h b/compiler/libsolidity/analysis/TypeChecker.h index b66063a5..92a4f2ec 100644 --- a/compiler/libsolidity/analysis/TypeChecker.h +++ b/compiler/libsolidity/analysis/TypeChecker.h @@ -134,7 +134,15 @@ class TypeChecker: private ASTConstVisitor FunctionCall const& _functionCall, FunctionTypePointer _functionType ); - void typeCheckTvmEncodeArg(Type const* type, Expression const& node); + struct Result { + bool ok{}; + std::optional location; + }; + static Result canStoreToBuilder(Type const* type, bool _topType); +public: + void typeCheckTvmEncodeArg(Type const* type, solidity::langutil::SourceLocation const& _location, + std::string const& errMsg, bool isStateVar); +private: void typeCheckTvmEncodeFunctions(FunctionCall const& _functionCall); static FunctionDefinition const* getFunctionDefinition(Expression const* expr); static std::pair getConstructorDefinition(Expression const* expr); @@ -148,14 +156,11 @@ class TypeChecker: private ASTConstVisitor FunctionCall const& _functionCall, bool ignoreCallBack ); - void checkBuildExtMsg(FunctionCall const& _functionCall); void checkRemoteAndCallBackFunctions( FunctionDefinition const* calleeDefinition, FunctionDefinition const* callbackFunc, langutil::SourceLocation const& _location ); - void checkOnErrorId(FunctionDefinition const* errorFunction, langutil::SourceLocation const& _location); - /// Performs checks specific to the ABI encode functions of type ABIEncodeCall void typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall); diff --git a/compiler/libsolidity/analysis/ViewPureChecker.cpp b/compiler/libsolidity/analysis/ViewPureChecker.cpp index d7918116..914d5730 100644 --- a/compiler/libsolidity/analysis/ViewPureChecker.cpp +++ b/compiler/libsolidity/analysis/ViewPureChecker.cpp @@ -258,7 +258,8 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall) if (ma) { if (auto libFunction = dynamic_cast(ma->annotation().referencedDeclaration)) { DeclarationAnnotation const &da = libFunction->annotation(); - if (da.contract->isLibrary()) { + ContractDefinition const* contract = da.contract; + if (contract && contract->isLibrary()) { isLibCall = true; auto t = ma->expression().annotation().type; if (t->category() == Type::Category::TypeType) { @@ -350,7 +351,6 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::ABI, "encodeBody"}, {MagicType::Kind::ABI, "encodeCall"}, {MagicType::Kind::ABI, "encodeData"}, - {MagicType::Kind::ABI, "encodeExtMsg"}, {MagicType::Kind::ABI, "encodeIntMsg"}, {MagicType::Kind::ABI, "encodeOldDataInit"}, {MagicType::Kind::ABI, "encodePacked"}, diff --git a/compiler/libsolidity/ast/AST.h b/compiler/libsolidity/ast/AST.h index 6b490609..95ec9b86 100644 --- a/compiler/libsolidity/ast/AST.h +++ b/compiler/libsolidity/ast/AST.h @@ -1577,11 +1577,32 @@ class TvmVector: public TypeName { public: TvmVector( - int64_t _id, - SourceLocation const& _location, - ASTPointer const& _type + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _type + ): + TypeName(_id, _location), m_type(_type) {} + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + TypeName const& type() const { return *m_type.get(); } + +private: + ASTPointer m_type; +}; + +/** + * stack(Type) + */ +class TvmStack: public TypeName +{ +public: + TvmStack( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _type ): - TypeName(_id, _location), m_type(_type) {} + TypeName(_id, _location), m_type(_type) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; @@ -2307,23 +2328,16 @@ class BinaryOperation: public Expression class FunctionCall: public Expression { public: - enum class Kind { - Await, - ExtMsg, - Usual - }; - FunctionCall( int64_t _id, SourceLocation const& _location, ASTPointer _expression, std::vector> _arguments, std::vector> _names, - std::vector _nameLocations, - Kind kind + std::vector _nameLocations ): Expression(_id, _location), m_expression(std::move(_expression)), m_arguments(std::move(_arguments)), - m_names(std::move(_names)), m_nameLocations(std::move(_nameLocations)), m_kind(kind) + m_names(std::move(_names)), m_nameLocations(std::move(_nameLocations)) { solAssert(m_nameLocations.size() == m_names.size()); } @@ -2343,15 +2357,12 @@ class FunctionCall: public Expression std::vector const& nameLocations() const { return m_nameLocations; } FunctionCallAnnotation& annotation() const override; - bool isAwait() const { return m_kind == Kind::Await; } - bool isExtMsg() const { return m_kind == Kind::ExtMsg; } private: ASTPointer m_expression; std::vector> m_arguments; std::vector> m_names; std::vector m_nameLocations; - Kind m_kind; }; diff --git a/compiler/libsolidity/ast/ASTJsonExporter.cpp b/compiler/libsolidity/ast/ASTJsonExporter.cpp index cf5e1364..73e0d4c0 100644 --- a/compiler/libsolidity/ast/ASTJsonExporter.cpp +++ b/compiler/libsolidity/ast/ASTJsonExporter.cpp @@ -624,6 +624,13 @@ bool ASTJsonExporter::visit(TvmVector const& _node) { return false; } +bool ASTJsonExporter::visit(TvmStack const& _node) { + setJsonNode(_node, "stack", { + std::make_pair("type", toJson(_node.type())), + }); + return false; +} + bool ASTJsonExporter::visit(ArrayTypeName const& _node) { setJsonNode(_node, "ArrayTypeName", { @@ -1129,6 +1136,8 @@ std::string ASTJsonExporter::literalTokenKind(Token _token) return "bool"; case Token::EmptyMap: return "mapping"; + case Token::TVMNaN: + return "NaN"; case Token::NullLiteral: return "optional"; default: diff --git a/compiler/libsolidity/ast/ASTJsonExporter.h b/compiler/libsolidity/ast/ASTJsonExporter.h index 151b2e12..d918736b 100644 --- a/compiler/libsolidity/ast/ASTJsonExporter.h +++ b/compiler/libsolidity/ast/ASTJsonExporter.h @@ -97,6 +97,7 @@ class ASTJsonExporter: public ASTConstVisitor bool visit(Mapping const& _node) override; bool visit(Optional const& _node) override; bool visit(TvmVector const& _node) override; + bool visit(TvmStack const& _node) override; bool visit(ArrayTypeName const& _node) override; bool visit(FreeInlineAssembly const& _node) override; bool visit(Block const& _node) override; diff --git a/compiler/libsolidity/ast/ASTVisitor.h b/compiler/libsolidity/ast/ASTVisitor.h index d841ac2a..1d5b3fc3 100644 --- a/compiler/libsolidity/ast/ASTVisitor.h +++ b/compiler/libsolidity/ast/ASTVisitor.h @@ -79,6 +79,7 @@ class ASTVisitor virtual bool visit(Mapping& _node) { return visitNode(_node); } virtual bool visit(Optional& _node) { return visitNode(_node); } virtual bool visit(TvmVector& _node) { return visitNode(_node); } + virtual bool visit(TvmStack& _node) { return visitNode(_node); } virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); } virtual bool visit(FreeInlineAssembly& _node) { return visitNode(_node); } virtual bool visit(Block& _node) { return visitNode(_node); } @@ -150,6 +151,7 @@ class ASTVisitor virtual void endVisit(Mapping& _node) { endVisitNode(_node); } virtual void endVisit(Optional& _node) { endVisitNode(_node); } virtual void endVisit(TvmVector& _node) { endVisitNode(_node); } + virtual void endVisit(TvmStack& _node) { endVisitNode(_node); } virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); } virtual void endVisit(FreeInlineAssembly& _node) { endVisitNode(_node); } virtual void endVisit(Block& _node) { endVisitNode(_node); } @@ -243,6 +245,7 @@ class ASTConstVisitor virtual bool visit(Mapping const& _node) { return visitNode(_node); } virtual bool visit(Optional const& _node) { return visitNode(_node); } virtual bool visit(TvmVector const& _node) { return visitNode(_node); } + virtual bool visit(TvmStack const& _node) { return visitNode(_node); } virtual bool visit(ArrayTypeName const& _node) { return visitNode(_node); } virtual bool visit(Block const& _node) { return visitNode(_node); } virtual bool visit(PlaceholderStatement const& _node) { return visitNode(_node); } @@ -314,6 +317,7 @@ class ASTConstVisitor virtual void endVisit(Mapping const& _node) { endVisitNode(_node); } virtual void endVisit(Optional const& _node) { endVisitNode(_node); } virtual void endVisit(TvmVector const& _node) { endVisitNode(_node); } + virtual void endVisit(TvmStack const& _node) { endVisitNode(_node); } virtual void endVisit(ArrayTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(Block const& _node) { endVisitNode(_node); } virtual void endVisit(PlaceholderStatement const& _node) { endVisitNode(_node); } diff --git a/compiler/libsolidity/ast/AST_accept.h b/compiler/libsolidity/ast/AST_accept.h index aad41ed3..c9aac9ec 100644 --- a/compiler/libsolidity/ast/AST_accept.h +++ b/compiler/libsolidity/ast/AST_accept.h @@ -528,6 +528,24 @@ void TvmVector::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void TvmStack::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_type->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TvmStack::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_type->accept(_visitor); + } + _visitor.endVisit(*this); +} + void ArrayTypeName::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/compiler/libsolidity/ast/TypeProvider.cpp b/compiler/libsolidity/ast/TypeProvider.cpp index da760c8b..a9c192d2 100644 --- a/compiler/libsolidity/ast/TypeProvider.cpp +++ b/compiler/libsolidity/ast/TypeProvider.cpp @@ -31,6 +31,7 @@ EmptyMapType const TypeProvider::m_emptyMapType{}; TvmCellType const TypeProvider::m_tvmcell{}; TvmSliceType const TypeProvider::m_tvmslice{}; TvmBuilderType const TypeProvider::m_tvmbuilder{}; +StringBuilderType const TypeProvider::m_stringBuilder{}; InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{}; @@ -571,6 +572,529 @@ std::array, 256> const TypeProvider::m_uintM{{ {std::make_unique(256, IntegerType::Modifier::Unsigned)}, }}; +std::unique_ptr const TypeProvider::m_qintNAN = std::make_unique(); + +std::array, 257> const TypeProvider::m_qintM{{ + {std::make_unique(1, IntegerType::Modifier::Signed)}, + {std::make_unique(2, IntegerType::Modifier::Signed)}, + {std::make_unique(3, IntegerType::Modifier::Signed)}, + {std::make_unique(4, IntegerType::Modifier::Signed)}, + {std::make_unique(5, IntegerType::Modifier::Signed)}, + {std::make_unique(6, IntegerType::Modifier::Signed)}, + {std::make_unique(7, IntegerType::Modifier::Signed)}, + {std::make_unique(8, IntegerType::Modifier::Signed)}, + {std::make_unique(9, IntegerType::Modifier::Signed)}, + {std::make_unique(10, IntegerType::Modifier::Signed)}, + {std::make_unique(11, IntegerType::Modifier::Signed)}, + {std::make_unique(12, IntegerType::Modifier::Signed)}, + {std::make_unique(13, IntegerType::Modifier::Signed)}, + {std::make_unique(14, IntegerType::Modifier::Signed)}, + {std::make_unique(15, IntegerType::Modifier::Signed)}, + {std::make_unique(16, IntegerType::Modifier::Signed)}, + {std::make_unique(17, IntegerType::Modifier::Signed)}, + {std::make_unique(18, IntegerType::Modifier::Signed)}, + {std::make_unique(19, IntegerType::Modifier::Signed)}, + {std::make_unique(20, IntegerType::Modifier::Signed)}, + {std::make_unique(21, IntegerType::Modifier::Signed)}, + {std::make_unique(22, IntegerType::Modifier::Signed)}, + {std::make_unique(23, IntegerType::Modifier::Signed)}, + {std::make_unique(24, IntegerType::Modifier::Signed)}, + {std::make_unique(25, IntegerType::Modifier::Signed)}, + {std::make_unique(26, IntegerType::Modifier::Signed)}, + {std::make_unique(27, IntegerType::Modifier::Signed)}, + {std::make_unique(28, IntegerType::Modifier::Signed)}, + {std::make_unique(29, IntegerType::Modifier::Signed)}, + {std::make_unique(30, IntegerType::Modifier::Signed)}, + {std::make_unique(31, IntegerType::Modifier::Signed)}, + {std::make_unique(32, IntegerType::Modifier::Signed)}, + {std::make_unique(33, IntegerType::Modifier::Signed)}, + {std::make_unique(34, IntegerType::Modifier::Signed)}, + {std::make_unique(35, IntegerType::Modifier::Signed)}, + {std::make_unique(36, IntegerType::Modifier::Signed)}, + {std::make_unique(37, IntegerType::Modifier::Signed)}, + {std::make_unique(38, IntegerType::Modifier::Signed)}, + {std::make_unique(39, IntegerType::Modifier::Signed)}, + {std::make_unique(40, IntegerType::Modifier::Signed)}, + {std::make_unique(41, IntegerType::Modifier::Signed)}, + {std::make_unique(42, IntegerType::Modifier::Signed)}, + {std::make_unique(43, IntegerType::Modifier::Signed)}, + {std::make_unique(44, IntegerType::Modifier::Signed)}, + {std::make_unique(45, IntegerType::Modifier::Signed)}, + {std::make_unique(46, IntegerType::Modifier::Signed)}, + {std::make_unique(47, IntegerType::Modifier::Signed)}, + {std::make_unique(48, IntegerType::Modifier::Signed)}, + {std::make_unique(49, IntegerType::Modifier::Signed)}, + {std::make_unique(50, IntegerType::Modifier::Signed)}, + {std::make_unique(51, IntegerType::Modifier::Signed)}, + {std::make_unique(52, IntegerType::Modifier::Signed)}, + {std::make_unique(53, IntegerType::Modifier::Signed)}, + {std::make_unique(54, IntegerType::Modifier::Signed)}, + {std::make_unique(55, IntegerType::Modifier::Signed)}, + {std::make_unique(56, IntegerType::Modifier::Signed)}, + {std::make_unique(57, IntegerType::Modifier::Signed)}, + {std::make_unique(58, IntegerType::Modifier::Signed)}, + {std::make_unique(59, IntegerType::Modifier::Signed)}, + {std::make_unique(60, IntegerType::Modifier::Signed)}, + {std::make_unique(61, IntegerType::Modifier::Signed)}, + {std::make_unique(62, IntegerType::Modifier::Signed)}, + {std::make_unique(63, IntegerType::Modifier::Signed)}, + {std::make_unique(64, IntegerType::Modifier::Signed)}, + {std::make_unique(65, IntegerType::Modifier::Signed)}, + {std::make_unique(66, IntegerType::Modifier::Signed)}, + {std::make_unique(67, IntegerType::Modifier::Signed)}, + {std::make_unique(68, IntegerType::Modifier::Signed)}, + {std::make_unique(69, IntegerType::Modifier::Signed)}, + {std::make_unique(70, IntegerType::Modifier::Signed)}, + {std::make_unique(71, IntegerType::Modifier::Signed)}, + {std::make_unique(72, IntegerType::Modifier::Signed)}, + {std::make_unique(73, IntegerType::Modifier::Signed)}, + {std::make_unique(74, IntegerType::Modifier::Signed)}, + {std::make_unique(75, IntegerType::Modifier::Signed)}, + {std::make_unique(76, IntegerType::Modifier::Signed)}, + {std::make_unique(77, IntegerType::Modifier::Signed)}, + {std::make_unique(78, IntegerType::Modifier::Signed)}, + {std::make_unique(79, IntegerType::Modifier::Signed)}, + {std::make_unique(80, IntegerType::Modifier::Signed)}, + {std::make_unique(81, IntegerType::Modifier::Signed)}, + {std::make_unique(82, IntegerType::Modifier::Signed)}, + {std::make_unique(83, IntegerType::Modifier::Signed)}, + {std::make_unique(84, IntegerType::Modifier::Signed)}, + {std::make_unique(85, IntegerType::Modifier::Signed)}, + {std::make_unique(86, IntegerType::Modifier::Signed)}, + {std::make_unique(87, IntegerType::Modifier::Signed)}, + {std::make_unique(88, IntegerType::Modifier::Signed)}, + {std::make_unique(89, IntegerType::Modifier::Signed)}, + {std::make_unique(90, IntegerType::Modifier::Signed)}, + {std::make_unique(91, IntegerType::Modifier::Signed)}, + {std::make_unique(92, IntegerType::Modifier::Signed)}, + {std::make_unique(93, IntegerType::Modifier::Signed)}, + {std::make_unique(94, IntegerType::Modifier::Signed)}, + {std::make_unique(95, IntegerType::Modifier::Signed)}, + {std::make_unique(96, IntegerType::Modifier::Signed)}, + {std::make_unique(97, IntegerType::Modifier::Signed)}, + {std::make_unique(98, IntegerType::Modifier::Signed)}, + {std::make_unique(99, IntegerType::Modifier::Signed)}, + {std::make_unique(100, IntegerType::Modifier::Signed)}, + {std::make_unique(101, IntegerType::Modifier::Signed)}, + {std::make_unique(102, IntegerType::Modifier::Signed)}, + {std::make_unique(103, IntegerType::Modifier::Signed)}, + {std::make_unique(104, IntegerType::Modifier::Signed)}, + {std::make_unique(105, IntegerType::Modifier::Signed)}, + {std::make_unique(106, IntegerType::Modifier::Signed)}, + {std::make_unique(107, IntegerType::Modifier::Signed)}, + {std::make_unique(108, IntegerType::Modifier::Signed)}, + {std::make_unique(109, IntegerType::Modifier::Signed)}, + {std::make_unique(110, IntegerType::Modifier::Signed)}, + {std::make_unique(111, IntegerType::Modifier::Signed)}, + {std::make_unique(112, IntegerType::Modifier::Signed)}, + {std::make_unique(113, IntegerType::Modifier::Signed)}, + {std::make_unique(114, IntegerType::Modifier::Signed)}, + {std::make_unique(115, IntegerType::Modifier::Signed)}, + {std::make_unique(116, IntegerType::Modifier::Signed)}, + {std::make_unique(117, IntegerType::Modifier::Signed)}, + {std::make_unique(118, IntegerType::Modifier::Signed)}, + {std::make_unique(119, IntegerType::Modifier::Signed)}, + {std::make_unique(120, IntegerType::Modifier::Signed)}, + {std::make_unique(121, IntegerType::Modifier::Signed)}, + {std::make_unique(122, IntegerType::Modifier::Signed)}, + {std::make_unique(123, IntegerType::Modifier::Signed)}, + {std::make_unique(124, IntegerType::Modifier::Signed)}, + {std::make_unique(125, IntegerType::Modifier::Signed)}, + {std::make_unique(126, IntegerType::Modifier::Signed)}, + {std::make_unique(127, IntegerType::Modifier::Signed)}, + {std::make_unique(128, IntegerType::Modifier::Signed)}, + {std::make_unique(129, IntegerType::Modifier::Signed)}, + {std::make_unique(130, IntegerType::Modifier::Signed)}, + {std::make_unique(131, IntegerType::Modifier::Signed)}, + {std::make_unique(132, IntegerType::Modifier::Signed)}, + {std::make_unique(133, IntegerType::Modifier::Signed)}, + {std::make_unique(134, IntegerType::Modifier::Signed)}, + {std::make_unique(135, IntegerType::Modifier::Signed)}, + {std::make_unique(136, IntegerType::Modifier::Signed)}, + {std::make_unique(137, IntegerType::Modifier::Signed)}, + {std::make_unique(138, IntegerType::Modifier::Signed)}, + {std::make_unique(139, IntegerType::Modifier::Signed)}, + {std::make_unique(140, IntegerType::Modifier::Signed)}, + {std::make_unique(141, IntegerType::Modifier::Signed)}, + {std::make_unique(142, IntegerType::Modifier::Signed)}, + {std::make_unique(143, IntegerType::Modifier::Signed)}, + {std::make_unique(144, IntegerType::Modifier::Signed)}, + {std::make_unique(145, IntegerType::Modifier::Signed)}, + {std::make_unique(146, IntegerType::Modifier::Signed)}, + {std::make_unique(147, IntegerType::Modifier::Signed)}, + {std::make_unique(148, IntegerType::Modifier::Signed)}, + {std::make_unique(149, IntegerType::Modifier::Signed)}, + {std::make_unique(150, IntegerType::Modifier::Signed)}, + {std::make_unique(151, IntegerType::Modifier::Signed)}, + {std::make_unique(152, IntegerType::Modifier::Signed)}, + {std::make_unique(153, IntegerType::Modifier::Signed)}, + {std::make_unique(154, IntegerType::Modifier::Signed)}, + {std::make_unique(155, IntegerType::Modifier::Signed)}, + {std::make_unique(156, IntegerType::Modifier::Signed)}, + {std::make_unique(157, IntegerType::Modifier::Signed)}, + {std::make_unique(158, IntegerType::Modifier::Signed)}, + {std::make_unique(159, IntegerType::Modifier::Signed)}, + {std::make_unique(160, IntegerType::Modifier::Signed)}, + {std::make_unique(161, IntegerType::Modifier::Signed)}, + {std::make_unique(162, IntegerType::Modifier::Signed)}, + {std::make_unique(163, IntegerType::Modifier::Signed)}, + {std::make_unique(164, IntegerType::Modifier::Signed)}, + {std::make_unique(165, IntegerType::Modifier::Signed)}, + {std::make_unique(166, IntegerType::Modifier::Signed)}, + {std::make_unique(167, IntegerType::Modifier::Signed)}, + {std::make_unique(168, IntegerType::Modifier::Signed)}, + {std::make_unique(169, IntegerType::Modifier::Signed)}, + {std::make_unique(170, IntegerType::Modifier::Signed)}, + {std::make_unique(171, IntegerType::Modifier::Signed)}, + {std::make_unique(172, IntegerType::Modifier::Signed)}, + {std::make_unique(173, IntegerType::Modifier::Signed)}, + {std::make_unique(174, IntegerType::Modifier::Signed)}, + {std::make_unique(175, IntegerType::Modifier::Signed)}, + {std::make_unique(176, IntegerType::Modifier::Signed)}, + {std::make_unique(177, IntegerType::Modifier::Signed)}, + {std::make_unique(178, IntegerType::Modifier::Signed)}, + {std::make_unique(179, IntegerType::Modifier::Signed)}, + {std::make_unique(180, IntegerType::Modifier::Signed)}, + {std::make_unique(181, IntegerType::Modifier::Signed)}, + {std::make_unique(182, IntegerType::Modifier::Signed)}, + {std::make_unique(183, IntegerType::Modifier::Signed)}, + {std::make_unique(184, IntegerType::Modifier::Signed)}, + {std::make_unique(185, IntegerType::Modifier::Signed)}, + {std::make_unique(186, IntegerType::Modifier::Signed)}, + {std::make_unique(187, IntegerType::Modifier::Signed)}, + {std::make_unique(188, IntegerType::Modifier::Signed)}, + {std::make_unique(189, IntegerType::Modifier::Signed)}, + {std::make_unique(190, IntegerType::Modifier::Signed)}, + {std::make_unique(191, IntegerType::Modifier::Signed)}, + {std::make_unique(192, IntegerType::Modifier::Signed)}, + {std::make_unique(193, IntegerType::Modifier::Signed)}, + {std::make_unique(194, IntegerType::Modifier::Signed)}, + {std::make_unique(195, IntegerType::Modifier::Signed)}, + {std::make_unique(196, IntegerType::Modifier::Signed)}, + {std::make_unique(197, IntegerType::Modifier::Signed)}, + {std::make_unique(198, IntegerType::Modifier::Signed)}, + {std::make_unique(199, IntegerType::Modifier::Signed)}, + {std::make_unique(200, IntegerType::Modifier::Signed)}, + {std::make_unique(201, IntegerType::Modifier::Signed)}, + {std::make_unique(202, IntegerType::Modifier::Signed)}, + {std::make_unique(203, IntegerType::Modifier::Signed)}, + {std::make_unique(204, IntegerType::Modifier::Signed)}, + {std::make_unique(205, IntegerType::Modifier::Signed)}, + {std::make_unique(206, IntegerType::Modifier::Signed)}, + {std::make_unique(207, IntegerType::Modifier::Signed)}, + {std::make_unique(208, IntegerType::Modifier::Signed)}, + {std::make_unique(209, IntegerType::Modifier::Signed)}, + {std::make_unique(210, IntegerType::Modifier::Signed)}, + {std::make_unique(211, IntegerType::Modifier::Signed)}, + {std::make_unique(212, IntegerType::Modifier::Signed)}, + {std::make_unique(213, IntegerType::Modifier::Signed)}, + {std::make_unique(214, IntegerType::Modifier::Signed)}, + {std::make_unique(215, IntegerType::Modifier::Signed)}, + {std::make_unique(216, IntegerType::Modifier::Signed)}, + {std::make_unique(217, IntegerType::Modifier::Signed)}, + {std::make_unique(218, IntegerType::Modifier::Signed)}, + {std::make_unique(219, IntegerType::Modifier::Signed)}, + {std::make_unique(220, IntegerType::Modifier::Signed)}, + {std::make_unique(221, IntegerType::Modifier::Signed)}, + {std::make_unique(222, IntegerType::Modifier::Signed)}, + {std::make_unique(223, IntegerType::Modifier::Signed)}, + {std::make_unique(224, IntegerType::Modifier::Signed)}, + {std::make_unique(225, IntegerType::Modifier::Signed)}, + {std::make_unique(226, IntegerType::Modifier::Signed)}, + {std::make_unique(227, IntegerType::Modifier::Signed)}, + {std::make_unique(228, IntegerType::Modifier::Signed)}, + {std::make_unique(229, IntegerType::Modifier::Signed)}, + {std::make_unique(230, IntegerType::Modifier::Signed)}, + {std::make_unique(231, IntegerType::Modifier::Signed)}, + {std::make_unique(232, IntegerType::Modifier::Signed)}, + {std::make_unique(233, IntegerType::Modifier::Signed)}, + {std::make_unique(234, IntegerType::Modifier::Signed)}, + {std::make_unique(235, IntegerType::Modifier::Signed)}, + {std::make_unique(236, IntegerType::Modifier::Signed)}, + {std::make_unique(237, IntegerType::Modifier::Signed)}, + {std::make_unique(238, IntegerType::Modifier::Signed)}, + {std::make_unique(239, IntegerType::Modifier::Signed)}, + {std::make_unique(240, IntegerType::Modifier::Signed)}, + {std::make_unique(241, IntegerType::Modifier::Signed)}, + {std::make_unique(242, IntegerType::Modifier::Signed)}, + {std::make_unique(243, IntegerType::Modifier::Signed)}, + {std::make_unique(244, IntegerType::Modifier::Signed)}, + {std::make_unique(245, IntegerType::Modifier::Signed)}, + {std::make_unique(246, IntegerType::Modifier::Signed)}, + {std::make_unique(247, IntegerType::Modifier::Signed)}, + {std::make_unique(248, IntegerType::Modifier::Signed)}, + {std::make_unique(249, IntegerType::Modifier::Signed)}, + {std::make_unique(250, IntegerType::Modifier::Signed)}, + {std::make_unique(251, IntegerType::Modifier::Signed)}, + {std::make_unique(252, IntegerType::Modifier::Signed)}, + {std::make_unique(253, IntegerType::Modifier::Signed)}, + {std::make_unique(254, IntegerType::Modifier::Signed)}, + {std::make_unique(255, IntegerType::Modifier::Signed)}, + {std::make_unique(256, IntegerType::Modifier::Signed)}, + {std::make_unique(257, IntegerType::Modifier::Signed)}, +}}; + +std::array, 256> const TypeProvider::m_quintM{{ + {std::make_unique(1, IntegerType::Modifier::Unsigned)}, + {std::make_unique(2, IntegerType::Modifier::Unsigned)}, + {std::make_unique(3, IntegerType::Modifier::Unsigned)}, + {std::make_unique(4, IntegerType::Modifier::Unsigned)}, + {std::make_unique(5, IntegerType::Modifier::Unsigned)}, + {std::make_unique(6, IntegerType::Modifier::Unsigned)}, + {std::make_unique(7, IntegerType::Modifier::Unsigned)}, + {std::make_unique(8, IntegerType::Modifier::Unsigned)}, + {std::make_unique(9, IntegerType::Modifier::Unsigned)}, + {std::make_unique(10, IntegerType::Modifier::Unsigned)}, + {std::make_unique(11, IntegerType::Modifier::Unsigned)}, + {std::make_unique(12, IntegerType::Modifier::Unsigned)}, + {std::make_unique(13, IntegerType::Modifier::Unsigned)}, + {std::make_unique(14, IntegerType::Modifier::Unsigned)}, + {std::make_unique(15, IntegerType::Modifier::Unsigned)}, + {std::make_unique(16, IntegerType::Modifier::Unsigned)}, + {std::make_unique(17, IntegerType::Modifier::Unsigned)}, + {std::make_unique(18, IntegerType::Modifier::Unsigned)}, + {std::make_unique(19, IntegerType::Modifier::Unsigned)}, + {std::make_unique(20, IntegerType::Modifier::Unsigned)}, + {std::make_unique(21, IntegerType::Modifier::Unsigned)}, + {std::make_unique(22, IntegerType::Modifier::Unsigned)}, + {std::make_unique(23, IntegerType::Modifier::Unsigned)}, + {std::make_unique(24, IntegerType::Modifier::Unsigned)}, + {std::make_unique(25, IntegerType::Modifier::Unsigned)}, + {std::make_unique(26, IntegerType::Modifier::Unsigned)}, + {std::make_unique(27, IntegerType::Modifier::Unsigned)}, + {std::make_unique(28, IntegerType::Modifier::Unsigned)}, + {std::make_unique(29, IntegerType::Modifier::Unsigned)}, + {std::make_unique(30, IntegerType::Modifier::Unsigned)}, + {std::make_unique(31, IntegerType::Modifier::Unsigned)}, + {std::make_unique(32, IntegerType::Modifier::Unsigned)}, + {std::make_unique(33, IntegerType::Modifier::Unsigned)}, + {std::make_unique(34, IntegerType::Modifier::Unsigned)}, + {std::make_unique(35, IntegerType::Modifier::Unsigned)}, + {std::make_unique(36, IntegerType::Modifier::Unsigned)}, + {std::make_unique(37, IntegerType::Modifier::Unsigned)}, + {std::make_unique(38, IntegerType::Modifier::Unsigned)}, + {std::make_unique(39, IntegerType::Modifier::Unsigned)}, + {std::make_unique(40, IntegerType::Modifier::Unsigned)}, + {std::make_unique(41, IntegerType::Modifier::Unsigned)}, + {std::make_unique(42, IntegerType::Modifier::Unsigned)}, + {std::make_unique(43, IntegerType::Modifier::Unsigned)}, + {std::make_unique(44, IntegerType::Modifier::Unsigned)}, + {std::make_unique(45, IntegerType::Modifier::Unsigned)}, + {std::make_unique(46, IntegerType::Modifier::Unsigned)}, + {std::make_unique(47, IntegerType::Modifier::Unsigned)}, + {std::make_unique(48, IntegerType::Modifier::Unsigned)}, + {std::make_unique(49, IntegerType::Modifier::Unsigned)}, + {std::make_unique(50, IntegerType::Modifier::Unsigned)}, + {std::make_unique(51, IntegerType::Modifier::Unsigned)}, + {std::make_unique(52, IntegerType::Modifier::Unsigned)}, + {std::make_unique(53, IntegerType::Modifier::Unsigned)}, + {std::make_unique(54, IntegerType::Modifier::Unsigned)}, + {std::make_unique(55, IntegerType::Modifier::Unsigned)}, + {std::make_unique(56, IntegerType::Modifier::Unsigned)}, + {std::make_unique(57, IntegerType::Modifier::Unsigned)}, + {std::make_unique(58, IntegerType::Modifier::Unsigned)}, + {std::make_unique(59, IntegerType::Modifier::Unsigned)}, + {std::make_unique(60, IntegerType::Modifier::Unsigned)}, + {std::make_unique(61, IntegerType::Modifier::Unsigned)}, + {std::make_unique(62, IntegerType::Modifier::Unsigned)}, + {std::make_unique(63, IntegerType::Modifier::Unsigned)}, + {std::make_unique(64, IntegerType::Modifier::Unsigned)}, + {std::make_unique(65, IntegerType::Modifier::Unsigned)}, + {std::make_unique(66, IntegerType::Modifier::Unsigned)}, + {std::make_unique(67, IntegerType::Modifier::Unsigned)}, + {std::make_unique(68, IntegerType::Modifier::Unsigned)}, + {std::make_unique(69, IntegerType::Modifier::Unsigned)}, + {std::make_unique(70, IntegerType::Modifier::Unsigned)}, + {std::make_unique(71, IntegerType::Modifier::Unsigned)}, + {std::make_unique(72, IntegerType::Modifier::Unsigned)}, + {std::make_unique(73, IntegerType::Modifier::Unsigned)}, + {std::make_unique(74, IntegerType::Modifier::Unsigned)}, + {std::make_unique(75, IntegerType::Modifier::Unsigned)}, + {std::make_unique(76, IntegerType::Modifier::Unsigned)}, + {std::make_unique(77, IntegerType::Modifier::Unsigned)}, + {std::make_unique(78, IntegerType::Modifier::Unsigned)}, + {std::make_unique(79, IntegerType::Modifier::Unsigned)}, + {std::make_unique(80, IntegerType::Modifier::Unsigned)}, + {std::make_unique(81, IntegerType::Modifier::Unsigned)}, + {std::make_unique(82, IntegerType::Modifier::Unsigned)}, + {std::make_unique(83, IntegerType::Modifier::Unsigned)}, + {std::make_unique(84, IntegerType::Modifier::Unsigned)}, + {std::make_unique(85, IntegerType::Modifier::Unsigned)}, + {std::make_unique(86, IntegerType::Modifier::Unsigned)}, + {std::make_unique(87, IntegerType::Modifier::Unsigned)}, + {std::make_unique(88, IntegerType::Modifier::Unsigned)}, + {std::make_unique(89, IntegerType::Modifier::Unsigned)}, + {std::make_unique(90, IntegerType::Modifier::Unsigned)}, + {std::make_unique(91, IntegerType::Modifier::Unsigned)}, + {std::make_unique(92, IntegerType::Modifier::Unsigned)}, + {std::make_unique(93, IntegerType::Modifier::Unsigned)}, + {std::make_unique(94, IntegerType::Modifier::Unsigned)}, + {std::make_unique(95, IntegerType::Modifier::Unsigned)}, + {std::make_unique(96, IntegerType::Modifier::Unsigned)}, + {std::make_unique(97, IntegerType::Modifier::Unsigned)}, + {std::make_unique(98, IntegerType::Modifier::Unsigned)}, + {std::make_unique(99, IntegerType::Modifier::Unsigned)}, + {std::make_unique(100, IntegerType::Modifier::Unsigned)}, + {std::make_unique(101, IntegerType::Modifier::Unsigned)}, + {std::make_unique(102, IntegerType::Modifier::Unsigned)}, + {std::make_unique(103, IntegerType::Modifier::Unsigned)}, + {std::make_unique(104, IntegerType::Modifier::Unsigned)}, + {std::make_unique(105, IntegerType::Modifier::Unsigned)}, + {std::make_unique(106, IntegerType::Modifier::Unsigned)}, + {std::make_unique(107, IntegerType::Modifier::Unsigned)}, + {std::make_unique(108, IntegerType::Modifier::Unsigned)}, + {std::make_unique(109, IntegerType::Modifier::Unsigned)}, + {std::make_unique(110, IntegerType::Modifier::Unsigned)}, + {std::make_unique(111, IntegerType::Modifier::Unsigned)}, + {std::make_unique(112, IntegerType::Modifier::Unsigned)}, + {std::make_unique(113, IntegerType::Modifier::Unsigned)}, + {std::make_unique(114, IntegerType::Modifier::Unsigned)}, + {std::make_unique(115, IntegerType::Modifier::Unsigned)}, + {std::make_unique(116, IntegerType::Modifier::Unsigned)}, + {std::make_unique(117, IntegerType::Modifier::Unsigned)}, + {std::make_unique(118, IntegerType::Modifier::Unsigned)}, + {std::make_unique(119, IntegerType::Modifier::Unsigned)}, + {std::make_unique(120, IntegerType::Modifier::Unsigned)}, + {std::make_unique(121, IntegerType::Modifier::Unsigned)}, + {std::make_unique(122, IntegerType::Modifier::Unsigned)}, + {std::make_unique(123, IntegerType::Modifier::Unsigned)}, + {std::make_unique(124, IntegerType::Modifier::Unsigned)}, + {std::make_unique(125, IntegerType::Modifier::Unsigned)}, + {std::make_unique(126, IntegerType::Modifier::Unsigned)}, + {std::make_unique(127, IntegerType::Modifier::Unsigned)}, + {std::make_unique(128, IntegerType::Modifier::Unsigned)}, + {std::make_unique(129, IntegerType::Modifier::Unsigned)}, + {std::make_unique(130, IntegerType::Modifier::Unsigned)}, + {std::make_unique(131, IntegerType::Modifier::Unsigned)}, + {std::make_unique(132, IntegerType::Modifier::Unsigned)}, + {std::make_unique(133, IntegerType::Modifier::Unsigned)}, + {std::make_unique(134, IntegerType::Modifier::Unsigned)}, + {std::make_unique(135, IntegerType::Modifier::Unsigned)}, + {std::make_unique(136, IntegerType::Modifier::Unsigned)}, + {std::make_unique(137, IntegerType::Modifier::Unsigned)}, + {std::make_unique(138, IntegerType::Modifier::Unsigned)}, + {std::make_unique(139, IntegerType::Modifier::Unsigned)}, + {std::make_unique(140, IntegerType::Modifier::Unsigned)}, + {std::make_unique(141, IntegerType::Modifier::Unsigned)}, + {std::make_unique(142, IntegerType::Modifier::Unsigned)}, + {std::make_unique(143, IntegerType::Modifier::Unsigned)}, + {std::make_unique(144, IntegerType::Modifier::Unsigned)}, + {std::make_unique(145, IntegerType::Modifier::Unsigned)}, + {std::make_unique(146, IntegerType::Modifier::Unsigned)}, + {std::make_unique(147, IntegerType::Modifier::Unsigned)}, + {std::make_unique(148, IntegerType::Modifier::Unsigned)}, + {std::make_unique(149, IntegerType::Modifier::Unsigned)}, + {std::make_unique(150, IntegerType::Modifier::Unsigned)}, + {std::make_unique(151, IntegerType::Modifier::Unsigned)}, + {std::make_unique(152, IntegerType::Modifier::Unsigned)}, + {std::make_unique(153, IntegerType::Modifier::Unsigned)}, + {std::make_unique(154, IntegerType::Modifier::Unsigned)}, + {std::make_unique(155, IntegerType::Modifier::Unsigned)}, + {std::make_unique(156, IntegerType::Modifier::Unsigned)}, + {std::make_unique(157, IntegerType::Modifier::Unsigned)}, + {std::make_unique(158, IntegerType::Modifier::Unsigned)}, + {std::make_unique(159, IntegerType::Modifier::Unsigned)}, + {std::make_unique(160, IntegerType::Modifier::Unsigned)}, + {std::make_unique(161, IntegerType::Modifier::Unsigned)}, + {std::make_unique(162, IntegerType::Modifier::Unsigned)}, + {std::make_unique(163, IntegerType::Modifier::Unsigned)}, + {std::make_unique(164, IntegerType::Modifier::Unsigned)}, + {std::make_unique(165, IntegerType::Modifier::Unsigned)}, + {std::make_unique(166, IntegerType::Modifier::Unsigned)}, + {std::make_unique(167, IntegerType::Modifier::Unsigned)}, + {std::make_unique(168, IntegerType::Modifier::Unsigned)}, + {std::make_unique(169, IntegerType::Modifier::Unsigned)}, + {std::make_unique(170, IntegerType::Modifier::Unsigned)}, + {std::make_unique(171, IntegerType::Modifier::Unsigned)}, + {std::make_unique(172, IntegerType::Modifier::Unsigned)}, + {std::make_unique(173, IntegerType::Modifier::Unsigned)}, + {std::make_unique(174, IntegerType::Modifier::Unsigned)}, + {std::make_unique(175, IntegerType::Modifier::Unsigned)}, + {std::make_unique(176, IntegerType::Modifier::Unsigned)}, + {std::make_unique(177, IntegerType::Modifier::Unsigned)}, + {std::make_unique(178, IntegerType::Modifier::Unsigned)}, + {std::make_unique(179, IntegerType::Modifier::Unsigned)}, + {std::make_unique(180, IntegerType::Modifier::Unsigned)}, + {std::make_unique(181, IntegerType::Modifier::Unsigned)}, + {std::make_unique(182, IntegerType::Modifier::Unsigned)}, + {std::make_unique(183, IntegerType::Modifier::Unsigned)}, + {std::make_unique(184, IntegerType::Modifier::Unsigned)}, + {std::make_unique(185, IntegerType::Modifier::Unsigned)}, + {std::make_unique(186, IntegerType::Modifier::Unsigned)}, + {std::make_unique(187, IntegerType::Modifier::Unsigned)}, + {std::make_unique(188, IntegerType::Modifier::Unsigned)}, + {std::make_unique(189, IntegerType::Modifier::Unsigned)}, + {std::make_unique(190, IntegerType::Modifier::Unsigned)}, + {std::make_unique(191, IntegerType::Modifier::Unsigned)}, + {std::make_unique(192, IntegerType::Modifier::Unsigned)}, + {std::make_unique(193, IntegerType::Modifier::Unsigned)}, + {std::make_unique(194, IntegerType::Modifier::Unsigned)}, + {std::make_unique(195, IntegerType::Modifier::Unsigned)}, + {std::make_unique(196, IntegerType::Modifier::Unsigned)}, + {std::make_unique(197, IntegerType::Modifier::Unsigned)}, + {std::make_unique(198, IntegerType::Modifier::Unsigned)}, + {std::make_unique(199, IntegerType::Modifier::Unsigned)}, + {std::make_unique(200, IntegerType::Modifier::Unsigned)}, + {std::make_unique(201, IntegerType::Modifier::Unsigned)}, + {std::make_unique(202, IntegerType::Modifier::Unsigned)}, + {std::make_unique(203, IntegerType::Modifier::Unsigned)}, + {std::make_unique(204, IntegerType::Modifier::Unsigned)}, + {std::make_unique(205, IntegerType::Modifier::Unsigned)}, + {std::make_unique(206, IntegerType::Modifier::Unsigned)}, + {std::make_unique(207, IntegerType::Modifier::Unsigned)}, + {std::make_unique(208, IntegerType::Modifier::Unsigned)}, + {std::make_unique(209, IntegerType::Modifier::Unsigned)}, + {std::make_unique(210, IntegerType::Modifier::Unsigned)}, + {std::make_unique(211, IntegerType::Modifier::Unsigned)}, + {std::make_unique(212, IntegerType::Modifier::Unsigned)}, + {std::make_unique(213, IntegerType::Modifier::Unsigned)}, + {std::make_unique(214, IntegerType::Modifier::Unsigned)}, + {std::make_unique(215, IntegerType::Modifier::Unsigned)}, + {std::make_unique(216, IntegerType::Modifier::Unsigned)}, + {std::make_unique(217, IntegerType::Modifier::Unsigned)}, + {std::make_unique(218, IntegerType::Modifier::Unsigned)}, + {std::make_unique(219, IntegerType::Modifier::Unsigned)}, + {std::make_unique(220, IntegerType::Modifier::Unsigned)}, + {std::make_unique(221, IntegerType::Modifier::Unsigned)}, + {std::make_unique(222, IntegerType::Modifier::Unsigned)}, + {std::make_unique(223, IntegerType::Modifier::Unsigned)}, + {std::make_unique(224, IntegerType::Modifier::Unsigned)}, + {std::make_unique(225, IntegerType::Modifier::Unsigned)}, + {std::make_unique(226, IntegerType::Modifier::Unsigned)}, + {std::make_unique(227, IntegerType::Modifier::Unsigned)}, + {std::make_unique(228, IntegerType::Modifier::Unsigned)}, + {std::make_unique(229, IntegerType::Modifier::Unsigned)}, + {std::make_unique(230, IntegerType::Modifier::Unsigned)}, + {std::make_unique(231, IntegerType::Modifier::Unsigned)}, + {std::make_unique(232, IntegerType::Modifier::Unsigned)}, + {std::make_unique(233, IntegerType::Modifier::Unsigned)}, + {std::make_unique(234, IntegerType::Modifier::Unsigned)}, + {std::make_unique(235, IntegerType::Modifier::Unsigned)}, + {std::make_unique(236, IntegerType::Modifier::Unsigned)}, + {std::make_unique(237, IntegerType::Modifier::Unsigned)}, + {std::make_unique(238, IntegerType::Modifier::Unsigned)}, + {std::make_unique(239, IntegerType::Modifier::Unsigned)}, + {std::make_unique(240, IntegerType::Modifier::Unsigned)}, + {std::make_unique(241, IntegerType::Modifier::Unsigned)}, + {std::make_unique(242, IntegerType::Modifier::Unsigned)}, + {std::make_unique(243, IntegerType::Modifier::Unsigned)}, + {std::make_unique(244, IntegerType::Modifier::Unsigned)}, + {std::make_unique(245, IntegerType::Modifier::Unsigned)}, + {std::make_unique(246, IntegerType::Modifier::Unsigned)}, + {std::make_unique(247, IntegerType::Modifier::Unsigned)}, + {std::make_unique(248, IntegerType::Modifier::Unsigned)}, + {std::make_unique(249, IntegerType::Modifier::Unsigned)}, + {std::make_unique(250, IntegerType::Modifier::Unsigned)}, + {std::make_unique(251, IntegerType::Modifier::Unsigned)}, + {std::make_unique(252, IntegerType::Modifier::Unsigned)}, + {std::make_unique(253, IntegerType::Modifier::Unsigned)}, + {std::make_unique(254, IntegerType::Modifier::Unsigned)}, + {std::make_unique(255, IntegerType::Modifier::Unsigned)}, + {std::make_unique(256, IntegerType::Modifier::Unsigned)}, +}}; + +std::unique_ptr const TypeProvider::m_qbool = std::make_unique(); + std::array, 32> const TypeProvider::m_bytesM{{ {std::make_unique(1)}, {std::make_unique(2)}, @@ -659,7 +1183,7 @@ void TypeProvider::reset() instance().m_stringLiteralTypes.clear(); instance().m_ufixedMxN.clear(); instance().m_fixedMxN.clear(); - instance().m_varInterger.clear(); + instance().m_varinterger.clear(); } template @@ -682,19 +1206,27 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& switch (_type.token()) { case Token::VarUint: - return varInteger(32, IntegerType::Modifier::Unsigned); + case Token::Varuint: + return varinteger(32, IntegerType::Modifier::Unsigned); case Token::VarUintM: - return varInteger(m, IntegerType::Modifier::Unsigned); + case Token::VaruintM: + return varinteger(m, IntegerType::Modifier::Unsigned); case Token::coins: return coins(); case Token::VarInt: - return varInteger(32, IntegerType::Modifier::Signed); + case Token::Varint: + return varinteger(32, IntegerType::Modifier::Signed); case Token::VarIntM: - return varInteger(m, IntegerType::Modifier::Signed); + case Token::VarintM: + return varinteger(m, IntegerType::Modifier::Signed); case Token::IntM: return integer(m, IntegerType::Modifier::Signed); case Token::UIntM: return integer(m, IntegerType::Modifier::Unsigned); + case Token::QIntM: + return qInteger(m, IntegerType::Modifier::Signed); + case Token::QUIntM: + return qInteger(m, IntegerType::Modifier::Unsigned); case Token::Byte: return byte(); case Token::BytesM: @@ -704,9 +1236,15 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& case Token::UFixedMxN: return fixedPoint(m, n, FixedPointType::Modifier::Unsigned); case Token::Int: - return integer(256, IntegerType::Modifier::Signed); + return integer(257, IntegerType::Modifier::Signed); case Token::UInt: return integer(256, IntegerType::Modifier::Unsigned); + case Token::QInt: + return qInteger(257, IntegerType::Modifier::Signed); + case Token::QUInt: + return qInteger(256, IntegerType::Modifier::Unsigned); + case Token::QBool: + return qBool(); case Token::Fixed: return fixedPoint(128, 18, FixedPointType::Modifier::Signed); case Token::UFixed: @@ -721,6 +1259,8 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& return tvmslice(); case Token::TvmBuilder: return tvmbuilder(); + case Token::StringBuilder: + return stringBuilder(); case Token::Bytes: return bytesStorage(); case Token::String: @@ -821,6 +1361,8 @@ Type const* TypeProvider::forLiteral(Literal const& _literal) return nullType(); case Token::EmptyMap: return emptyMapType(); + case Token::TVMNaN: + return qIntegerNAN(); case Token::Number: return rationalNumber(_literal); case Token::StringLiteral: @@ -860,8 +1402,8 @@ StringLiteralType const* TypeProvider::stringLiteral(std::string const& literal) return instance().m_stringLiteralTypes.emplace(literal, std::make_unique(literal)).first->second.get(); } -VarIntegerType const* TypeProvider::varInteger(unsigned m, IntegerType::Modifier _modifier) { - auto& map = instance().m_varInterger; +VarIntegerType const* TypeProvider::varinteger(unsigned m, IntegerType::Modifier _modifier) { + auto& map = instance().m_varinterger; auto i = map.find(std::make_pair(m, _modifier)); if (i != map.end()) return i->second.get(); @@ -874,7 +1416,7 @@ VarIntegerType const* TypeProvider::varInteger(unsigned m, IntegerType::Modifier } VarIntegerType const* TypeProvider::coins() { - return TypeProvider::varInteger(16, IntegerType::Modifier::Unsigned); + return TypeProvider::varinteger(16, IntegerType::Modifier::Unsigned); } FixedPointType const* TypeProvider::fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier) @@ -1049,7 +1591,7 @@ MappingType const* TypeProvider::mapping(Type const* _keyType, ASTString _keyNam MappingType const *TypeProvider::extraCurrencyCollection() { auto keyType = TypeProvider::uint(32); - auto valueType = TypeProvider::varInteger(32, IntegerType::Modifier::Unsigned); + auto valueType = TypeProvider::varinteger(32, IntegerType::Modifier::Unsigned); return createAndGet(keyType, "", valueType, ""); } @@ -1058,11 +1600,16 @@ OptionalType const* TypeProvider::optional(Type const* _type) return createAndGet(_type); } -TvmVectorType const* TypeProvider::tvmtuple(Type const* _type) +TvmVectorType const* TypeProvider::tvmVector(Type const* _type) { return createAndGet(_type); } +TvmStackType const* TypeProvider::tvmStack(Type const* _type) +{ + return createAndGet(_type); +} + UserDefinedValueType const* TypeProvider::userDefinedValueType(UserDefinedValueTypeDefinition const& _definition) { return createAndGet(_definition); diff --git a/compiler/libsolidity/ast/TypeProvider.h b/compiler/libsolidity/ast/TypeProvider.h index 20e6bf62..d6f7b57a 100644 --- a/compiler/libsolidity/ast/TypeProvider.h +++ b/compiler/libsolidity/ast/TypeProvider.h @@ -67,6 +67,7 @@ class TypeProvider static TvmCellType const* tvmcell() noexcept { return &m_tvmcell; } static TvmSliceType const* tvmslice() noexcept { return &m_tvmslice; } static TvmBuilderType const* tvmbuilder() noexcept { return &m_tvmbuilder; } + static StringBuilderType const* stringBuilder() noexcept { return &m_stringBuilder; } static FixedBytesType const* byte() { return fixedBytes(1); } static FixedBytesType const* fixedBytes(unsigned m) { return m_bytesM.at(m - 1).get(); } static ArrayType const* bytesStorage(); @@ -99,14 +100,32 @@ class TypeProvider return m_intM.at(_bits - 1).get(); } + static NanType const* qIntegerNAN() + { + return m_qintNAN.get(); + } + + static QIntegerType const* qInteger(unsigned _bits, IntegerType::Modifier _modifier) + { + if (_modifier == IntegerType::Modifier::Unsigned) + return m_quintM.at(_bits - 1).get(); + else + return m_qintM.at(_bits - 1).get(); + } + + static QBoolType const* qBool() + { + return m_qbool.get(); + } + static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); } static IntegerType const* int_(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Signed); } static IntegerType const* uint256() { return uint(256); } - static IntegerType const* int256() { return integer(256, IntegerType::Modifier::Signed); } + static IntegerType const* int257() { return integer(257, IntegerType::Modifier::Signed); } static VarIntegerType const* coins(); - static VarIntegerType const* varInteger(unsigned m, IntegerType::Modifier _modifier); + static VarIntegerType const* varinteger(unsigned m, IntegerType::Modifier _modifier); static FixedPointType const* fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier); @@ -188,7 +207,9 @@ class TypeProvider static OptionalType const* optional(Type const* _type); - static TvmVectorType const* tvmtuple(Type const* _type); + static TvmVectorType const* tvmVector(Type const* _type); + + static TvmStackType const* tvmStack(Type const* _type); static UserDefinedValueType const* userDefinedValueType(UserDefinedValueTypeDefinition const& _definition); @@ -209,6 +230,7 @@ class TypeProvider static TvmCellType const m_tvmcell; static TvmSliceType const m_tvmslice; static TvmBuilderType const m_tvmbuilder; + static StringBuilderType const m_stringBuilder; static InaccessibleDynamicType const m_inaccessibleDynamic; @@ -226,10 +248,14 @@ class TypeProvider static CallListType const m_callList; static std::array, 257> const m_intM; static std::array, 256> const m_uintM; + static std::unique_ptr const m_qintNAN; + static std::array, 257> const m_qintM; + static std::array, 256> const m_quintM; + static std::unique_ptr const m_qbool; static std::array, 32> const m_bytesM; static std::array, 8> const m_magics; ///< MagicType's except MetaType - std::map, std::unique_ptr> m_varInterger{}; + std::map, std::unique_ptr> m_varinterger{}; std::map, std::unique_ptr> m_ufixedMxN{}; std::map, std::unique_ptr> m_fixedMxN{}; std::map> m_stringLiteralTypes{}; diff --git a/compiler/libsolidity/ast/Types.cpp b/compiler/libsolidity/ast/Types.cpp index dbc43bd3..af13e9a5 100644 --- a/compiler/libsolidity/ast/Types.cpp +++ b/compiler/libsolidity/ast/Types.cpp @@ -302,7 +302,37 @@ Type const* Type::commonType(Type const* _a, Type const* _b) { if (!_a || !_b) return nullptr; - else if (_a->mobileType() && _b->isImplicitlyConvertibleTo(*_a->mobileType())) + + if (_a->category() == Type::Category::QInteger || + _b->category() == Type::Category::QInteger + ) { + if (_b->category() == Type::Category::QInteger) + std::swap(_a, _b); + auto a = dynamic_cast(_a); + auto comType = commonType(a->asIntegerType(), _b); + if (comType == nullptr) + return comType; + if (comType->category() == Type::Category::Integer) { + auto comIntType = dynamic_cast(comType); + return TypeProvider::qInteger( + comIntType->numBits(), + comIntType->isSigned() ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned + ); + } + solAssert(comType->category() == Type::Category::QInteger, ""); + return comType; + } + + if (_b->category() == Type::Category::Null) + std::swap(_a, _b); + if (_a->category() == Type::Category::Null) { + if (_b->category() == Type::Category::Null || _b->category() == Type::Category::Optional) + return _b; + else if (_b->mobileType()) + return TypeProvider::optional(_b->mobileType()); + } + + if (_a->mobileType() && _b->isImplicitlyConvertibleTo(*_a->mobileType())) return _a->mobileType(); else if (_b->mobileType() && _a->isImplicitlyConvertibleTo(*_b->mobileType())) return _b->mobileType(); @@ -481,9 +511,8 @@ std::string AddressType::richIdentifier() const BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } return _convertTo.category() == category(); } @@ -605,6 +634,23 @@ bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType) return false; } +// Same as `isValidShiftAndAmountType` but we check whether shiftAmount <= 1023 bits +bool isValidShiftAndAmountTypeForQuite(Token _operator, Type const& _shiftAmountType) +{ + // Disable >>> here. + if (_operator == Token::SHR) + return false; + else if (IntegerType const* otherInt = dynamic_cast(&_shiftAmountType)) + return !otherInt->isSigned() && otherInt->numBits() <= 10; + else if (QIntegerType const* otherQInt = dynamic_cast(&_shiftAmountType)) + return !otherQInt->asIntegerType()->isSigned() && otherQInt->asIntegerType()->numBits() <= 10; + else if (RationalNumberType const* otherRat = dynamic_cast(&_shiftAmountType)) + return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned() && + otherRat->value2() <= 1023; + else + return false; +} + } IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): @@ -635,6 +681,11 @@ BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const IntegerType const& convertTo = dynamic_cast(_convertTo).asIntegerType(); return maxValue() <= convertTo.maxValue() && minValue() >= convertTo.minValue(); } + else if (_convertTo.category() == Category::QInteger) + { + IntegerType const& convertTo = *dynamic_cast(_convertTo).asIntegerType(); + return maxValue() <= convertTo.maxValue() && minValue() >= convertTo.minValue(); + } else if (_convertTo.category() == category()) { IntegerType const& convertTo = dynamic_cast(_convertTo); @@ -758,6 +809,7 @@ TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other _other->category() != Category::RationalNumber && _other->category() != Category::FixedPoint && _other->category() != Category::VarInteger && + _other->category() != Category::QInteger && _other->category() != category() ) return nullptr; @@ -819,9 +871,8 @@ std::string FixedPointType::richIdentifier() const BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (_convertTo.category() == category()) { @@ -1090,9 +1141,8 @@ std::tuple RationalNumberType::isValidLiteral(Literal const& _li BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } switch (_convertTo.category()) { @@ -1110,6 +1160,13 @@ BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) IntegerType const& targetType = dynamic_cast(_convertTo); return fitsIntegerType(m_value.numerator(), targetType); } + case Category::QInteger: + { + if (isFractional()) + return false; + QIntegerType const& targetType = dynamic_cast(_convertTo); + return fitsIntegerType(m_value.numerator(), *targetType.asIntegerType()); + } case Category::FixedPoint: { FixedPointType const& targetType = dynamic_cast(_convertTo); @@ -1178,7 +1235,7 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* { if (!isValidShiftAndAmountType(_operator, *_other)) return nullptr; - return isNegative() ? TypeProvider::int256() : TypeProvider::uint256(); + return isNegative() ? TypeProvider::int257() : TypeProvider::uint256(); } else if (Token::Exp == _operator) { @@ -1190,7 +1247,7 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* else if (dynamic_cast(_other)) return TypeResult::err("Exponent is fractional."); - return isNegative() ? TypeProvider::int256() : TypeProvider::uint256(); + return isNegative() ? TypeProvider::int257() : TypeProvider::uint256(); } else { @@ -1387,9 +1444,8 @@ StringLiteralType::StringLiteralType(std::string _value): BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (auto fixedBytes = dynamic_cast(&_convertTo)) { @@ -1471,9 +1527,8 @@ FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes) BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (_convertTo.category() != category()) return false; @@ -1563,6 +1618,16 @@ u256 BoolType::literalValue(Literal const* _literal) const solAssert(false, "Bool type constructed from non-boolean literal."); } +BoolResult BoolType::isImplicitlyConvertibleTo(Type const& _convertTo) const { + if (Type::isImplicitlyConvertibleTo(_convertTo)) + return true; + + if (_convertTo.category() == Type::Category::QBool) + return true; + + return false; +} + TypeResult BoolType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) @@ -1575,10 +1640,13 @@ TypeResult BoolType::unaryOperatorResult(Token _operator) const TypeResult BoolType::binaryOperatorResult(Token _operator, Type const* _other) const { - if (category() != _other->category()) + bool hasQBool = _other->category() == Type::Category::QBool; + if (category() != _other->category() && !hasQBool) return nullptr; if (_operator == Token::Equal || _operator == Token::NotEqual || _operator == Token::And || _operator == Token::Or) - return _other; + return hasQBool ? + dynamic_cast(TypeProvider::qBool()) : + dynamic_cast(TypeProvider::boolean()); else return nullptr; } @@ -1593,9 +1661,8 @@ Type const* ContractType::encodingType() const BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (m_super) return false; @@ -1697,9 +1764,8 @@ void ArrayType::clearCache() const BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (_convertTo.category() != category()) return false; @@ -2402,9 +2468,8 @@ Type const* StructType::encodingType() const BoolResult StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (_convertTo.category() != category()) return false; @@ -3194,7 +3259,6 @@ std::string FunctionType::richIdentifier() const case Kind::LogTVM: id += "logtvm"; break; case Kind::TVMAccept: id += "tvmaccept"; break; - case Kind::ABIBuildExtMsg: id += "abibuildextmsg"; break; case Kind::ABIBuildIntMsg: id += "abibuildintmsg"; break; case Kind::ABICodeSalt: id += "abicodesalt"; break; case Kind::ABIDecodeFunctionParams: id += "abidecodefunctionparams"; break; @@ -3212,10 +3276,23 @@ std::string FunctionType::richIdentifier() const case Kind::TVMBuilderStoreTons: id += "tvmbuilderstoretons"; break; case Kind::TVMBuilderStoreUint: id += "tvmbuilderstoreuint"; break; - case Kind::TVMTuplePush: id += "tvmtuplepush"; break; - case Kind::TVMTuplePop: id += "tvmtuplepop"; break; - case Kind::TVMTupleLength: id += "tvmtuplelength"; break; - case Kind::TVMTupleEmpty: id += "tvmtupleempty"; break; + case Kind::StringBuilderToString: id += "stringbuildertostring"; break; + case Kind::StringBuilderAppendByte: id += "stringbuilderappendbyte"; break; + case Kind::StringBuilderAppendByteNTimes: id += "stringbuilderappendbytentimes"; break; + case Kind::StringBuilderAppendString: id += "stringbuilderappendstring"; break; + + case Kind::TVMVectorEmpty: id += "tvmvectorempty"; break; + case Kind::TVMVectorLast: id += "tvmvectorlast"; break; + case Kind::TVMVectorLength: id += "tvmvectorlength"; break; + case Kind::TVMVectorPop: id += "tvmvectorpop"; break; + case Kind::TVMVectorPush: id += "tvmvectorpush"; break; + + case Kind::TVMStackEmpty: id += "tvmstackempty"; break; + case Kind::TVMStackPop: id += "tvmstackpop"; break; + case Kind::TVMStackPush: id += "tvmstackpush"; break; + case Kind::TVMStackReverse: id += "tvmstackreverse"; break; + case Kind::TVMStackSort: id += "tvmstacksort"; break; + case Kind::TVMStackTop: id += "tvmstacktop"; break; case Kind::TVMBuyGas: id += "tvmbuygas"; break; case Kind::TVMChecksign: id += "tvmchecksign"; break; @@ -3243,6 +3320,12 @@ std::string FunctionType::richIdentifier() const case Kind::TXtimestamp: id += "txtimestamp"; break; + case Kind::QIsNaN: id += "qisnan"; break; + case Kind::QGet: id += "qget"; break; + case Kind::QGetOr: id += "qgetor"; break; + case Kind::QGetOrDefault: id += "qgetordefault"; break; + case Kind::QToOptional: id += "qtooptional"; break; + case Kind::VariantIsUint: id += "variantisuint"; break; case Kind::VariantToUint: id += "varianttouint"; break; @@ -3391,9 +3474,8 @@ BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (Type::isImplicitlyConvertibleTo(_convertTo)) { + if (Type::isImplicitlyConvertibleTo(_convertTo)) return true; - } if (_convertTo.category() != category()) return false; @@ -4040,9 +4122,8 @@ Type const* MappingType::realKeyType() const BoolResult MappingType::isImplicitlyConvertibleTo(Type const& _other) const { - if (Type::isImplicitlyConvertibleTo(_other)) { + if (Type::isImplicitlyConvertibleTo(_other)) return true; - } if (_other.category() != category()) return false; @@ -4167,6 +4248,22 @@ std::vector> MappingType::makeStackItems() return {std::make_tuple("slot", TypeProvider::uint256())}; } +BoolResult NanType::isImplicitlyConvertibleTo(Type const& _other) const +{ + return _other.category() == Type::Category::QInteger || + _other.category() == Type::Category::QBool; +} + +std::string NanType::richIdentifier() const +{ + return "NaN"; +} + +std::string NanType::toString(bool) const +{ + return "NaN"; +} + std::string TypeType::richIdentifier() const { return "t_type" + identifierList(actualType()); @@ -4617,38 +4714,6 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const FunctionType::Kind::TVMRawConfigParam, StateMutability::Pure )}, - {"buildExtMsg", TypeProvider::function( - TypePointers{TypeProvider::address(), - TypeProvider::callList(), - TypeProvider::uint(32), - TypeProvider::uint(8), - TypeProvider::uint(32), - TypeProvider::optional(TypeProvider::uint(32)), - TypeProvider::uint(64), - TypeProvider::uint(32), - TypeProvider::optional(TypeProvider::uint256()), - TypeProvider::boolean(), - TypeProvider::tvmcell(), - TypeProvider::uint(8)}, - TypePointers{TypeProvider::tvmcell()}, - strings{std::string("dest"), // mandatory - std::string("call"), // mandatory - std::string("callbackId"), // mandatory - std::string("abiVer"), // can be omitted - std::string("onErrorId"), // mandatory - std::string("signBoxHandle"), // can be omitted - std::string("time"), // can be omitted - std::string("expire"), // can be omitted - std::string("pubkey"), // can be omitted - std::string("sign"), // can be omitted - std::string("stateInit"), // can be omitted - std::string("flags")}, // can be omitted - strings{std::string()}, - FunctionType::Kind::ABIBuildExtMsg, - StateMutability::Pure, - nullptr, - FunctionType::Options::withArbitraryParameters() - )}, {"buildIntMsg", TypeProvider::function( { TypeProvider::address(), @@ -4774,91 +4839,92 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const { "divr", TypeProvider::function( - {}, {}, {}, {}, FunctionType::Kind::MathDivR, StateMutability::Pure + {}, {}, {}, {}, FunctionType::Kind::MathDivR, StateMutability::Pure ) } }; members.emplace_back("max", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathMax, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathMax, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("min", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathMin, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathMin, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("minmax", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathMinMax, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathMinMax, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); for(const std::string code : {"muldiv", "muldivr", "muldivc"}) { members.emplace_back(code.c_str(), TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathMulDiv, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - } - members.emplace_back("muldivmod", TypeProvider::function( TypePointers{}, TypePointers{}, strings{}, strings{}, - FunctionType::Kind::MathMulDivMod, + FunctionType::Kind::MathMulDiv, StateMutability::Pure, nullptr, FunctionType::Options::withArbitraryParameters() + )); + } + members.emplace_back("muldivmod", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathMulDivMod, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("divmod", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathDivMod, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathDivMod, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("abs", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathAbs, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathAbs, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("modpow2", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::MathModpow2, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathModpow2, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); members.emplace_back("sign", TypeProvider::function( - TypePointers{TypeProvider::integer(257, IntegerType::Modifier::Signed)}, - TypePointers{TypeProvider::integer(2, IntegerType::Modifier::Signed)}, - strings{std::string("value")}, - strings{std::string("sign")}, - FunctionType::Kind::MathSign, - StateMutability::Pure + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::MathSign, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() )); return members; } @@ -5014,38 +5080,6 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const StateMutability::Pure, nullptr, FunctionType::Options::withArbitraryParameters() )}, - {"encodeExtMsg", TypeProvider::function( - TypePointers{TypeProvider::address(), - TypeProvider::callList(), - TypeProvider::uint(32), - TypeProvider::uint(8), - TypeProvider::uint(32), - TypeProvider::optional(TypeProvider::uint(32)), - TypeProvider::uint(64), - TypeProvider::uint(32), - TypeProvider::optional(TypeProvider::uint256()), - TypeProvider::boolean(), - TypeProvider::tvmcell(), - TypeProvider::uint(8)}, - TypePointers{TypeProvider::tvmcell()}, - strings{std::string("dest"), // mandatory - std::string("call"), // mandatory - std::string("callbackId"), // mandatory - std::string("abiVer"), // can be omitted - std::string("onErrorId"), // mandatory - std::string("signBoxHandle"), // can be omitted - std::string("time"), // can be omitted - std::string("expire"), // can be omitted - std::string("pubkey"), // can be omitted - std::string("sign"), // can be omitted - std::string("stateInit"), // can be omitted - std::string("flags")}, // can be omitted - strings{std::string()}, - FunctionType::Kind::ABIBuildExtMsg, - StateMutability::Pure, - nullptr, - FunctionType::Options::withArbitraryParameters() - )}, {"encodeIntMsg", TypeProvider::function( { TypeProvider::address(), @@ -5223,10 +5257,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const } else if (m_typeArgument->category() == Type::Category::VarInteger) { - VarIntegerType const* varIntTypePointer = dynamic_cast(m_typeArgument); + VarIntegerType const* varintTypePointer = dynamic_cast(m_typeArgument); return MemberList::MemberMap({ - {"min", varIntTypePointer}, - {"max", varIntTypePointer}, + {"min", varintTypePointer}, + {"max", varintTypePointer}, }); } else if (m_typeArgument->category() == Type::Category::Enum) @@ -5785,7 +5819,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { { "loadSigned", TypeProvider::function( TypePointers{TypeProvider::uint(9)}, - TypePointers{TypeProvider::int256()}, + TypePointers{TypeProvider::int257()}, strings{std::string()}, strings{std::string()}, FunctionType::Kind::TVMSliceLoadInt, @@ -5795,7 +5829,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { { "loadInt", TypeProvider::function( TypePointers{TypeProvider::uint(9)}, - TypePointers{TypeProvider::int256()}, + TypePointers{TypeProvider::int257()}, strings{std::string()}, strings{std::string()}, FunctionType::Kind::TVMSliceLoadInt, @@ -5805,7 +5839,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { { "loadIntQ", TypeProvider::function( TypePointers{TypeProvider::uint(9)}, - TypePointers{TypeProvider::optional(TypeProvider::int256())}, + TypePointers{TypeProvider::optional(TypeProvider::int257())}, strings{std::string()}, strings{std::string()}, FunctionType::Kind::TVMSliceLoadIntQ, @@ -5815,7 +5849,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { { "preloadInt", TypeProvider::function( TypePointers{TypeProvider::uint(9)}, - TypePointers{TypeProvider::int256()}, + TypePointers{TypeProvider::int257()}, strings{std::string()}, strings{std::string()}, FunctionType::Kind::TVMSlicePreLoadInt, @@ -5825,7 +5859,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { { "preloadIntQ", TypeProvider::function( TypePointers{TypeProvider::uint(9)}, - TypePointers{TypeProvider::optional(TypeProvider::int256())}, + TypePointers{TypeProvider::optional(TypeProvider::int257())}, strings{std::string()}, strings{std::string()}, FunctionType::Kind::TVMSlicePreLoadIntQ, @@ -6191,44 +6225,64 @@ TypeResult TvmVectorType::unaryOperatorResult(Token _operator) const { MemberList::MemberMap TvmVectorType::nativeMembers(const ASTNode *) const { - MemberList::MemberMap members; - - members.emplace_back("push", TypeProvider::function( - TypePointers{valueType()}, - TypePointers{}, - strings{std::string()}, - strings{}, - FunctionType::Kind::TVMTuplePush, - StateMutability::Pure - )); - - members.emplace_back("length", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(8)}, - strings{}, - strings{std::string("length")}, - FunctionType::Kind::TVMTupleLength, - StateMutability::Pure - )); - - members.emplace_back("pop", TypeProvider::function( - TypePointers{}, - TypePointers{valueType()}, - strings{}, - strings{std::string("last")}, - FunctionType::Kind::TVMTuplePop, - StateMutability::Pure - )); - - members.emplace_back("empty", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::boolean()}, - strings{}, - strings{std::string("is_empty")}, - FunctionType::Kind::TVMTupleLength, - StateMutability::Pure - )); - + MemberList::MemberMap members = + { + { + "push", + TypeProvider::function( + TypePointers{valueType()}, + TypePointers{}, + strings{std::string()}, + strings{}, + FunctionType::Kind::TVMVectorPush, + StateMutability::Pure + ) + }, + { + "length", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(8)}, + strings{}, + strings{std::string("length")}, + FunctionType::Kind::TVMVectorLength, + StateMutability::Pure + ) + }, + { + "pop", + TypeProvider::function( + TypePointers{}, + TypePointers{valueType()}, + strings{}, + strings{std::string("last")}, + FunctionType::Kind::TVMVectorPop, + StateMutability::Pure + ) + }, + { + "empty", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::boolean()}, + strings{}, + strings{std::string("is_empty")}, + FunctionType::Kind::TVMVectorEmpty, + StateMutability::Pure + ) + }, + { + "last", + TypeProvider::function( + TypePointers{}, + TypePointers{valueType()}, + strings{}, + strings{std::string("last")}, + FunctionType::Kind::TVMVectorLast, + StateMutability::Pure + ) + } + }; return members; } @@ -6240,6 +6294,94 @@ std::string TvmVectorType::richIdentifier() const { return "t_vector_" + valueType()->richIdentifier(); } +TypeResult TvmStackType::unaryOperatorResult(Token _operator) const { + if (_operator == Token::Delete) + return TypeProvider::emptyTuple(); + return nullptr; +} + +MemberList::MemberMap TvmStackType::nativeMembers(const ASTNode *) const +{ + MemberList::MemberMap members = + { + { + "push", + TypeProvider::function( + TypePointers{valueType()}, + TypePointers{}, + strings{std::string()}, + strings{}, + FunctionType::Kind::TVMStackPush, + StateMutability::Pure + ) + }, + { + "pop", + TypeProvider::function( + TypePointers{}, + TypePointers{valueType()}, + strings{}, + strings{std::string("last")}, + FunctionType::Kind::TVMStackPop, + StateMutability::Pure + ) + }, + { + "empty", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::boolean()}, + strings{}, + strings{std::string("is_empty")}, + FunctionType::Kind::TVMStackEmpty, + StateMutability::Pure + ) + }, + { + "top", + TypeProvider::function( + TypePointers{}, + TypePointers{valueType()}, + strings{}, + strings{std::string("last")}, + FunctionType::Kind::TVMStackTop, + StateMutability::Pure + ) + }, + { + "sort", + TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMStackSort, + StateMutability::Pure + ) + }, + { + "reverse", + TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMStackReverse, + StateMutability::Pure + ) + } + }; + return members; +} + +std::string TvmStackType::toString(bool _short) const { + return "stack(" + valueType()->toString(_short) + ")"; +} + +std::string TvmStackType::richIdentifier() const { + return "t_stack_" + valueType()->richIdentifier(); +} + TypeResult TvmBuilderType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) return TypeProvider::emptyTuple(); @@ -6429,7 +6571,7 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const }, { "storeSigned", TypeProvider::function( - TypePointers{TypeProvider::int256(), TypeProvider::uint(9)}, + TypePointers{TypeProvider::int257(), TypeProvider::uint(9)}, TypePointers{}, strings{std::string(), std::string()}, strings{}, @@ -6439,7 +6581,7 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const }, { "storeInt", TypeProvider::function( - TypePointers{TypeProvider::int256(), TypeProvider::uint(9)}, + TypePointers{TypeProvider::int257(), TypeProvider::uint(9)}, TypePointers{}, strings{std::string(), std::string()}, strings{}, @@ -6542,6 +6684,58 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const return members; } +TypeResult StringBuilderType::unaryOperatorResult(Token _operator) const { + if (_operator == Token::Delete) + return TypeProvider::emptyTuple(); + return nullptr; +} + +MemberList::MemberMap StringBuilderType::nativeMembers(const ASTNode *) const { + MemberList::MemberMap members = { + { + "toString", TypeProvider::function( + {}, + {TypeProvider::stringMemory()}, + {}, + {{}}, + FunctionType::Kind::StringBuilderToString, + StateMutability::Pure + ) + }, + { + "append", TypeProvider::function( + {TypeProvider::fixedBytes(1)}, + {}, + {{}}, + {}, + FunctionType::Kind::StringBuilderAppendByte, + StateMutability::Pure + ) + }, + { + "append", TypeProvider::function( + {TypeProvider::fixedBytes(1), TypeProvider::uint(31)}, + {}, + {{}, {}}, + {}, + FunctionType::Kind::StringBuilderAppendByteNTimes, + StateMutability::Pure + ) + }, + { + "append", TypeProvider::function( + {TypeProvider::stringMemory()}, + {}, + {{}}, + {}, + FunctionType::Kind::StringBuilderAppendString, + StateMutability::Pure + ) + }, + }; + return members; +} + BoolResult VarIntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { return m_int.isImplicitlyConvertibleTo(_convertTo); } @@ -6551,9 +6745,16 @@ BoolResult VarIntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) con } TypeResult VarIntegerType::unaryOperatorResult(Token _operator) const { + // "delete" is ok for all integer types if (_operator == Token::Delete) - return TypeProvider::emptyTuple(); - return nullptr; + return TypeResult{TypeProvider::emptyTuple()}; + // unary negation only on signed types + else if (_operator == Token::Sub) + return m_int.isSigned() ? TypeResult{this} : TypeResult::err("Unary negation is only allowed for signed integers."); + else if (_operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) + return TypeResult{this}; + else + return TypeResult::err(""); } TypeResult VarIntegerType::binaryOperatorResult(Token _operator, Type const* _other) const { @@ -6567,7 +6768,7 @@ TypeResult VarIntegerType::binaryOperatorResult(Token _operator, Type const* _ot } std::string VarIntegerType::toString(bool) const { - return std::string{} + "var" + (m_int.isSigned()? "Int" : "Uint") + std::to_string(m_n); + return std::string{"var"} + (m_int.isSigned()? "" : "u") + "int" + std::to_string(m_n); } int VarIntegerType::maxBitSizeInCell() const { @@ -6579,3 +6780,188 @@ int VarIntegerType::maxBitSizeInCell() const { } solUnimplemented(""); } + +BoolResult QIntegerType::isImplicitlyConvertibleTo(Type const& _other) const { + if (Type::isImplicitlyConvertibleTo(_other)) + return true; + + if (category() == _other.category()) { + auto const& other = dynamic_cast(_other); + if (m_int->isImplicitlyConvertibleTo(*other.m_int)) + return true; + } + + return false; +} + +bool QIntegerType::operator==(Type const& _other) const { + if (_other.category() != category()) + return false; + auto const& other = dynamic_cast(_other); + return m_int == other.m_int; +} + +TypeResult QIntegerType::unaryOperatorResult(Token _operator) const { + // "delete" is ok for all integer types + if (_operator == Token::Delete) + return TypeResult{TypeProvider::emptyTuple()}; + // unary negation only on signed types + else if (_operator == Token::Sub) + return m_int->isSigned() ? TypeResult{this} : TypeResult::err("Unary negation is only allowed for signed integers."); + else if (_operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) + return TypeResult{this}; + else + return TypeResult::err(""); +} + +TypeResult QIntegerType::binaryOperatorResult(Token _operator, Type const* _other) const { + // See TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other) const + if ( + _other->category() != Category::RationalNumber && + _other->category() != Category::FixedPoint && + _other->category() != Category::VarInteger && + _other->category() != Category::Integer && + _other->category() != category() + ) + return nullptr; + if (TokenTraits::isShiftOp(_operator)) + { + // Shifts are not symmetric with respect to the type + if (isValidShiftAndAmountTypeForQuite(_operator, *_other)) + return this; + else + return nullptr; + } + else if (Token::Exp == _operator) + { + if (auto otherIntType = dynamic_cast(_other)) + { + if (otherIntType->isSigned()) + return TypeResult::err("Exponentiation power is not allowed to be a signed integer type."); + } + else if (dynamic_cast(_other)) + return nullptr; + else if (auto rationalNumberType = dynamic_cast(_other)) + { + if (rationalNumberType->isFractional()) + return TypeResult::err("Exponent is fractional."); + if (!rationalNumberType->integerType()) + return TypeResult::err("Exponent too large."); + if (rationalNumberType->isNegative()) + return TypeResult::err("Exponentiation power is not allowed to be a negative integer literal."); + } + return this; + } + + auto commonType = Type::commonType(this, _other); //might be an integer or fixed point + if (!commonType) + return nullptr; + + // All integer types can be compared + if (TokenTraits::isCompareOp(_operator)) + return commonType; + if (TokenTraits::isBooleanOp(_operator)) + return nullptr; + return commonType; +} + +namespace { +MemberList::MemberMap qTypeNativeMembers(Type const* underType) { + MemberList::MemberMap members = { + { + "isNaN", TypeProvider::function( + {}, + {TypeProvider::boolean()}, + {}, + {{}}, + FunctionType::Kind::QIsNaN, + StateMutability::Pure + ) + }, + { + "get", TypeProvider::function( + {}, + {underType}, + {}, + {{}}, + FunctionType::Kind::QGet, + StateMutability::Pure + ) + }, + { + "getOr", TypeProvider::function( + {underType}, + {underType}, + {{}}, + {{}}, + FunctionType::Kind::QGetOr, + StateMutability::Pure + ) + }, + { + "getOrDefault", TypeProvider::function( + {}, + {underType}, + {}, + {{}}, + FunctionType::Kind::QGetOrDefault, + StateMutability::Pure + ) + }, + { + "toOptional", TypeProvider::function( + {}, + {TypeProvider::optional(underType)}, + {}, + {{}}, + FunctionType::Kind::QToOptional, + StateMutability::Pure + ) + } + }; + return members; +} +} + +MemberList::MemberMap QIntegerType::nativeMembers(ASTNode const*) const { + return qTypeNativeMembers(m_int.get()); +} + +std::string QIntegerType::toString(bool f) const { + if (m_int == nullptr) + return "NaN"; + return "q" + m_int->toString(f); +} + +BoolResult QBoolType::isImplicitlyConvertibleTo(Type const& _convertTo) const { + if (Type::isImplicitlyConvertibleTo(_convertTo)) + return true; + + return false; +} + +TypeResult QBoolType::unaryOperatorResult(Token _operator) const { + if (_operator == Token::Delete) + return TypeProvider::emptyTuple(); + else if (_operator == Token::Not) + return this; + else + return nullptr; +} + +TypeResult QBoolType::binaryOperatorResult(Token _operator, Type const* _other) const { + if (category() != _other->category() && _other->category() != Type::Category::Bool) + return nullptr; + if (_operator == Token::Equal || _operator == Token::NotEqual || _operator == Token::And || _operator == Token::Or) + return TypeProvider::qBool(); + else + return nullptr; +} + +MemberList::MemberMap QBoolType::nativeMembers(ASTNode const*) const { + return qTypeNativeMembers(TypeProvider::boolean()); +} + +std::string QBoolType::toString(bool) const { + return "qbool"; +} diff --git a/compiler/libsolidity/ast/Types.h b/compiler/libsolidity/ast/Types.h index 679fd1ea..d7f51192 100644 --- a/compiler/libsolidity/ast/Types.h +++ b/compiler/libsolidity/ast/Types.h @@ -193,12 +193,15 @@ class Type Magic, Module, InaccessibleDynamic, - TvmCell, TvmSlice, TvmBuilder, TvmVector, Variant, + TvmCell, TvmSlice, TvmBuilder, StringBuilder, TvmVector, TvmStack, Variant, VarInteger, + QInteger, + QBool, InitializerList, CallList, // <-- variables of that types can't be declared in solidity contract Optional, Null, - EmpyMap + EmptyMap, + TVMNaN }; /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise @@ -709,6 +712,7 @@ class BoolType: public Type { public: Category category() const override { return Category::Bool; } + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; std::string richIdentifier() const override { return "t_bool"; } TypeResult unaryOperatorResult(Token _operator) const override; TypeResult binaryOperatorResult(Token _operator, Type const* _other) const override; @@ -787,6 +791,32 @@ class TvmVectorType: public Type Type const* m_type; }; +/** + * The TVM Stack type. + */ +class TvmStackType: public Type +{ +public: + TvmStackType(Type const* _type): + m_type(_type) {} + + Category category() const override { return Category::TvmStack; } + bool isValueType() const override { return true; } + std::string richIdentifier() const override; + TypeResult unaryOperatorResult(Token _operator) const override; + std::string toString(bool) const override; + + Type const* encodingType() const override { return this; } + TypeResult interfaceType(bool) const override { return this; } + + MemberList::MemberMap nativeMembers(ASTNode const*) const override; + + Type const* valueType() const { return m_type; } + +private: + Type const* m_type; +}; + /** * The TVM Slice type. */ @@ -823,6 +853,25 @@ class TvmBuilderType: public Type MemberList::MemberMap nativeMembers(ASTNode const*) const override; }; +/** + * The StringBuilder type. + */ +class StringBuilderType: public Type +{ +public: + Category category() const override { return Category::StringBuilder; } + bool isValueType() const override { return true; } + std::string richIdentifier() const override { return "t_stringbuilder"; } + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token /*_operator*/, Type const* /*_other*/) const override { return nullptr; } + std::string toString(bool) const override { return "StringBuilder"; } + + Type const* encodingType() const override { return this; } + TypeResult interfaceType(bool) const override { return this; } + + MemberList::MemberMap nativeMembers(ASTNode const*) const override; +}; + /** * The VarInteger type. */ @@ -849,6 +898,47 @@ class VarIntegerType: public Type IntegerType m_int; }; +class QIntegerType: public Type +{ +public: + explicit QIntegerType(uint32_t _n, IntegerType::Modifier _modifier) : + m_int{std::make_unique(_n, _modifier)} {} + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; +// BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + Category category() const override { return Category::QInteger; } + bool isValueType() const override { return true; } + bool operator==(Type const& _other) const override; + std::string richIdentifier() const override { return "t_qinteger"; } + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, Type const* _other) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; + std::string toString(bool) const override; + + Type const* encodingType() const override { return this; } + TypeResult interfaceType(bool) const override { return this; } + IntegerType const* asIntegerType() const { solAssert(m_int, ""); return m_int.get(); } +private: + std::unique_ptr m_int; +}; + +class QBoolType: public Type +{ +public: + explicit QBoolType() = default; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; +// BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + Category category() const override { return Category::QBool; } + bool isValueType() const override { return true; } + std::string richIdentifier() const override { return "t_qbool"; } + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, Type const* _other) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; + std::string toString(bool) const override; + + Type const* encodingType() const override { return this; } + TypeResult interfaceType(bool) const override { return this; } +}; + /** * Used for initialization list in deploy via new * new MyContract{..., varInit: {m_a: 11, m_b: 22}, ...}(...) @@ -868,7 +958,7 @@ class InitializerListType: public Type /** * Used for call list in external message creation - * abi.encodeExtMsg({...,call : {Contract.Func, arg1, arg2} ,...}) + * abi.encodeIntMsg({...,call : {Contract.Func, arg1, arg2} ,...}) */ class CallListType: public Type { @@ -1336,6 +1426,12 @@ class FunctionType: public Type IntCast, ///< int a; a.cast(uint8); + QGet, + QGetOr, + QGetOrDefault, + QIsNaN, + QToOptional, + VariantIsUint, VariantToUint, @@ -1379,10 +1475,23 @@ class FunctionType: public Type TVMBuilderStoreTons, ///< builder.storeTons() TVMBuilderStoreUint, ///< builder.storeUint() - TVMTuplePush, ///< tuple.push(...) - TVMTuplePop, ///< tuple.pop() - TVMTupleLength, ///< tuple.length() - TVMTupleEmpty, ///< tuple.empty() + StringBuilderToString, ///< stringBuilder.toString() + StringBuilderAppendByte, /// < stringBuilder.append(bytes1) + StringBuilderAppendByteNTimes, /// < stringBuilder.append(bytes1, uint31) + StringBuilderAppendString, /// < stringBuilder.append(string) + + TVMVectorEmpty, ///< vector.empty() + TVMVectorLast, ///< vector.last() + TVMVectorLength, ///< vector.length() + TVMVectorPop, ///< vector.pop() + TVMVectorPush, ///< vector.push(...) + + TVMStackEmpty, ///< stack.empty() + TVMStackPop, ///< stack.pop() + TVMStackPush, ///< stack.push(...) + TVMStackReverse, ///< stack.reverse() + TVMStackSort, ///< stack.sort() + TVMStackTop, ///< stack.top() ExtraCurrencyCollectionMethods, ///< extraCurrencyCollection.*() KECCAK256, ///< KECCAK256 @@ -1427,7 +1536,6 @@ class FunctionType: public Type MathDivMod, ///< math.divmod() MathSign, ///< math.sign() - ABIBuildExtMsg, ///< abi.encodeExtMsg() ABIBuildIntMsg, ///< abi.encodeIntMsg() ABICodeSalt, ///< abi.codeSalt() ABIDecodeData, ///< abi.decodeData(contract_name, slice) @@ -1867,7 +1975,7 @@ class NullType: public Type class EmptyMapType: public Type { public: - Category category() const override { return Category::EmpyMap; } + Category category() const override { return Category::EmptyMap; } BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; @@ -1878,6 +1986,20 @@ class EmptyMapType: public Type bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } }; +class NanType: public Type +{ +public: + Category category() const override { return Category::TVMNaN; } + BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; + std::string richIdentifier() const override; + //bool operator==(Type const& _other) const override; + std::string toString(bool _short) const override; + //std::string canonicalName() const override; + TypeResult interfaceType(bool) const override { return this; } + TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } +}; + /** * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example * of a TypeType. diff --git a/compiler/libsolidity/codegen/DictOperations.cpp b/compiler/libsolidity/codegen/DictOperations.cpp index 4bb4fde4..0903a25b 100644 --- a/compiler/libsolidity/codegen/DictOperations.cpp +++ b/compiler/libsolidity/codegen/DictOperations.cpp @@ -11,10 +11,10 @@ * See the GNU General Public License for more details at: https://www.gnu.org/licenses/gpl-3.0.html */ -#include "DictOperations.hpp" -#include "TVMPusher.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMConstants.hpp" +#include +#include +#include +#include using namespace solidity; using namespace solidity::frontend; @@ -293,5 +293,5 @@ void DelMinOrMax::delMinOrMax() { // mapLValue... D optPair const int cntOfValuesOnStack = pusher.stackSize() - stackSize; pusher.blockSwap(cntOfValuesOnStack - 1, 1); // optPair mapLValue... map - ec->collectLValue(lValueInfo, true, false); // value key + ec->collectLValue(lValueInfo, true); // value key } diff --git a/compiler/libsolidity/codegen/DictOperations.hpp b/compiler/libsolidity/codegen/DictOperations.hpp index e0568225..9a0ce45f 100644 --- a/compiler/libsolidity/codegen/DictOperations.hpp +++ b/compiler/libsolidity/codegen/DictOperations.hpp @@ -19,7 +19,7 @@ #include #include -#include "TVMCommons.hpp" +#include namespace solidity::frontend { diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp index 764ce853..13f32083 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp @@ -18,12 +18,12 @@ #include -#include "PeepholeOptimizer.hpp" -#include "StackOpcodeSquasher.hpp" -#include "TVM.hpp" -#include "TVMConstants.hpp" -#include "TVMPusher.hpp" -#include "TvmAst.hpp" +#include +#include +#include +#include +#include +#include using namespace std; using namespace solidity::util; @@ -911,12 +911,18 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer ) { return Result{2, gen(cmd2GenOpcode->opcode() + " " + arg(cmd1))}; } + // PUSHINT 2**N + // DIV / MUL + // => + // RSHIFT N / LSHIFT N if (is(cmd1, "PUSHINT") && (is(cmd2, "DIV") || is(cmd2, "MUL"))) { bigint val = pushintValue(cmd1); if (power2Exp().count(val)) { - const std::string& newOp = is(cmd2, "DIV") ? "RSHIFT" : "LSHIFT"; - return Result{2, gen(newOp + " " + toString(power2Exp().at(val)))}; + std::string const& newOp = is(cmd2, "DIV") ? "RSHIFT" : "LSHIFT"; + int const n = power2Exp().at(val); + if (n > 0) + return Result{2, gen(newOp + " " + toString(n))}; } } // PUSHINT 2**N diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.hpp b/compiler/libsolidity/codegen/PeepholeOptimizer.hpp index 46e1facc..80cdc382 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.hpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.hpp @@ -16,7 +16,7 @@ #pragma once -#include "TvmAstVisitor.hpp" +#include namespace solidity::frontend { class PeepholeOptimizer : public TvmAstVisitor { diff --git a/compiler/libsolidity/codegen/SizeOptimizer.cpp b/compiler/libsolidity/codegen/SizeOptimizer.cpp index 9a416f64..868fc7e1 100644 --- a/compiler/libsolidity/codegen/SizeOptimizer.cpp +++ b/compiler/libsolidity/codegen/SizeOptimizer.cpp @@ -17,7 +17,7 @@ #include #include -#include "SizeOptimizer.hpp" +#include using namespace std; using namespace solidity::util; diff --git a/compiler/libsolidity/codegen/StackOpcodeSquasher.cpp b/compiler/libsolidity/codegen/StackOpcodeSquasher.cpp index ecd60e68..9cee32ba 100644 --- a/compiler/libsolidity/codegen/StackOpcodeSquasher.cpp +++ b/compiler/libsolidity/codegen/StackOpcodeSquasher.cpp @@ -14,7 +14,7 @@ #include #include -#include "StackOpcodeSquasher.hpp" +#include using namespace solidity::frontend; using namespace std; diff --git a/compiler/libsolidity/codegen/StackOpcodeSquasher.hpp b/compiler/libsolidity/codegen/StackOpcodeSquasher.hpp index d6d530d9..617eadf6 100644 --- a/compiler/libsolidity/codegen/StackOpcodeSquasher.hpp +++ b/compiler/libsolidity/codegen/StackOpcodeSquasher.hpp @@ -13,7 +13,7 @@ #pragma once -#include "TvmAst.hpp" +#include #include #include @@ -23,7 +23,7 @@ constexpr static int MAX_NEW_OPCODES = 3; class StackState { public: - constexpr static int MAX_STACK_DEPTH = 9; + constexpr static int MAX_STACK_DEPTH = 8; explicit StackState(int _size); explicit StackState(int8_t _size, std::array _values) : m_size{_size}, m_values{_values} { diff --git a/compiler/libsolidity/codegen/StackOptimizer.cpp b/compiler/libsolidity/codegen/StackOptimizer.cpp index 756a7bfe..1ae1cf9d 100644 --- a/compiler/libsolidity/codegen/StackOptimizer.cpp +++ b/compiler/libsolidity/codegen/StackOptimizer.cpp @@ -16,11 +16,11 @@ #include -#include "TvmAst.hpp" -#include "TVMCommons.hpp" -#include "TVMConstants.hpp" -#include "StackOptimizer.hpp" -#include "TVMSimulator.hpp" +#include +#include +#include +#include +#include namespace solidity::frontend { @@ -270,27 +270,25 @@ bool StackOptimizer::visit(While &_node) { bool StackOptimizer::visit(Function &f) { switch (f.type()) { - case Function::FunctionType::PrivateFunctionWithObj: - case Function::FunctionType::Fragment: - case Function::FunctionType::OnCodeUpgrade: - case Function::FunctionType::OnTickTock: { - if (f.name() != "c7_to_c4_for_await") { - for (int iter = 0; iter < TvmConst::IterStackOptQty; ++iter) { - m_didSome = false; - m_stackSize.clear(); - initStack(f.take()); - f.block()->accept(*this); - if (!m_didSome) - break; - } - } - break; + case Function::FunctionType::PrivateFunctionWithObj: + case Function::FunctionType::Fragment: + case Function::FunctionType::OnCodeUpgrade: + case Function::FunctionType::OnTickTock: { + for (int iter = 0; iter < TvmConst::IterStackOptQty; ++iter) { + m_didSome = false; + m_stackSize.clear(); + initStack(f.take()); + f.block()->accept(*this); + if (!m_didSome) + break; } + break; + } - case Function::FunctionType::PublicStateVariableGetter: - case Function::FunctionType::MainInternal: - case Function::FunctionType::MainExternal: - break; + case Function::FunctionType::PublicStateVariableGetter: + case Function::FunctionType::MainInternal: + case Function::FunctionType::MainExternal: + break; } return false; } diff --git a/compiler/libsolidity/codegen/TVM.cpp b/compiler/libsolidity/codegen/TVM.cpp index d87475d4..455f52a3 100644 --- a/compiler/libsolidity/codegen/TVM.cpp +++ b/compiler/libsolidity/codegen/TVM.cpp @@ -15,8 +15,8 @@ */ -#include "TVM.hpp" -#include "TVMContractCompiler.hpp" +#include +#include using namespace std; using namespace solidity::frontend; diff --git a/compiler/libsolidity/codegen/TVMABI.cpp b/compiler/libsolidity/codegen/TVMABI.cpp index f05467b0..c3aa67ad 100644 --- a/compiler/libsolidity/codegen/TVMABI.cpp +++ b/compiler/libsolidity/codegen/TVMABI.cpp @@ -17,11 +17,11 @@ #include #include -#include "TVMABI.hpp" -#include "TVMPusher.hpp" -#include "TVM.hpp" -#include "TVMConstants.hpp" -#include "TVMContractCompiler.hpp" +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace std; @@ -154,12 +154,6 @@ Json::Value TVMABI::generateABIJson( } } - for (FunctionDefinition const* fd : ctx.usage().awaitFunctions()) { - std::string name = "_await_" + fd->annotation().contract->name() + "_" + fd->name(); - functions.append(toJson(name, convertArray(fd->returnParameters()), {})); - } - - root["functions"] = functions; } @@ -191,9 +185,6 @@ Json::Value TVMABI::generateABIJson( offset.emplace_back("_timestamp", "uint64"); } offset.emplace_back("_constructorFlag", "bool"); - if (ctx.usage().hasAwaitCall()) { - offset.emplace_back("_await", "optional(cell)"); - } for (const auto& [name, type] : offset) { Json::Value field(Json::objectValue); @@ -448,9 +439,8 @@ Json::Value TVMABI::setupNameTypeComponents(const string &name, const Type *type if (category == Type::Category::Address || category == Type::Category::Contract) { typeName = "address"; } else if (category == Type::Category::VarInteger) { - auto varInt = to(type); - typeName = varInt->toString(false); - boost::algorithm::to_lower(typeName); + auto varint = to(type); + typeName = varint->toString(false); } else if (auto* fixedBytesType = to(type)) { typeName = "fixedbytes" + toString(fixedBytesType->numBytes()); } else if (ti.isNumeric) { diff --git a/compiler/libsolidity/codegen/TVMABI.hpp b/compiler/libsolidity/codegen/TVMABI.hpp index 55ff0d93..3e6324de 100644 --- a/compiler/libsolidity/codegen/TVMABI.hpp +++ b/compiler/libsolidity/codegen/TVMABI.hpp @@ -18,7 +18,7 @@ #include -#include "TVMCommons.hpp" +#include namespace solidity::frontend { diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.cpp b/compiler/libsolidity/codegen/TVMAnalyzer.cpp index f2f8854f..57da3686 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.cpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.cpp @@ -14,7 +14,7 @@ * AST analyzer specified to search for TVM specific issues. */ -#include "TVMAnalyzer.hpp" +#include #include #include @@ -181,23 +181,14 @@ bool ContactsUsageScanner::visit(FunctionCall const& _functionCall) { } } switch (funType->kind()) { - case FunctionType::Kind::MsgPubkey: - m_hasMsgPubkey = true; - break; - default: - break; + case FunctionType::Kind::MsgPubkey: + m_hasMsgPubkey = true; + break; + default: + break; } } - if (_functionCall.isAwait()) { - m_hasAwaitCall = true; - - Type const* expressionType = getType(&_functionCall.expression()); - auto functionType = to(expressionType); - auto fd = to(&functionType->declaration()); - solAssert(fd, ""); - m_awaitFunctions.insert(fd); - } return true; } diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.hpp b/compiler/libsolidity/codegen/TVMAnalyzer.hpp index cb80ee93..f16695b2 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.hpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.hpp @@ -66,16 +66,12 @@ class ContactsUsageScanner: public ASTConstVisitor bool hasMsgPubkey() const { return m_hasMsgPubkey; } bool hasMsgSender() const { return m_hasMsgSender; } bool hasResponsibleFunction() const { return m_hasResponsibleFunction; } - bool hasAwaitCall() const { return m_hasAwaitCall; } - std::set const& awaitFunctions() const { return m_awaitFunctions; } private: bool m_hasMsgPubkey{}; bool m_hasMsgSender{}; bool m_hasResponsibleFunction{}; - bool m_hasAwaitCall{}; std::set m_usedFunctions; - std::set m_awaitFunctions; }; template diff --git a/compiler/libsolidity/codegen/TVMCommons.cpp b/compiler/libsolidity/codegen/TVMCommons.cpp index eb9217ca..d5f882b7 100644 --- a/compiler/libsolidity/codegen/TVMCommons.cpp +++ b/compiler/libsolidity/codegen/TVMCommons.cpp @@ -18,9 +18,9 @@ #include -#include "TVM.hpp" -#include "TVMCommons.hpp" -#include "TVMConstants.hpp" +#include +#include +#include using namespace std; using namespace solidity::langutil; @@ -268,19 +268,6 @@ bool isAddressThis(const FunctionCall *funCall) { return false; } -vector getContractFunctions(ContractDefinition const *contract, const string &funcName) { - vector result; - for (auto &[functionDefinition, contractDefinition] : getContractFunctionPairs(contract)) { - (void)contractDefinition; // suppress unused variable error - if (functionDefinition->isConstructor()) { - continue; - } - if (functionName(functionDefinition) == funcName) - result.push_back(functionDefinition); - } - return result; -} - CallableDeclaration const* getFunctionDeclarationOrConstructor(Expression const* expr, bool quiet) { auto f = to(expr->annotation().type); if (f) { @@ -400,8 +387,8 @@ void ABITypeSize::init(Type const* type) { solAssert(ti.isNumeric, ""); maxBits = ti.numBits; maxRefs = 0; - } else if (auto varInt = to(type)) { - maxBits = varInt->maxBitSizeInCell(); + } else if (auto varint = to(type)) { + maxBits = varint->maxBitSizeInCell(); maxRefs = 0; } else if (auto arrayType = to(type)) { if (arrayType->isByteArrayOrString()) { @@ -654,6 +641,7 @@ std::string StrUtils::stringToHex(const std::string& str) { std::optional ExprUtils::constValue(const Expression &_e) { + // TODO see ConstantEvaluator ? if (*_e.annotation().isPure) { if (auto memberAccess = to(&_e)) { if (auto variable = dynamic_cast(memberAccess->annotation().referencedDeclaration)) { @@ -736,4 +724,55 @@ std::map const& MathConsts::power10() { return power10; } +namespace { +std::pair getSignAndBits(Type const* type) { + bool isSigned = false; + int numBits = 0; + if (auto intResult = to(type)) { + isSigned = intResult->isSigned(); + numBits = intResult->numBits(); + } else if (auto qintResult = to(type)) { + isSigned = qintResult->asIntegerType()->isSigned(); + numBits = qintResult->asIntegerType()->numBits(); + } else if (auto varintResult = to(type)) { + isSigned = varintResult->asIntegerType().isSigned(); + numBits = varintResult->asIntegerType().numBits(); + } else if (auto fixedResult = to(type)) { + isSigned = fixedResult->isSigned(); + numBits = fixedResult->numBits(); + } else + solUnimplemented(type->toString()); + return {isSigned, numBits}; +} +} + +bool isFitUselessUnary(Type const* common, Token op) { + auto const [isSigned, numBits] = getSignAndBits(common); + return + ( + !isSigned && + numBits == 256 && + op == Token::Inc + ); +} + +bool isFitUseless(Type const* left, Type const* right, Type const* common, Token op) { + bool const isLeftSigned = getSignAndBits(left->mobileType()).first; + bool const isRightSigned = getSignAndBits(right->mobileType()).first; + auto const [isSigned, numBits] = getSignAndBits(common); + return + ( + !isSigned && + numBits == 256 && + isIn(op, Token::Add, Token::Exp, Token::Mul, Token::SHL) + ) + || + ( + // we should throw an overflow exception if case of type(SignType).min / -1, + // e.g. -128/-1 does not fit into int8 + (!isLeftSigned || !isRightSigned) && + op == Token::Div + ); +} + } // end namespace solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMCommons.hpp b/compiler/libsolidity/codegen/TVMCommons.hpp index 4c26e9b3..41362baa 100644 --- a/compiler/libsolidity/codegen/TVMCommons.hpp +++ b/compiler/libsolidity/codegen/TVMCommons.hpp @@ -31,7 +31,7 @@ #include #include -#include "TvmAst.hpp" +#include namespace solidity::frontend { @@ -99,30 +99,39 @@ struct TypeInfo { bool isSigned{}; int numBits{}; Type::Category category{}; + bool isQuiet{}; explicit TypeInfo(const Type* type) { + category = type->category(); + isNumeric = true; if (auto* integerType = to(type)) { - isNumeric = true; isSigned = integerType->isSigned(); numBits = int(integerType->numBits()); + } else if (auto* qintegerType = to(type)) { + isSigned = qintegerType->asIntegerType()->isSigned(); + numBits = int(qintegerType->asIntegerType()->numBits()); + isQuiet = true; + } else if (auto* varint = to(type)) { + isSigned = varint->asIntegerType().isSigned(); + numBits = int(varint->asIntegerType().numBits()); } else if (to(type)) { - isNumeric = true; isSigned = true; numBits = 1; + } else if (to(type)) { + isSigned = true; + numBits = 1; + isQuiet = true; } else if (auto* fixedBytesType = to(type)) { - isNumeric = true; isSigned = false; numBits = 8 * static_cast(fixedBytesType->numBytes()); } else if (auto enumType = to(type)) { - isNumeric = true; isSigned = false; numBits = bitsForEnum(enumType->numberOfMembers()); } else if (auto* fp = to(type)) { - isNumeric = true; isSigned = fp->isSigned(); numBits = int(fp->numBits()); - } - category = type->category(); + } else + isNumeric = false; } }; @@ -158,9 +167,6 @@ getContractFunctionPairs(ContractDefinition const* contract); bool isSuper(Expression const* expr); bool isAddressThis(const FunctionCall* funCall); -// List of all function but constructors with a given name -std::vector getContractFunctions(ContractDefinition const* contract, const std::string& funcName); - FunctionDefinition const* getSuperFunction( const ContractDefinition* currentContract, const ContractDefinition* mainContract, @@ -397,4 +403,6 @@ namespace MathConsts { std::map const& power10(); } +bool isFitUselessUnary(Type const* common, Token op); +bool isFitUseless(Type const* left, Type const* right, Type const* common, Token op); } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMConstants.hpp b/compiler/libsolidity/codegen/TVMConstants.hpp index 98025223..8a804e71 100644 --- a/compiler/libsolidity/codegen/TVMConstants.hpp +++ b/compiler/libsolidity/codegen/TVMConstants.hpp @@ -36,7 +36,6 @@ namespace TvmConst { } const int MsgPubkey = 5; constexpr int ConstructorFlag = 6; - constexpr int AwaitAnswerId = 8; constexpr int SenderAddress = 9; constexpr int FirstIndexForVariables = 10; } @@ -90,10 +89,9 @@ namespace TvmConst { const int ByExtMsgOnly = 71; const int ByIntMsgOnly = 72; const int WrongValueOfEnum = 73; - const int WrongAwaitAddress = 74; - const int WrongAwaitFuncId = 75; const int CallThatWasBeforeCtorCall = 76; const int BadVariant = 77; + const int IsNaN = 80; } namespace FunctionId { diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.cpp b/compiler/libsolidity/codegen/TVMContractCompiler.cpp index 54932458..967e1be9 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.cpp @@ -20,17 +20,17 @@ #include -#include "PeepholeOptimizer.hpp" -#include "SizeOptimizer.hpp" -#include "StackOptimizer.hpp" -#include "TVMABI.hpp" -#include "TvmAst.hpp" -#include "TvmAstVisitor.hpp" -#include "TVMConstants.hpp" -#include "TVMContractCompiler.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMFunctionCompiler.hpp" -#include "TVMInlineFunctionChecker.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace std; @@ -141,19 +141,14 @@ TVMContractCompiler::generateContractCode( if (!ctx.isBaseFunction(_function)) functions.emplace_back(TVMFunctionCompiler::generateOnCodeUpgrade(ctx, _function)); } else { - if (!ctx.isStdlib()) { - if (_function->isPublic()) { - bool isBaseMethod = _function != getContractFunctions(contract, _function->name()).back(); - if (!isBaseMethod) { - functions.emplace_back(TVMFunctionCompiler::generatePublicFunction(ctx, _function)); - - StackPusher pusher{&ctx}; - ChainDataEncoder encoder{&pusher}; // TODO delete pusher - uint32_t functionId = encoder.calculateFunctionIDWithReason(_function, - ReasonOfOutboundMessage::RemoteCallInternal); - ctx.addPublicFunction(functionId, _function->name()); - } - } + if (!ctx.isStdlib() && _function->isPublic() && !ctx.isBaseFunction(_function)) { + functions.emplace_back(TVMFunctionCompiler::generatePublicFunction(ctx, _function)); + + StackPusher pusher{&ctx}; + ChainDataEncoder encoder{&pusher}; + uint32_t functionId = encoder.calculateFunctionIDWithReason(_function, + ReasonOfOutboundMessage::RemoteCallInternal); + ctx.addPublicFunction(functionId, _function->name()); } std::string const functionName = ctx.getFunctionInternalName(_function); functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, _function, functionName)); @@ -166,22 +161,12 @@ TVMContractCompiler::generateContractCode( functions.emplace_back(TVMFunctionCompiler::generateDefaultC4(ctx)); { StackPusher pusher{&ctx}; - Pointer f = pusher.generateC7ToC4(false); - functions.emplace_back(f); - } - if (ctx.usage().hasAwaitCall()) { - StackPusher pusher{&ctx}; - Pointer f = pusher.generateC7ToC4(true); + Pointer f = pusher.generateC7ToC4(); functions.emplace_back(f); } functions.emplace_back(TVMFunctionCompiler::updateOnlyTime(ctx)); functions.emplace_back(TVMFunctionCompiler::generateMainInternal(ctx, contract)); - if (ctx.usage().hasAwaitCall()) { - functions.emplace_back(TVMFunctionCompiler::generateCheckResume(ctx)); - } - { - functions.emplace_back(TVMFunctionCompiler::generateMainExternal(ctx, contract)); - } + functions.emplace_back(TVMFunctionCompiler::generateMainExternal(ctx, contract)); } for (VariableDeclaration const* vd : ctx.c4StateVariables()) { @@ -214,8 +199,10 @@ TVMContractCompiler::generateContractCode( cast_error(*function->modifiers().at(0).get(), "Modifiers for library functions are not supported yet."); } - if (!function->parameters().empty()) - functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function)); + if (!function->parameters().empty()) { + const std::string name = TVMCompilerContext::getLibFunctionName(function, true); + functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function, name)); + } const std::string name = TVMCompilerContext::getLibFunctionName(function, false); functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); } @@ -229,11 +216,14 @@ TVMContractCompiler::generateContractCode( for (ASTPointer const &node: source->nodes()) { if (auto function = dynamic_cast(node.get())) { if (function->isFree() && !function->isInlineAssembly()) { - if (!function->modifiers().empty()) { + if (!function->modifiers().empty()) cast_error(*function->modifiers().at(0).get(), "Modifiers for free functions are not supported yet."); + if (!function->parameters().empty()) { + const std::string name = ctx.getFunctionInternalName(function, true); + functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function, name)); } - const std::string name = ctx.getFunctionInternalName(function); + const std::string name = ctx.getFunctionInternalName(function, false); functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); } } diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.hpp b/compiler/libsolidity/codegen/TVMContractCompiler.hpp index a0d34438..fed02d5a 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.hpp @@ -16,9 +16,9 @@ #pragma once -#include "TVM.hpp" -#include "TVMPusher.hpp" -#include "TvmAst.hpp" +#include +#include +#include namespace solidity::frontend { diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp index ce718acb..77eabf55 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp @@ -18,12 +18,12 @@ #include -#include "DictOperations.hpp" -#include "TVMConstants.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMFunctionCall.hpp" -#include "TVMStructCompiler.hpp" -#include "TVM.hpp" +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace solidity::langutil; @@ -115,20 +115,23 @@ void TVMExpressionCompiler::visitStringLiteralAbiV2(Literal const &_node) { void TVMExpressionCompiler::visit2(Literal const &_node) { const auto* type = getType(&_node); switch (_node.annotation().type->category()) { - case Type::Category::Bool: - m_pusher << (_node.token() == Token::TrueLiteral? "TRUE" : "FALSE"); - break; - case Type::Category::Null: - m_pusher.pushNull(); - break; - case Type::Category::EmpyMap: - m_pusher << "NEWDICT"; - break; - case Type::Category::StringLiteral: - visitStringLiteralAbiV2(_node); - break; - default: - cast_error(_node, string("Unsupported type ") + type->canonicalName()); + case Type::Category::Bool: + m_pusher << (_node.token() == Token::TrueLiteral? "TRUE" : "FALSE"); + break; + case Type::Category::Null: + m_pusher.pushNull(); + break; + case Type::Category::TVMNaN: + m_pusher.pushNaN(); + break; + case Type::Category::EmptyMap: + m_pusher << "NEWDICT"; + break; + case Type::Category::StringLiteral: + visitStringLiteralAbiV2(_node); + break; + default: + cast_error(_node, string("Unsupported type ") + type->canonicalName()); } } @@ -257,37 +260,41 @@ void TVMExpressionCompiler::compileUnaryOperation( const std::string &tvmUnaryOperation, const bool isPrefixOperation ) { - const int saveStackSize = m_pusher.stackSize(); Type const* resType = _node.annotation().type; - LValueInfo lValueInfo; + + auto checkResult = [&](){ + if (!isFitUselessUnary(resType, _node.getOperator()) && + !m_pusher.ctx().ignoreIntegerOverflow() + ) + m_pusher.checkFit(resType); + }; + + const int saveStackSize = m_pusher.stackSize(); + LValueInfo lValueInfo = expandLValue(&_node.subExpression(), true);; + const int expandedLValueSize = m_pusher.stackSize() - saveStackSize - 1; + solAssert(expandedLValueSize >= 0, ""); if (isCurrentResultNeeded()) { - lValueInfo = expandLValue(&_node.subExpression(), true); - const int expandedLValueSize = m_pusher.stackSize() - saveStackSize - 1; - solAssert(expandedLValueSize >= 0, ""); if (isPrefixOperation) { m_pusher << tvmUnaryOperation; + checkResult(); m_pusher.pushS(0); // expanded.. value value - if (expandedLValueSize != 0) { + if (expandedLValueSize != 0) m_pusher.blockSwap(expandedLValueSize + 1, 1); // value expanded.. value - } } else { m_pusher.pushS(0); // expanded.. value value m_pusher << tvmUnaryOperation; // expanded.. value newValue + checkResult(); if (expandedLValueSize != 0) { m_pusher.exchange(1); // expanded.. newValue value m_pusher.blockSwap(expandedLValueSize + 1, 1); // value expanded.. newValue } } } else { - lValueInfo = expandLValue(&_node.subExpression(), true); m_pusher << tvmUnaryOperation; + checkResult(); } - if (!isCheckFitUseless(resType, _node.getOperator()) && - !m_pusher.ctx().ignoreIntegerOverflow() - ) - m_pusher.checkFit(resType); - collectLValue(lValueInfo, true, false); + collectLValue(lValueInfo, true); } void TVMExpressionCompiler::compileUnaryDelete(UnaryOperation const &node) { @@ -296,28 +303,37 @@ void TVMExpressionCompiler::compileUnaryDelete(UnaryOperation const &node) { Type const* exprType = node.subExpression().annotation().type; if (to(lastExpr)) { m_pusher.pushDefaultValue(exprType); - collectLValue(lValueInfo, true, false); + collectLValue(lValueInfo, true); } else if (auto memberAccess = to(lastExpr)) { MemberAccessAnnotation& a = memberAccess->annotation(); auto decl = to(a.referencedDeclaration); m_pusher.pushDefaultValue(decl->type()); - collectLValue(lValueInfo, true, false); + collectLValue(lValueInfo, true); } else if (auto indexAccess = to(lastExpr)) { - // ... index dict Type const* baseExprType = indexAccess->baseExpression().annotation().type; - auto arrayType = to(baseExprType); - if (arrayType) { + if (baseExprType->category() == Type::Category::Array) { + // ... index dict + auto arrayType = to(baseExprType); Type const* valueType = arrayType->baseType(); m_pusher.pushDefaultValue(valueType); // index dict value - collectLValue(lValueInfo, true, false); - } else { // mapping + collectLValue(lValueInfo, true); + } else if (baseExprType->category() == Type::Category::TvmVector) { + // ... index vector + auto arrayType = to(baseExprType); + Type const* valueType = arrayType->valueType(); + m_pusher.pushDefaultValue(valueType); // index vector value + collectLValue(lValueInfo, true); + } else if (baseExprType->category() == Type::Category::Mapping) { // mapping + // ... index dict m_pusher.pushS(1); // ... index dict index Type const* dictKey = StackPusher::parseIndexType(indexAccess->baseExpression().annotation().type); m_pusher.exchange(1); // ... index index' dict m_pusher.pushInt(dictKeyLength(dictKey)); // ..index index dict nbits m_pusher << "DICT" + typeToDictChar(dictKey) + "DEL"; // ... index dict' {-1,0} m_pusher.drop(); // ... index dict' - collectLValue(lValueInfo, false, false); // lValueInfo.isValueBuilder is ignored + collectLValue(lValueInfo, false); + } else { + solUnimplemented(""); } } else { solUnimplemented(""); @@ -325,37 +341,34 @@ void TVMExpressionCompiler::compileUnaryDelete(UnaryOperation const &node) { } void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { + auto type = getType(&_node.subExpression()); + TypeInfo ti{type}; + std::string const prefix = ti.isQuiet ? "Q" : ""; + auto op = _node.getOperator(); if (op == Token::Inc) { - compileUnaryOperation(_node, "INC", _node.isPrefixOperation()); + compileUnaryOperation(_node, prefix + "INC", _node.isPrefixOperation()); } else if (op == Token::Dec) { - compileUnaryOperation(_node, "DEC", _node.isPrefixOperation()); + compileUnaryOperation(_node, prefix + "DEC", _node.isPrefixOperation()); } else if (op == Token::Not) { + solAssert(ti.isNumeric, "! operator is supported only for numbers."); compileNewExpr(&_node.subExpression()); - m_pusher << "NOT"; + m_pusher << prefix + "NOT"; } else if (op == Token::Sub) { + solAssert(ti.isNumeric, "- operator is supported only for numbers."); compileNewExpr(&_node.subExpression()); - m_pusher << "NEGATE"; + m_pusher << prefix + "NEGATE"; if (!m_pusher.ctx().ignoreIntegerOverflow()) m_pusher.checkFit(getType(&_node.subExpression())); } else if (op == Token::BitNot) { - int numBits = 0; - bool isSigned {}; - auto type = getType(&_node.subExpression()); - TypeInfo ti{type}; - if (ti.isNumeric) { - numBits = ti.numBits; - isSigned = ti.isSigned; - } else { - cast_error(_node, "~ operation is supported only for numbers."); - } - if (isSigned) { + solAssert(ti.isNumeric, "~ operator is supported only for numbers."); + if (ti.isSigned) { compileNewExpr(&_node.subExpression()); - m_pusher << "BITNOT"; + m_pusher << prefix + "BITNOT"; } else { - m_pusher.pushInt((bigint(1) << numBits) - 1); + m_pusher.pushInt((bigint(1) << ti.numBits) - 1); compileNewExpr(&_node.subExpression()); - m_pusher << "SUB"; + m_pusher << prefix + "SUB"; } } else if (op == Token::Delete) { compileUnaryDelete(_node); @@ -397,27 +410,26 @@ void TVMExpressionCompiler::compareSlices(Token op) { void TVMExpressionCompiler::compareStrings(Token op) { m_pusher.pushFragmentInCallRef(2, 1, "__compareStrings"); switch(op) { - case Token::GreaterThan: - m_pusher << "ISPOS"; - break; - case Token::GreaterThanOrEqual: - m_pusher << "ISNNEG"; - break; - case Token::LessThan: - m_pusher << "ISNEG"; - break; - case Token::LessThanOrEqual: - m_pusher << "ISNPOS"; - break; - case Token::Equal: - m_pusher << "ISZERO"; - break; - case Token::NotEqual: - m_pusher << "ISZERO"; - m_pusher << "NOT"; - break; - default: - solUnimplemented("Wrong compare operation"); + case Token::GreaterThan: + m_pusher << "ISPOS"; + break; + case Token::GreaterThanOrEqual: + m_pusher << "ISNNEG"; + break; + case Token::LessThan: + m_pusher << "ISNEG"; + break; + case Token::LessThanOrEqual: + m_pusher << "ISNPOS"; + break; + case Token::Equal: + m_pusher << "EQINT 0"; + break; + case Token::NotEqual: + m_pusher << "NEQINT 0"; + break; + default: + solUnimplemented("Wrong compare operation"); } } @@ -481,19 +493,82 @@ void TVMExpressionCompiler::visitBinaryOperationForTvmCell( void TVMExpressionCompiler::visitLogicalShortCircuiting(BinaryOperation const &_binaryOperation) { const Token op = _binaryOperation.getOperator(); - - std::vector order = unroll(_binaryOperation); - compileNewExpr(order[0]); - for (int i = 1; i < static_cast(order.size()); ++i) { - m_pusher.pushS(0); - m_pusher.fixStack(-1); // fix stack - m_pusher.startContinuation(); - m_pusher.drop(); - compileNewExpr(order[i]); - } - - for (int i = static_cast(order.size()) - 1; i >= 1; --i) { - m_pusher.endLogCircuit(op == Token::Or ? LogCircuit::Type::OR : LogCircuit::Type::AND); + solAssert(op == Token::And || op == Token::Or, ""); + bool const isAnd = op == Token::And; + bool const isQuiet = _binaryOperation.annotation().type->category() == Type::Category::QBool; + if (isQuiet) { + int const startStackSize = m_pusher.stackSize(); + std::vector order = unroll(_binaryOperation); + compileNewExpr(order[0]); // a + for (int i = 1; i < static_cast(order.size()); ++i) { + m_pusher.pushS(0); // a a + m_pusher << "ISNAN"; // a a.isNaN() + m_pusher.pushS(0); // a a.isNaN() a.isNaN() + m_pusher.startContinuation(); + { + // a a.isNaN() + m_pusher.drop(); // a + m_pusher.pushS(0); // a a + if (!isAnd) + m_pusher << "NOT"; // a !a + } + m_pusher.endContinuation(); + m_pusher.ifNot(); + // a (a.isNaN() || a.get()) // for && + // a (a.isNaN() || !a.get()) // for || + m_pusher.startContinuation(); + { + // a + m_pusher.fixStack(startStackSize - m_pusher.stackSize() + 1); + compileNewExpr(order[i]); // a b + + m_pusher.pushS(0); // a b b + m_pusher << "ISNAN"; // a b b.isNaN() + m_pusher.pushS(0); // a b b.isNaN() b.isNaN() + m_pusher.startContinuation(); + { + // a b !b.isNaN() + m_pusher.drop(); // a b + m_pusher.pushS(0); // a b + if (!isAnd) + m_pusher << "NOT"; // a b !b + } + m_pusher.endContinuation(); + m_pusher.ifNot(); + // a b (b.isNaN() || b.get()) // for && + // a b (b.isNaN() || !b.get()) // for || + m_pusher.startContinuation(); + { + // a b + m_pusher << (isAnd ? "QAND" : "QOR"); + } + m_pusher.endContinuation(); + m_pusher.startContinuation(); + { + // a b + m_pusher.dropUnder(1, 1); + // b + } + m_pusher.endContinuation(); + m_pusher.ifElse(); + } + m_pusher.endContinuation(); + m_pusher._if(); + } + m_pusher.fixStack(startStackSize - m_pusher.stackSize() + 1); + solAssert(m_pusher.stackSize() == startStackSize + 1, ""); + } else { + std::vector order = unroll(_binaryOperation); + compileNewExpr(order[0]); + for (int i = 1; i < static_cast(order.size()); ++i) { + m_pusher.pushS(0); + m_pusher.fixStack(-1); // fix stack + m_pusher.startContinuation(); + m_pusher.drop(); + compileNewExpr(order[i]); + } + for (int i = static_cast(order.size()) - 1; i >= 1; --i) + m_pusher.endLogCircuit(op == Token::Or ? LogCircuit::Type::OR : LogCircuit::Type::AND); } } @@ -560,119 +635,125 @@ void TVMExpressionCompiler::visit2(BinaryOperation const &_binaryOperation) { const auto& leftValue = ExprUtils::constValue(_binaryOperation.leftExpression()); const auto& rightValue = ExprUtils::constValue(_binaryOperation.rightExpression()); - visitMathBinaryOperation(op, commonType, leftValue, acceptRight, rightValue); + visitMathBinaryOperation(op, lt, rt, commonType, leftValue, acceptRight, rightValue); } -bool TVMExpressionCompiler::isCheckFitUseless(Type const* commonType, Token op) { - auto intResult = to(commonType); - return - intResult && - !intResult->isSigned() && - intResult->numBits() == 256 && - isIn(op, Token::Add, Token::Exp, Token::Mul, Token::SHL, Token::Inc); - -} - -// if pushRight is set we haven't value on stack -// else right value is on stack +// if pushRight is set we haven't value on the stack +// else right value is on the stack void TVMExpressionCompiler::visitMathBinaryOperation( const Token op, + Type const* leftType, + Type const* rightType, Type const* commonType, const std::optional& leftValue, const std::function& pushRight, const std::optional& rightValue ) { - bool checkOverflow = false; + bool const isQuiet = isIn(commonType->category(), Type::Category::QInteger, Type::Category::QBool); + std::string const prefix = isQuiet ? "Q" : ""; + + bool canOverflow = false; if (op == Token::Exp) { if (rightValue.has_value() && (*rightValue == 2 || *rightValue == 3 || *rightValue == 4)) { if (*rightValue == 2) { m_pusher.pushS(0); - m_pusher << "MUL"; + m_pusher << prefix + "MUL"; } else if (*rightValue == 3) { m_pusher.pushS(0); m_pusher.pushS(0); - m_pusher << "MUL"; - m_pusher << "MUL"; + m_pusher << prefix + "MUL"; + m_pusher << prefix + "MUL"; } else if (*rightValue == 4) { m_pusher.pushS(0); - m_pusher << "MUL"; + m_pusher << prefix + "MUL"; m_pusher.pushS(0); - m_pusher << "MUL"; + m_pusher << prefix + "MUL"; } else { solUnimplemented(""); } } else { if (leftValue.has_value() && leftValue == 2) { + solAssert(!isQuiet, "TODO support!"); // TODO quint(1) == 1 m_pusher.drop(); pushRight(); m_pusher << "POW2"; } else { pushRight(); - m_pusher.pushS(1); - m_pusher.pushS(1); - m_pusher << "OR"; - m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::Exponent00)); - m_pusher.pushFragmentInCallRef(2, 1, "__exp"); + std::string expName = isQuiet ? "__qexp" : "__exp"; + m_pusher.pushFragmentInCallRef(2, 1, expName); } } - checkOverflow = true; + canOverflow = true; } else { - if (pushRight) { + if (pushRight) pushRight(); - } + if (op == Token::Add) { - m_pusher << "ADD"; - checkOverflow = true; + m_pusher << prefix + "ADD"; + canOverflow = true; } else if (op == Token::Mul) { if (commonType->category() == Type::Category::FixedPoint) { + solAssert(!isQuiet, ""); int power = to(commonType)->fractionalDigits(); m_pusher.pushInt(MathConsts::power10().at(power)); m_pusher << "MULDIV"; } else { - m_pusher << "MUL"; + m_pusher << prefix + "MUL"; } - checkOverflow = true; + canOverflow = true; } else if (op == Token::Sub) { - m_pusher << "SUB"; - checkOverflow = true; + m_pusher << prefix + "SUB"; + canOverflow = true; } - else if (op == Token::Mod) { m_pusher << "MOD"; } + else if (op == Token::Mod) + m_pusher << prefix + "MOD"; else if (op == Token::Div) { if (commonType->category() == Type::Category::FixedPoint) { + solAssert(!isQuiet, ""); int power = to(commonType)->fractionalDigits(); - m_pusher.pushInt(MathConsts::power10().at(power)); // res 10^n - m_pusher.exchange(1); - m_pusher << "MULDIV"; + m_pusher.pushInt(MathConsts::power10().at(power)); // a b 10^n + m_pusher.exchange(1); // a 10^n b + m_pusher << "MULDIV"; // a * 10^n / b } else { - m_pusher << "DIV"; + m_pusher << prefix + "DIV"; } + canOverflow = true; + } + else if (op == Token::GreaterThan) m_pusher << prefix + "GREATER"; + else if (op == Token::GreaterThanOrEqual) m_pusher << prefix + "GEQ"; + else if (op == Token::LessThan) m_pusher << prefix + "LESS"; + else if (op == Token::LessThanOrEqual) m_pusher << prefix + "LEQ"; + else if (op == Token::Equal) m_pusher << prefix + "EQUAL"; + else if (op == Token::NotEqual) m_pusher << prefix + "NEQ"; + else if (op == Token::BitAnd) { + if (isQuiet) + m_pusher.pushFragmentInCallRef(2, 1, "__qand"); + else + m_pusher << "AND"; + } + else if (op == Token::BitOr) { + if (isQuiet) + m_pusher.pushFragmentInCallRef(2, 1, "__qor"); + else + m_pusher << "OR"; } - else if (op == Token::GreaterThan) m_pusher << "GREATER"; - else if (op == Token::GreaterThanOrEqual) m_pusher << "GEQ"; - else if (op == Token::LessThan) m_pusher << "LESS"; - else if (op == Token::LessThanOrEqual) m_pusher << "LEQ"; - else if (op == Token::Equal) m_pusher << "EQUAL"; - else if (op == Token::NotEqual) m_pusher << "NEQ"; - else if (op == Token::BitAnd) m_pusher << "AND"; - else if (op == Token::BitOr) m_pusher << "OR"; else if (op == Token::SHL) { if (leftValue.has_value() && leftValue == 1) { + solAssert(!isQuiet, ""); // TODO quint(1) == 1 m_pusher.dropUnder(1, 1); m_pusher << "POW2"; - } else { - m_pusher << "LSHIFT"; - } - checkOverflow = true; + } else + m_pusher << prefix + "LSHIFT"; + canOverflow = true; } - else if (op == Token::SAR) m_pusher << "RSHIFT"; - else if (op == Token::BitXor) m_pusher << "XOR"; - else { + else if (op == Token::SAR) m_pusher << prefix + "RSHIFT"; + else if (op == Token::BitXor) m_pusher << prefix + "XOR"; + else solUnimplemented("Unsupported binary operation"); - } } - if (checkOverflow && - !isCheckFitUseless(commonType, op) && + if (canOverflow && + !isFitUseless(leftType, rightType, commonType, op) && !m_pusher.ctx().ignoreIntegerOverflow() ) m_pusher.checkFit(commonType); @@ -865,8 +946,8 @@ void TVMExpressionCompiler::visitMagic(MemberAccess const &_memberAccess) { const Type *argType = arg->typeArgument(); if (auto const* integerType = to(argType)) opcode += toString(member == "min" ? integerType->minValue() : integerType->maxValue()); - else if (auto const* varInt = to(argType)) - opcode += toString(member == "min" ? varInt->asIntegerType().minValue() : varInt->asIntegerType().maxValue()); + else if (auto const* varint = to(argType)) + opcode += toString(member == "min" ? varint->asIntegerType().minValue() : varint->asIntegerType().maxValue()); else if (auto const* enumType = to(argType)) opcode += toString(member == "min" ? enumType->minValue() : enumType->maxValue()); else @@ -1003,7 +1084,7 @@ void TVMExpressionCompiler::indexTypeCheck(IndexAccess const &_node) { Type const* baseExprType = _node.baseExpression().annotation().type; Type::Category baseExprCategory = _node.baseExpression().annotation().type->category(); if (!isIn(baseExprCategory, Type::Category::Mapping, Type::Category::TvmVector) && !isUsualArray(baseExprType)) { - cast_error(_node, "Index access is supported only for dynamic arrays, tuples and mappings"); + cast_error(_node, "Index access is supported only for dynamic arrays, vectors and mappings"); } } @@ -1066,18 +1147,9 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { acceptExpr(&indexAccess.baseExpression()); // tuple const auto& val = ExprUtils::constValue(*indexAccess.indexExpression()); if (val.has_value() && val <= 15) { - m_pusher.indexWithExcep(0); m_pusher.indexWithExcep(boost::lexical_cast(val.value().str())); } else { acceptExpr(indexAccess.indexExpression()); // vector index - m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher << "DIVMOD"; - // vector tuple_num rest - m_pusher.rotRev(); - // rest vector tuple_num - m_pusher << "INDEXVAR"; - // rest tuple - m_pusher.exchange(1); m_pusher << "INDEXVAR"; } return; @@ -1131,13 +1203,22 @@ void TVMExpressionCompiler::visit2(Conditional const &_conditional) { solAssert(stackSize + paramQty == m_pusher.stackSize(), ""); } -bool TVMExpressionCompiler::isOptionalGet(Expression const* expr) { +namespace { +bool isFunctionKind(Expression const* expr, FunctionType::Kind kind) { auto funCall = to(expr); - if (!funCall) { + if (!funCall) return false; - } - auto ma = to(&funCall->expression()); - return ma && ma->expression().annotation().type->category() == Type::Category::Optional; + auto functionType = to(funCall->expression().annotation().type); + return functionType && functionType->kind() == kind; +} + +bool isOptionalGet(Expression const* expr) { + return isFunctionKind(expr, FunctionType::Kind::OptionalGet); +} + +bool isStackTop(Expression const* expr) { + return isFunctionKind(expr, FunctionType::Kind::TVMStackTop); +} } LValueInfo @@ -1145,7 +1226,8 @@ TVMExpressionCompiler::expandLValue( Expression const *const _expr, const bool withExpandLastValue ) { - solAssert(*_expr->annotation().isLValue, ""); + if (!*_expr->annotation().isLValue) + cast_error(*_expr, "Expression has to be an lvalue."); const int startStackSize = m_pusher.stackSize(); LValueInfo lValueInfo {}; @@ -1162,7 +1244,7 @@ TVMExpressionCompiler::expandLValue( memberAccess && getType(&memberAccess->expression())->category() == Type::Category::Struct ) { expr = &memberAccess->expression(); - } else if (isOptionalGet(expr)) { + } else if (isOptionalGet(expr) || isStackTop(expr)) { auto funCall = to(expr); auto ma = to(&funCall->expression()); expr = &ma->expression(); @@ -1196,7 +1278,8 @@ TVMExpressionCompiler::expandLValue( // dict1 index m_pusher.exchange(1); // index dict1 - if (isLast && !withExpandLastValue) break; + if (isLast && !withExpandLastValue) + break; m_pusher.pushS2(1, 0); // index dict1 index dict1 @@ -1204,6 +1287,13 @@ TVMExpressionCompiler::expandLValue( *index->annotation().type, GetDictOperation::GetFromMapping); // index dict1 dict2 + } else if (index->baseExpression().annotation().type->category() == Type::Category::TvmVector) { + // vector + compileNewExpr(index->indexExpression()); // vector index + if (isLast && !withExpandLastValue) + break; + m_pusher.pushS2(1, 0); // vector index vector index + m_pusher << "INDEXVAR"; // vector index value } else if (index->baseExpression().annotation().type->category() == Type::Category::Array) { // array m_pusher << "UNTUPLE 2"; // size dict @@ -1212,9 +1302,8 @@ TVMExpressionCompiler::expandLValue( m_pusher.pushS2(1, 2); // size index dict index size m_pusher << "LESS"; // size index dict indexbaseExpression().annotation().type), *index->annotation().type, GetDictOperation::GetFromArray); @@ -1232,10 +1321,15 @@ TVMExpressionCompiler::expandLValue( m_pusher.pushS(0); structCompiler.pushMember(memberName); } else if (isOptionalGet(lValueInfo.expressions[i])) { - if (!isLast || withExpandLastValue) { + if (!isLast || withExpandLastValue) m_pusher.pushS(0); - } m_pusher.checkOptionalValue(); + } else if (isStackTop(lValueInfo.expressions[i])) { + if (!isLast || withExpandLastValue) { + // stack + m_pusher << "UNTUPLE 2"; // value stack + m_pusher.blockSwap(1, 1); // stack value + } } else { solUnimplemented(""); } @@ -1247,8 +1341,7 @@ TVMExpressionCompiler::expandLValue( void TVMExpressionCompiler::collectLValue( const LValueInfo &lValueInfo, - const bool haveValueOnStackTop, - bool /*isValueBuilder*/ + const bool haveValueOnStackTop ) { // variable [arrayIndex | mapIndex | structMember | .get()]... @@ -1259,7 +1352,6 @@ TVMExpressionCompiler::collectLValue( const bool isLast = i + 1 == n; if (auto variable = to(lValueInfo.expressions[i])) { -// pushLog("colVar"); auto& stack = m_pusher.getStack(); if (stack.isParam(variable->annotation().referencedDeclaration)) { solAssert((haveValueOnStackTop && n == 1) || n > 1, ""); @@ -1282,11 +1374,18 @@ TVMExpressionCompiler::collectLValue( m_pusher.rotRev(); // value index dict m_pusher.setDict(*keyType, *valueDictType, dataType); // dict' } + } else if (indexAccess->baseExpression().annotation().type->category() == Type::Category::TvmVector) { + if (isLast && !haveValueOnStackTop) { + solUnimplemented("Impossible case"); + } else { + // vector index value + m_pusher.blockSwap(1, 1); + m_pusher << "SETINDEXVAR"; + } } else if (indexAccess->baseExpression().annotation().type->category() == Type::Category::Array) { - // pushLog("colArrIndex"); if (isLast && !haveValueOnStackTop) { // size index dict - m_pusher.popS(1); // size dict + solUnimplemented("Impossible case"); } else { // size index dict value Type const* keyType = StackPusher::parseIndexType(indexAccess->baseExpression().annotation().type); @@ -1295,19 +1394,21 @@ TVMExpressionCompiler::collectLValue( m_pusher.rotRev(); // size value index dict m_pusher.setDict(*keyType, *valueDictType, dataType); // size dict' } - m_pusher << "TUPLE 2"; } else { solUnimplemented(""); } } else if (auto memberAccess = to(lValueInfo.expressions[i])) { -// pushLog("colStruct"); auto structType = to(memberAccess->expression().annotation().type); StructCompiler structCompiler{&m_pusher, structType}; const string &memberName = memberAccess->memberName(); structCompiler.setMemberForTuple(memberName); } else if (isOptionalGet(lValueInfo.expressions[i])) { // do nothing + } else if (isStackTop(lValueInfo.expressions[i])) { + // stack value + m_pusher.blockSwap(1, 1); // value stack + m_pusher << "TUPLE 2"; } else { solUnimplemented(""); } @@ -1325,10 +1426,9 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { auto push_rhs = [&] () { compileNewExpr(&rhs); m_pusher.convert(getType(&lhs), getType(&rhs)); - return false; }; const int saveStackSize0 = m_pusher.stackSize(); - bool valueIsBuilder = push_rhs(); + push_rhs(); const int saveStackSize = m_pusher.stackSize(); const LValueInfo lValueInfo = expandLValue(&lhs, false); if (isCurrentResultNeeded()) { @@ -1337,7 +1437,7 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { } else { m_pusher.blockSwap(saveStackSize - saveStackSize0, m_pusher.stackSize() - saveStackSize); } - collectLValue(lValueInfo, true, valueIsBuilder); + collectLValue(lValueInfo, true); } else { Type const*& commonType = lhs.annotation().type; compileNewExpr(&rhs); // r @@ -1351,7 +1451,8 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { if (isString(commonType)) { m_pusher.pushFragmentInCallRef(2, 1, "__concatenateStrings"); } else { - visitMathBinaryOperation(binOp, commonType, nullopt, nullptr, nullopt); + visitMathBinaryOperation(binOp, lhs.annotation().type, rhs.annotation().type, commonType, + nullopt, nullptr, nullopt); } if (isCurrentResultNeeded()) { @@ -1360,7 +1461,7 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { } else { // expanded... res } - collectLValue(lValueInfo, true, false); + collectLValue(lValueInfo, true); } return true; @@ -1395,7 +1496,7 @@ bool TVMExpressionCompiler::tryAssignTuple(Assignment const &_assignment) { if (expandLValueSize > 0) { m_pusher.blockSwap(1, expandLValueSize); } - collectLValue(lValueInfo, true, false); + collectLValue(lValueInfo, true); } ++i; } diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp index 17680071..366570bf 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp @@ -16,7 +16,7 @@ #pragma once -#include "TVMPusher.hpp" +#include namespace solidity::frontend { @@ -31,7 +31,7 @@ class TVMExpressionCompiler { Expression const* const _expr, const bool withExpandLastValue ); - void collectLValue(const LValueInfo &lValueInfo, bool haveValueOnStackTop, bool isValueBuilder); + void collectLValue(const LValueInfo &lValueInfo, bool haveValueOnStackTop); protected: bool acceptExpr(const Expression* expr); @@ -64,9 +64,11 @@ class TVMExpressionCompiler { ); void visitLogicalShortCircuiting(BinaryOperation const &_binaryOperation); void visit2(BinaryOperation const& _node); - static bool isCheckFitUseless(Type const* type, Token op); +protected: void visitMathBinaryOperation( Token op, + Type const* leftType, + Type const* rightType, Type const* commonType, const std::optional& leftValue, const std::function& pushRight, @@ -84,7 +86,6 @@ class TVMExpressionCompiler { void visit2(FunctionCall const& _functionCall); void visit2(Conditional const& _conditional); bool fold_constants(const Expression *expr); - static bool isOptionalGet(Expression const* expr); bool tryAssignLValue(Assignment const& _assignment); bool tryAssignTuple(Assignment const& _assignment); diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.cpp b/compiler/libsolidity/codegen/TVMFunctionCall.cpp index 9322524c..a75344e3 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.cpp @@ -19,13 +19,13 @@ #include #include -#include "DictOperations.hpp" -#include "TVMABI.hpp" -#include "TVMConstants.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMFunctionCall.hpp" -#include "TVMStructCompiler.hpp" -#include "TVM.hpp" +#include +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace solidity::langutil; @@ -76,7 +76,7 @@ void FunctionCallCompiler::compile() { } if (checkRemoteMethodCall(m_functionCall) || - (m_memberAccess != nullptr && libraryCall(*m_memberAccess)) || + (m_memberAccess != nullptr && libraryCall()) || checkForMappingOrCurrenciesMethods() || checkNewExpression() || checkAddressThis() || @@ -105,8 +105,15 @@ void FunctionCallCompiler::compile() { sliceMethods(*m_memberAccess); } else if (category == Type::Category::TvmBuilder) { builderMethods(*m_memberAccess); + } else if (category == Type::Category::QInteger || category == Type::Category::QBool) { + qIntOrBoolMethods(); + } else if (category == Type::Category::StringBuilder) { + stringBuilderMethods(); + } else if (category == Type::Category::TvmVector) { + tvmVectorMethods(); + } else if (category == Type::Category::TvmStack) { + tvmStackMethods(); } else if ( - checkForTvmVectorMethods(*m_memberAccess, category) || checkForOptionalMethods(*m_memberAccess)) { // nothing @@ -115,8 +122,6 @@ void FunctionCallCompiler::compile() { abiBuildIntMsg(); } else if (m_funcType->kind() == FunctionType::Kind::ABIEncodeData) { abiBuildDataInit(); - } else if (m_funcType->kind() == FunctionType::Kind::ABIBuildExtMsg) { - abiBuildExtMsg(); } else if ( checkForTvmSendFunction(*m_memberAccess) || checkForTvmConfigParamFunction(*m_memberAccess) || @@ -140,8 +145,6 @@ void FunctionCallCompiler::compile() { abiEncodeBody(); else if (m_funcType->kind() == FunctionType::Kind::ABIBuildIntMsg) abiBuildIntMsg(); - else if (m_funcType->kind() == FunctionType::Kind::ABIBuildExtMsg) - abiBuildExtMsg(); else if (m_funcType->kind() == FunctionType::Kind::ABIEncodeData) abiBuildDataInit(); else if (!checkTvmABIDeployMethods(category)) @@ -254,7 +257,7 @@ void FunctionCallCompiler::mappingGetSet() { const int cntOfValuesOnStack = m_pusher.stackSize() - stackSize; m_pusher.blockSwap(cntOfValuesOnStack - 1, 1); // value lValue... map' - m_exprCompiler.collectLValue(lValueInfo, true, false); // value + m_exprCompiler.collectLValue(lValueInfo, true); // value } else if (isIn(memberName, "replace", "add", "getSet", "getAdd", "getReplace")) { const int stackSize = m_pusher.stackSize(); const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); // lValue... map @@ -292,7 +295,7 @@ void FunctionCallCompiler::mappingGetSet() { } const int cntOfValuesOnStack = m_pusher.stackSize() - stackSize; // mapLValue... map optValue m_pusher.blockSwap(cntOfValuesOnStack - 1, 1); // optValue mapLValue... map - m_exprCompiler.collectLValue(lValueInfo, true, false); // optValue + m_exprCompiler.collectLValue(lValueInfo, true); // optValue } else { solUnimplemented(""); } @@ -454,40 +457,42 @@ void FunctionCallCompiler::addressMethods(MemberAccess const &_node) { } } -bool FunctionCallCompiler::libraryCall(MemberAccess const& ma) { - if (auto libFunction = to(ma.annotation().referencedDeclaration)) { - DeclarationAnnotation const &da = libFunction->annotation(); - if (da.contract->contractKind() == ContractKind::Library) { - auto t = getType(&ma.expression()); - const int argQty = static_cast(m_arguments.size()); - const int retQty = static_cast(libFunction->returnParameters().size()); +bool FunctionCallCompiler::libraryCall() { + + auto funcViaObject = [this](FunctionDefinition const* function){ + const int argQty = static_cast(m_arguments.size()); + const int retQty = static_cast(function->returnParameters().size()); + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + // lValue.. arg0 + pushArgs(); // lValue.. arg0 arg1 arg2 ... + m_pusher.pushCallOrCallRef( + function, + std::make_pair(argQty + 1, retQty + 1), + true + ); + // lValue.. arg0 ret0 ret1 ... + m_pusher.blockSwap(lValueInfo.stackSizeDiff, retQty); + // ret0 ret1 ... lValue.. arg0 + m_exprCompiler.collectLValue(lValueInfo, true); + }; + + if (auto function = to(m_memberAccess->annotation().referencedDeclaration)) { + DeclarationAnnotation const &da = function->annotation(); + if (da.contract == nullptr) { + // using {add} for uint; // free function + // a.add(b); + funcViaObject(function); + return true; + } else if (da.contract->contractKind() == ContractKind::Library) { + auto t = getType(&m_memberAccess->expression()); if (t->category() == Type::Category::TypeType) { // uint z = MyLib.sum(a, b); pushArgs(); - m_pusher.pushCallOrCallRef( - libFunction, - std::nullopt, - false - ); - } else { - // using MathLib for uint; + m_pusher.pushCallOrCallRef(function, std::nullopt, false); + } else + // using MathLib for uint; // library // a.add(b); - const int stackSize0 = m_pusher.stackSize(); - const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&ma.expression(), true); - const int stackSize1 = m_pusher.stackSize(); - const int lValueQty = stackSize1 - stackSize0; - - pushArgs(); - m_pusher.pushCallOrCallRef( - libFunction, - std::make_pair(argQty + 1, retQty + 1), - true - ); - - m_pusher.blockSwap(lValueQty, retQty); - - m_exprCompiler.collectLValue(lValueInfo, true, false); - } + funcViaObject(function); return true; } } @@ -584,12 +589,10 @@ std::function FunctionCallCompiler::generateDataSection( if (ctx.storeTimestampInC4()) m_pusher << "STU 64"; m_pusher << "STZERO"; // constructor flag - if (ctx.usage().hasAwaitCall()) - m_pusher << "STZERO"; const std::vector& memberTypes = ctx.c4StateVariableTypes(); if (!memberTypes.empty()) { ChainDataEncoder encoder{&m_pusher}; - DecodePositionAbiV2 position{ctx.getOffsetC4(), ctx.usage().hasAwaitCall() ? 1 : 0, memberTypes}; + DecodePositionAbiV2 position{ctx.getOffsetC4(), 0, memberTypes}; encoder.encodeParameters(memberTypes, position); } m_pusher << "ENDC"; @@ -611,12 +614,6 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa return false; } - // Search for bounce option - if (_functionCall.isExtMsg()) { - checkExtMsgSend(); - return true; - } - // parse options they are stored in two vectors: names and options for (const auto &option: functionOptions->names()) if (!isIn(*option, "flag", "value", "currencies", "bounce", "callback")) @@ -687,21 +684,6 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa } } - if (_functionCall.isAwait()) { - const std::string name = "_await_" + functionDefinition->annotation().contract->name() + "_" + functionDefinition->name(); - std::vector ret; - callbackFunctionId = ChainDataEncoder{&m_pusher}.calculateFunctionIDWithReason( - name, - getTypesFromVarDecls(functionDefinition->returnParameters()), - &ret, - ReasonOfOutboundMessage::RemoteCallInternal, - {}, - false // Function isn't responsible. It's answer of responsible function - ); - m_pusher.pushInt(callbackFunctionId.value()); - m_pusher.setGlob(TvmConst::C7::AwaitAnswerId); - } - pushArgs(true); std::vector argDecl = convertArray(functionDefinition->parameters()); @@ -725,76 +707,10 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa ); }; - if (m_isCurrentResultNeeded && !_functionCall.isAwait()) - cast_error(_functionCall, "Calls to remote contract do not return result."); - - m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, _functionCall.isAwait(), m_arguments.size(), - nullptr); - - if (_functionCall.isAwait()) { - // stack: remote addr - m_pusher.startOpaque(); - m_pusher.startContinuation(); - m_pusher.pushFragmentInCallRef(0, 0, "c7_to_c4_for_await"); - m_pusher.push(createNode(std::vector{ - "DEPTH", - "ADDCONST -5; sys vars", - "PUSHINT 5", - "BLKSWX", - "SETGLOB " + toString(TvmConst::C7::AwaitAnswerId), - "SETGLOB " + toString(TvmConst::C7::SenderAddress), - "SETGLOB " + toString(TvmConst::C7::MsgPubkey), - "DEPTH", - "ADDCONST -4", - "PICK", - "LDU 32", - "SWAP", - }, 0, 0, false)); - m_pusher.getGlob(TvmConst::C7::AwaitAnswerId); - m_pusher << "EQUAL"; - m_pusher._throw("THROWIFNOT " + to_string(TvmConst::RuntimeException::WrongAwaitFuncId)); - m_pusher.fixStack(+2); // fix stack - // decode returned vars - ChainDataDecoder decoder{&m_pusher}; - vector types = getParams(functionDefinition->returnParameters()).first; - decoder.decodePublicFunctionParameters(types, false, true); - m_pusher.fixStack(-1); // fix stack - m_pusher.pushRefContAndCallX(0, 0, false); - m_pusher.endOpaque(1, types.size()); // take one parameter - it's dest address - } + m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, nullptr); return true; } -void FunctionCallCompiler::checkExtMsgSend() { - auto functionOptions = to(&m_functionCall.expression()); - - bool addSignature = false; - Expression const* sign = findOption("sign"); - if (sign != nullptr) { - addSignature = ExprUtils::constBool(*sign).value(); - } - - Expression const* pubkey = findOption("pubkey"); - Expression const* expire = findOption("expire"); - Expression const* time = findOption("time"); - Expression const* callbackid = findOption("callbackId"); - Expression const* onerrorid = findOption("onErrorId"); - Expression const* stateInit = findOption("stateInit"); - Expression const* signBoxHandle = findOption("signBoxHandle"); - Expression const* abiVer = findOption("abiVer"); - Expression const* flags = findOption("flags"); - - auto memberAccess = to(&functionOptions->expression()); - FunctionDefinition const* functionDefinition = getRemoteFunctionDefinition(memberAccess); - - Expression const* destination = &memberAccess->expression(); - - generateExtInboundMsg(addSignature, destination, pubkey, expire, time, callbackid, - onerrorid, stateInit, signBoxHandle, abiVer, flags, functionDefinition, m_arguments); - m_pusher.pushInt(TvmConst::SENDRAWMSG::DefaultFlag); - m_pusher.sendrawmsg(); -} - std::string FunctionCallCompiler::getDefaultMsgValue() { const std::optional>> expr = m_pusher.ctx().pragmaHelper().hasMsgValue(); if (!expr) { @@ -822,239 +738,6 @@ const FunctionDefinition* FunctionCallCompiler::getRemoteFunctionDefinition(cons return f; } -void FunctionCallCompiler::acceptExprOrPushFunctionId(Expression const* onerrorid) { - if (CallableDeclaration const* cd = getFunctionDeclarationOrConstructor(onerrorid, true)) { - auto fd = to(cd); - int funId = ChainDataEncoder{&m_pusher}. - calculateFunctionIDWithReason(fd, ReasonOfOutboundMessage::RemoteCallInternal); - m_pusher.pushInt(funId); - return ; - } - - auto t = to(onerrorid->annotation().type->mobileType()); - solAssert(t && !t->isSigned() && t->numBits() <= 32, ""); - - acceptExpr(onerrorid); -} - -void FunctionCallCompiler::generateExtInboundMsg( - bool addSignature, - const Expression *destination, - const Expression *pubkey, - const Expression *expire, - const Expression *time, - const Expression *callbackid, - const Expression *onerrorid, - const Expression *stateInit, - const Expression *signBoxHandle, - const Expression *abiVer, - const Expression *flags, - const CallableDeclaration *functionDefinition, - const ast_vec& arguments -) { - const int stackSize = m_pusher.stackSize(); - - auto appendBody = [&](int builderSize) { - /* builder size: - 2 header - 119 src abi + callbackid - 267 dest addr_std - 4 import_fee:Grams - 1 stateInit - = 393 - */ - - - // store body in a reference. In the end we store new builder to this builder - builderSize++; - m_pusher.stones(1); // body:(Either X ^X) - builderSize = 0; - - - for (const ASTPointer& arg : arguments | boost::adaptors::reversed) { - acceptExpr(arg.get()); - } - - m_pusher << "NEWC"; - builderSize += TvmConst::Abi::MaxOptionalSignLength; - if (addSignature) { - m_pusher.stones(1); - m_pusher.stzeroes(512); // Signature - } else { - m_pusher.stzeroes(1); // Signature - } - - // store header - // [optional]pubkey: 1 + [256] bits - // time: 64 bits - // expire: 32 bits - - - if (pubkey != nullptr) { - // pubkey is set - builderSize += 1 + 256; - // pubkey is optional, check whether it presents - acceptExpr(pubkey); - - if (addSignature) { - m_pusher.pushS(0); - m_pusher.startOpaque(); - m_pusher << "ISNULL"; - m_pusher.fixStack(-1); - - m_pusher.startContinuation(); - m_pusher.drop(1); - m_pusher.stzeroes(1); - m_pusher.endContinuation(); - - m_pusher.startContinuation(); - m_pusher.exchange(1); - m_pusher.stones(1); - m_pusher.fixStack(+1); // fix stack - m_pusher << "STU 256"; - m_pusher.endContinuation(); - - m_pusher.ifElse(); - m_pusher.endOpaque(3, 1); - } else { - m_pusher << "ISNULL"; - m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::MsgWithKeyButNoSign)); - m_pusher.stzeroes(1); - } - } - // if no pubkey - encode nothing - - if (time != nullptr) { - builderSize += 64; - acceptExpr(time); - m_pusher << "STUR 64"; - } - - if (expire != nullptr) { - builderSize += 32; - acceptExpr(expire); - m_pusher << "STUR 32"; - } - - // function call body - std::vector> funcParams; - if (functionDefinition != nullptr) - funcParams = functionDefinition->parameters(); - const std::vector ¶ms = convertArray(funcParams); - - std::vector types; - types = getParams(params).first; - builderSize += 32; - DecodePositionAbiV2 position{builderSize, 0, types}; - uint32_t functionId; - std::optional callbackFunctionId; - ChainDataEncoder encoder{&m_pusher}; - if (functionDefinition != nullptr) { - functionId = encoder.calculateFunctionIDWithReason(functionDefinition, ReasonOfOutboundMessage::RemoteCallInternal); - auto fd = to(functionDefinition); - solAssert(fd, ""); - if (fd->isResponsible()) { - callbackFunctionId = 0; // set to 0, because it's ext msg and it does matter - } - } else { - functionId = encoder.calculateConstructorFunctionID(); - } - - ChainDataEncoder{&m_pusher}.createMsgBody(params, functionId, callbackFunctionId, position); - - m_pusher << "STBREFR"; - }; - - // store dest address - acceptExpr(destination); - - - // generate payload to store it as a src address with addr_extern type - if (flags != nullptr) - acceptExpr(flags); - else - m_pusher << "PUSHINT 0"; - if (signBoxHandle != nullptr) - acceptExpr(signBoxHandle); - if (abiVer == nullptr) - m_pusher << "PUSHINT " + toString(TvmConst::Message::MajorAbiVersion); - else - acceptExprOrPushFunctionId(abiVer); - acceptExprOrPushFunctionId(onerrorid); - acceptExprOrPushFunctionId(callbackid); - m_pusher << "NEWC"; - // stack: flags [signBoxHandle] abiVer onerrorid callbackid builder - m_pusher << "STSLICECONST x6_"; // header 01 - if (signBoxHandle == nullptr) - m_pusher << "STSLICECONST x2A4_"; // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) - else { - m_pusher.pushS(4); - m_pusher.startOpaque(); - m_pusher << "ISNULL"; - m_pusher.startContinuation(); - m_pusher << "STSLICECONST x2A4_"; // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) - m_pusher.endContinuation(); - m_pusher.startContinuation(); - m_pusher << "STSLICECONST x3A4_"; // const length 116 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 33 opt signBox + 8 flags) - m_pusher.endContinuation(); - m_pusher.ifElse(); - m_pusher.fixStack(-1); - m_pusher.endOpaque(2, 1); - } - // stack: flags [signBoxHandle] abiVer onerrorid callbackid builder - m_pusher << "STU 32"; // stack: flags [signBoxHandle] abiVer onerrorid builder - m_pusher << "STU 32"; // stack: flags [signBoxHandle] abiVer builder - m_pusher << "STU 8"; // stack: flags [signBoxHandle] builder - if (time != nullptr) - m_pusher << "STONE"; - else - m_pusher << "STZERO"; - if (expire != nullptr) - m_pusher << "STONE"; - else - m_pusher << "STZERO"; - if (pubkey != nullptr) - m_pusher << "STONE"; - else - m_pusher << "STZERO"; - if (signBoxHandle == nullptr) - m_pusher << "STZERO"; - else { - // stack: flags [signBoxHandle] builder - m_pusher.pushS(1); - m_pusher.startOpaque(); - m_pusher << "ISNULL"; - m_pusher.startContinuation(); - m_pusher.dropUnder(1, 1); - m_pusher.stzeroes(1); - m_pusher.endContinuation(); - m_pusher.startContinuation(); - m_pusher.stones(1); - m_pusher.fixStack(+1); // fix stack - m_pusher << "STU 32"; - m_pusher.endContinuation(); - m_pusher.ifElse(); - m_pusher.fixStack(-1); // fix stack - m_pusher.endOpaque(3, 1); - } - // stack: flags builder - m_pusher << "STU 8"; - - std::function appendStateInit = nullptr; - if (stateInit != nullptr) - appendStateInit = [&]() { - m_pusher.stones(1); - acceptExpr(stateInit); - m_pusher.pushS(0); - checkStateInit(); - m_pusher << "STREFR"; - }; - - m_pusher.prepareMsg({TvmConst::ext_msg_info::src, TvmConst::ext_msg_info::dest}, {}, appendBody, appendStateInit, StackPusher::MsgType::ExternalIn); - - solAssert(stackSize + 1 == m_pusher.stackSize(), ""); -} - void FunctionCallCompiler::abiBuildIntMsg() { const int stackSize = m_pusher.stackSize(); @@ -1215,86 +898,6 @@ void FunctionCallCompiler::abiBuildDataInit() { solAssert(m_pusher.stackSize() == stackSize + 1, ""); } -void FunctionCallCompiler::abiBuildExtMsg() { - int destArg = -1; - int callArg = -1; - int timeArg = -1; - int expireArg = -1; - int pubkeyArg = -1; - int signArg = -1; - int callbackArg = -1; - int onerrorArg = -1; - int stateArg = -1; - int signHandlerArg = -1; - int abiVerArg = -1; - int flagsArg = -1; - if (!m_names.empty()) { - for (int arg = 0; arg < static_cast(m_arguments.size()); ++arg) { - switch (str2int(m_names[arg]->c_str())) { - case str2int("dest"): - destArg = arg; - break; - case str2int("call"): - callArg = arg; - break; - case str2int("time"): - timeArg = arg; - break; - case str2int("expire"): - expireArg = arg; - break; - case str2int("pubkey"): - pubkeyArg = arg; - break; - case str2int("sign"): - signArg = arg; - break; - case str2int("abiVer"): - abiVerArg = arg; - break; - case str2int("callbackId"): - callbackArg = arg; - break; - case str2int("onErrorId"): - onerrorArg = arg; - break; - case str2int("stateInit"): - stateArg = arg; - break; - case str2int("signBoxHandle"): - signHandlerArg = arg; - break; - case str2int("flags"): - flagsArg = arg; - break; - default: - solUnimplemented(""); - } - } - } - auto funcCall = to(m_arguments[callArg].get()); - auto functionDefinition = getFunctionDeclarationOrConstructor(funcCall->function()); - bool addSignature = false; - if (signArg != -1) { - const std::optional value = ExprUtils::constBool(*m_arguments[signArg]); - if (value.has_value()) { - addSignature = *value; - } - } - - generateExtInboundMsg(addSignature, m_arguments[destArg].get(), - (pubkeyArg != -1) ? m_arguments[pubkeyArg].get() : nullptr, - (expireArg != -1) ? m_arguments[expireArg].get() : nullptr, - (timeArg != -1) ? m_arguments[timeArg].get() : nullptr, - m_arguments[callbackArg].get(), - m_arguments[onerrorArg].get(), - (stateArg != -1) ? m_arguments[stateArg].get() : nullptr, - (signHandlerArg != -1) ? m_arguments[signHandlerArg].get() : nullptr, - (abiVerArg != -1) ? m_arguments[abiVerArg].get() : nullptr, - (flagsArg != -1) ? m_arguments[flagsArg].get() : nullptr, - functionDefinition, funcCall->arguments()); -} - bool FunctionCallCompiler::checkTvmABIDeployMethods(Type::Category category) { if (category != Type::Category::Magic) return false; @@ -1503,7 +1106,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { pushArgAndConvert(1); m_pusher << "SSKIPFIRST"; } - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (isIn(memberName, "loadFunctionParams", "decodeFunctionParams", "loadStateVars", "decodeStateVars")) { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); int paramQty = -1; @@ -1518,7 +1121,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { m_pusher.blockSwap(lValueInfo.stackSizeDiff - 1, paramQty); m_pusher.pushSlice("x8_"); } - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (isIn(memberName, "load", "decode", "loadQ", "decodeQ") || boost::starts_with(memberName, "load")) { int stackDelta = 0; const int stackSize = m_pusher.stackSize(); @@ -1551,7 +1154,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { } else if (isIn(memberName, "loadUint", "loadUnsigned", "loadInt", "loadSigned")) { std::string cmd = "LD"; cmd += isIn(memberName, "loadInt", "loadSigned") ? "I" : "U"; - if (value.has_value() && value != 0) { + if (value.has_value() && 1 <= value && value <= 256) { m_pusher << cmd + " " + value->str(); } else { pushArgAndConvert(0); @@ -1561,7 +1164,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { std::string cmd = "LD"; cmd += memberName == "loadIntQ" ? "I" : "U"; int take{}; - if (value.has_value() && value != 0) { + if (value.has_value() && 1 <= value && value <= 256) { cmd += "Q " + value->str(); take = 1; } else { @@ -1679,7 +1282,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { // lvalue... slice decodedValues... m_pusher.blockSwap(lValueInfo.stackSizeDiff, stackDelta); // decodedValues... lvalue... slice - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else { // decodedValues... slice m_pusher.drop(); @@ -1715,7 +1318,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { } else if (isIn(memberName, "preloadInt", "preloadUint")) { acceptExpr(&_node.expression()); string cmd = string{} + "PLD" + (memberName == "preloadInt" ? "I" : "U"); - if (value.has_value() && value != 0) { + if (value.has_value() && 1 <= value && value <= 256) { cmd += " " + value->str(); } else { pushArgs(); @@ -1733,7 +1336,7 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { acceptExpr(&_node.expression()); string cmd = string{} + "PLD" + (memberName == "preloadIntQ" ? "I" : "U"); int take{}; - if (value.has_value() && value != 0) { + if (value.has_value() && 1 <= value && value <= 256) { take = 1; cmd += "Q " + toString(value.value()); } else { @@ -1806,165 +1409,72 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { } } -bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, Type::Category category) { - if (category != Type::Category::TvmVector) - return false; - - if (_node.memberName() == "push") { - const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); +void FunctionCallCompiler::tvmVectorMethods() { + ASTString const& memberName = m_memberAccess->memberName(); + if (memberName == "push") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); acceptExpr(m_arguments[0].get()); - - //start callref - m_pusher.startContinuation(); - - // vector new_el - m_pusher.startOpaque(); - m_pusher.exchange(1); - // new_el vector - m_pusher.pushS(0); - m_pusher << "TLEN"; - //new_el vector vector_len - - // if vector is not empty get last tuple - m_pusher.startContinuation(); - m_pusher << "TPOP"; - // new_el vector' last_tuple - m_pusher.pushS(0); - m_pusher << "TLEN"; - m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher << "SUB"; - - // if last tuple is full (his length is equal to 255) push it back to vector and create new tuple - m_pusher.startContinuation(); + // lValue... vector element m_pusher << "TPUSH"; - m_pusher.makeTuple(0); - m_pusher.endContinuation(); - m_pusher.ifNot(); - // new_el vector' tuple - m_pusher.endContinuation(); - - // if vector is empty create new tuple - m_pusher.startContinuation(); - m_pusher.makeTuple(0); - m_pusher.endContinuation(); - - m_pusher.ifElse(); - - // new_el vector' tuple - m_pusher.rot(); - // vector' tuple new_el - m_pusher << "TPUSH"; - // vector' tuple' - m_pusher << "TPUSH"; - // vector'' - m_pusher.endOpaque(2, 1); - - // end callref - m_pusher.pushRefContAndCallX(2, 1, false); - - m_exprCompiler.collectLValue(lValueInfo, true, false); - return true; - } - - if (_node.memberName() == "pop") { - const int stackSize = m_pusher.stackSize(); - const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); - const int stackChange = m_pusher.stackSize() - stackSize; - - //start callref - m_pusher.startContinuation(); - - // vector - m_pusher.startOpaque(); - m_pusher << "TPOP"; - // vector' last_tuple + m_exprCompiler.collectLValue(lValueInfo, true); + } else if (memberName == "pop") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); m_pusher << "TPOP"; - // [vector_expand] vector' last_tuple' last_el - - // put last el before vector and it's expand components - m_pusher.blockSwap(stackChange + 1, 1); - // last_el [vector_expand] vector' last_tuple' - - // if the last tuple is empty delete it - m_pusher.pushS(0); + // lValue... vector lastElement + m_pusher.blockSwap(lValueInfo.stackSizeDiff, 1); + m_exprCompiler.collectLValue(lValueInfo, true); + // lastElement + } else if (memberName == "length") { + acceptExpr(&m_memberAccess->expression()); m_pusher << "TLEN"; - // last_el [vector_expand] vector' last_tuple' last_tuple_len - - // if last_tuple is not empty push it back to the vector - m_pusher.startContinuation(); - m_pusher << "TPUSH"; - m_pusher.endContinuation(); - - // if last_tuple is empty drop it - m_pusher.startContinuation(); - m_pusher.drop(1); - m_pusher.endContinuation(); - - m_pusher.ifElse(); - // last_el [vector_expand] vector' - - m_pusher.endOpaque(1, 2); - - // end callref - m_pusher.pushRefContAndCallX(1, 2, false); - - m_exprCompiler.collectLValue(lValueInfo, true, false); - // last_el - return true; - } - - if (_node.memberName() == "length") { - acceptExpr(&_node.expression()); - // vector - - //start callref - m_pusher.startContinuation(); - - m_pusher.startOpaque(); - m_pusher.pushS(0); + } else if (memberName == "empty") { + acceptExpr(&m_memberAccess->expression()); m_pusher << "TLEN"; - // vector len - m_pusher.pushS(0); - // vector len len - - // if len is not zero count length of full tuples and add length of the last tuple - m_pusher.startContinuation(); - // vector len - m_pusher << "DEC"; - // vector len-- - m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher << "MUL"; - // vector full_tuples_len - m_pusher.exchange(1); + m_pusher << "EQINT 0"; + } else if (memberName == "last") { + acceptExpr(&m_memberAccess->expression()); m_pusher << "LAST"; - // full_tuples_len last_tuple - m_pusher << "TLEN"; - // full_tuples_len last_tuple_len - m_pusher << "ADD"; - m_pusher.endContinuation(); - - // if length is zero just leave it on stack - m_pusher.startContinuation(); - m_pusher.popS(1); - m_pusher.endContinuation(); - - m_pusher.ifElse(); - - m_pusher.endOpaque(1, 1, true); - - // end callref - m_pusher.pushRefContAndCallX(1, 1, false); - return true; + } else { + solUnimplemented(""); } +} - if (_node.memberName() == "empty") { - acceptExpr(&_node.expression()); - m_pusher << "TLEN"; - m_pusher << "ISZERO"; - return true; +void FunctionCallCompiler::tvmStackMethods() { + ASTString const& memberName = m_memberAccess->memberName(); + if (memberName == "push") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + acceptExpr(m_arguments[0].get()); + // lValue... stack element + m_pusher.blockSwap(1, 1); + m_pusher << "TUPLE 2"; + m_exprCompiler.collectLValue(lValueInfo, true); + } else if (memberName == "pop") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + // lValue... stack + m_pusher << "UNTUPLE 2"; // lValue... value stack + m_pusher.blockSwap(1, 1); // lValue... stack value + m_pusher.blockSwap(lValueInfo.stackSizeDiff, 1); // value lValue... stack + m_exprCompiler.collectLValue(lValueInfo, true); // value + } else if (memberName == "top") { + acceptExpr(&m_memberAccess->expression()); + m_pusher.indexWithExcep(0); + } else if (memberName == "empty") { + acceptExpr(&m_memberAccess->expression()); + m_pusher << "ISNULL"; + } else if (memberName == "sort") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + // lValue... stack + pushArgs(); // lValue... stack lessFunc + m_pusher.pushFragmentInCallRef(2, 1, "__stackSort"); // lValue... stack + m_exprCompiler.collectLValue(lValueInfo, true); // value + } else if (memberName == "reverse") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + // lValue... stack + m_pusher.pushFragmentInCallRef(1, 1, "__stackReverse"); // lValue... stack + m_exprCompiler.collectLValue(lValueInfo, true); + } else { + solUnimplemented(""); } - - return false; } void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { @@ -2022,11 +1532,7 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { cmd += isIn(memberName, "storeSigned", "storeInt") ? "I" : "U"; pushArgAndConvert(0); const auto& val = ExprUtils::constValue(*m_arguments[1]); - if (val.has_value()) { - if (val < 1 || val > 256) { - // TODO val == 0 -> ok - cast_error(*m_arguments[1], "The value must be in the range 1 - 256."); - } + if (val.has_value() && 1 <= val && val <= 256) { m_pusher << cmd + "R " + val.value().str(); } else { pushArgAndConvert(1); @@ -2081,7 +1587,7 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { m_pusher << *opcode; } - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (memberName == "bits") { acceptExpr(&_node.expression()); m_pusher << "BBITS"; @@ -2119,6 +1625,61 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { } } +void FunctionCallCompiler::qIntOrBoolMethods() { + ASTString const& memberName = m_memberAccess->memberName(); + Type const* qType = m_memberAccess->expression().annotation().type; + if (memberName == "isNaN") { + acceptExpr(&m_memberAccess->expression()); + m_pusher << "ISNAN"; + } else if (memberName == "get") { + acceptExpr(&m_memberAccess->expression()); + m_pusher.pushS(0); + m_pusher << "ISNAN"; + m_pusher._throw("THROWIF " + toString(TvmConst::RuntimeException::IsNaN)); + } else if (isIn(memberName, "getOr", "getOrDefault", "toOptional")) { + int const startSize = m_pusher.stackSize(); + acceptExpr(&m_memberAccess->expression()); + if (memberName == "getOr") + pushArgs(); // q default + else if (memberName == "getOrDefault") + m_pusher.pushDefaultValue(qType); // q default + else if (memberName == "toOptional") + m_pusher.pushNull(); // q null + else + solUnimplemented(""); + m_pusher.pushS(1); // q default q + m_pusher << "ISNAN"; // q default isNaN + m_pusher.exchange(0, 2); // isNaN default q + m_pusher << "CONDSEL"; // default | q + solAssert(startSize + 1 == m_pusher.stackSize(), ""); + } else { + solUnimplemented(""); + } +} + +void FunctionCallCompiler::stringBuilderMethods() { + ASTString const& memberName = m_memberAccess->memberName(); + if (memberName == "toString") { + acceptExpr(&m_memberAccess->expression()); + m_pusher.pushFragmentInCallRef(1, 1, "__makeString"); + } else if (memberName == "append") { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&m_memberAccess->expression(), true); + pushArgs(); + if (m_funcType->kind() == FunctionType::Kind::StringBuilderAppendByte) { + m_pusher.pushFragmentInCallRef(2, 1, "__appendBytes1"); + } else if (m_funcType->kind() == FunctionType::Kind::StringBuilderAppendByteNTimes) { + m_pusher.pushFragmentInCallRef(3, 1, "__appendBytes1NTimes"); + } else if (m_funcType->kind() == FunctionType::Kind::StringBuilderAppendString) { + m_pusher.pushFragmentInCallRef(2, 1, "__appendStringToStringBuilder"); + } else { + solUnimplemented(""); + } + m_exprCompiler.collectLValue(lValueInfo, true); + } else { + solUnimplemented(""); + } +} + void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { Type const *type = _node.expression().annotation().type; if (_node.memberName() == "empty") { @@ -2184,7 +1745,7 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { } // stack: arr value arrayPush(m_pusher, arrayBaseType, dataType); - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (_node.memberName() == "pop") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); // arr @@ -2199,12 +1760,12 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { m_pusher << "DICTUDEL"; // newSize dict ? m_pusher.drop(1); // newSize dict m_pusher << "TUPLE 2"; // arr - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (_node.memberName() == "append") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); pushArgAndConvert(0); m_pusher.pushFragmentInCallRef(2, 1, "__concatenateStrings"); - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else { solUnimplemented(""); } @@ -2234,35 +1795,51 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { } } else if (isIn(memberName, "getOr", "getOrDefault")) { int startSize = m_pusher.stackSize(); - acceptExpr(&_node.expression()); - - m_pusher.startOpaque(); - m_pusher.pushS(0); - m_pusher << "ISNULL"; - - m_pusher.startContinuation(); - m_pusher.drop(); - if (memberName == "getOr") { + acceptExpr(&_node.expression()); // opt + solAssert(startSize + 1 == m_pusher.stackSize(), ""); + if (memberName == "getOr") pushArgs(); - } else if (memberName == "getOrDefault") { + else if (memberName == "getOrDefault") + { m_pusher.pushDefaultValue(optional->valueType()); - } else { - solUnimplemented(""); + solAssert(startSize + 1 + retQty == m_pusher.stackSize(), ""); } - m_pusher.endContinuation(); + else + solUnimplemented(""); - m_pusher.startContinuation(); - if (retTuple) { - m_pusher.untuple(retTuple->components().size()); - } else if (optValueAsTuple(m_retType)) { - m_pusher.untuple(1); + // opt default... isNull + m_pusher.pushS(retQty); + m_pusher << "ISNULL"; // opt default... isNull + solAssert(startSize + 1 + retQty + 1 == m_pusher.stackSize(), ""); + if (retTuple || optValueAsTuple(m_retType)) { + m_pusher.fixStack(-1); // opt default... + m_pusher.startContinuation(); + { + // opt default + m_pusher.dropUnder(1, retQty); // default... + m_pusher.fixStack(+1); + } + m_pusher.endContinuation(); + m_pusher.startContinuation(); + { + // opt default... + m_pusher.drop(retQty); // opt + if (retTuple) + m_pusher.untuple(retTuple->components().size()); + else if (optValueAsTuple(m_retType)) + m_pusher.untuple(1); + else + solUnimplemented(""); + } + m_pusher.endContinuation(); + m_pusher.ifElse(); + solAssert(startSize + retQty == m_pusher.stackSize(), ""); + } else { + // opt default isNull + m_pusher.exchange(0, 2); // isNull default opt + m_pusher << "CONDSEL"; // default | opt + solAssert(startSize + retQty == m_pusher.stackSize(), ""); } - m_pusher.endContinuation(); - - m_pusher.ifElse(); - m_pusher.endOpaque(1, retQty); - - solAssert(startSize + retQty == m_pusher.stackSize(), ""); } else if (memberName == "set") { Type const* rightType{}; if (m_arguments.size() >= 2) { @@ -2277,11 +1854,11 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), false); pushArgs(false, false); m_pusher.convert(getType(&_node.expression()), rightType); - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else if (memberName == "reset") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), false); m_pusher.pushDefaultValue(optional); - m_exprCompiler.collectLValue(lValueInfo, true, false); + m_exprCompiler.collectLValue(lValueInfo, true); } else { return false; } @@ -2456,7 +2033,7 @@ void FunctionCallCompiler::addressMethod() { setAppendStateInit(m_arguments.at(5).get()); } } - m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, false, 0, appendStateInit); + m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag, appendStateInit); } else if (m_memberAccess->memberName() == "isStdZero") { acceptExpr(&m_memberAccess->expression()); m_pusher.pushZeroAddress(); @@ -3156,18 +2733,25 @@ void FunctionCallCompiler::abiFunction() { } void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { + bool isQuiet = false; auto retTuple = to(m_retType); + if (m_retType->category() == Type::Category::Tuple) { + if (retTuple->components().at(0)->category() == Type::Category::QInteger) + isQuiet = true; + } else if (m_retType->category() == Type::Category::QInteger) + isQuiet = true; + std::string const prefix = isQuiet ? "Q" : ""; if (_node.memberName() == "max") { pushArgs(); for (int i = 0; i + 1 < static_cast(m_arguments.size()); ++i) - m_pusher << "MAX"; + m_pusher << prefix + "MAX"; } else if (_node.memberName() == "min") { pushArgs(); for (int i = 0; i + 1 < static_cast(m_arguments.size()); ++i) - m_pusher << "MIN"; + m_pusher << prefix + "MIN"; } else if (_node.memberName() == "minmax") { pushArgs(); - m_pusher << "MINMAX"; + m_pusher << prefix + "MINMAX"; } else if (isIn(_node.memberName(), "divr", "divc")) { pushArgs(); if (m_retType->category() == Type::Category::FixedPoint) { @@ -3176,20 +2760,33 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { m_pusher.exchange(1); m_pusher << "MUL" + boost::to_upper_copy(_node.memberName()); } else { - m_pusher << boost::to_upper_copy(_node.memberName()); + m_pusher << prefix + boost::to_upper_copy(_node.memberName()); } + Type const* leftType = m_arguments.at(0)->annotation().type; + Type const* rightType = m_arguments.at(1)->annotation().type; + if (!isFitUseless(leftType, rightType, m_retType, Token::Div) && + !m_pusher.ctx().ignoreIntegerOverflow()) + m_pusher.checkFit(m_retType); } else if (isIn(_node.memberName(), "muldiv", "muldivr", "muldivc")) { pushArgs(); - m_pusher << boost::to_upper_copy(_node.memberName()); - if (!m_pusher.ctx().ignoreIntegerOverflow()) { + m_pusher << prefix + boost::to_upper_copy(_node.memberName()); + if (!m_pusher.ctx().ignoreIntegerOverflow()) m_pusher.checkFit(m_retType); - } } else if (_node.memberName() == "divmod") { pushArgs(); - m_pusher << "DIVMOD"; + m_pusher << prefix + "DIVMOD"; + Type const* leftType = m_arguments.at(0)->annotation().type; + Type const* rightType = m_arguments.at(1)->annotation().type; + Type const* resType = retTuple->components().at(0); + if (!isFitUseless(leftType, rightType, resType, Token::Div) && + !m_pusher.ctx().ignoreIntegerOverflow()) { + m_pusher.blockSwap(1, 1); + m_pusher.checkFit(resType); + m_pusher.blockSwap(1, 1); + } } else if (_node.memberName() == "muldivmod") { pushArgs(); - m_pusher << "MULDIVMOD"; + m_pusher << prefix + "MULDIVMOD"; if (!m_pusher.ctx().ignoreIntegerOverflow()) { m_pusher.exchange(1); m_pusher.checkFit(retTuple->components().at(0)); @@ -3198,27 +2795,20 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { } else if (_node.memberName() == "abs") { pushArgs(); m_pusher << "ABS"; - if (!m_pusher.ctx().ignoreIntegerOverflow()) { + if (!m_pusher.ctx().ignoreIntegerOverflow()) m_pusher.checkFit(m_retType); - } } else if (_node.memberName() == "modpow2") { pushExprAndConvert(m_arguments[0].get(), m_retType); const Expression * expression = m_arguments[1].get(); const auto& value = ExprUtils::constValue(*expression); - if (value.has_value()) { - if (value < 0 || value >= 256) { - cast_error(m_functionCall, "Second argument must be in the range 1 - 255."); - } - m_pusher << "MODPOW2 " + value->str(); - } else { - cast_error(m_functionCall, "Second argument must be a constant integer."); - } + if (!value.has_value() || value < 0 || value >= 256) + cast_error(*expression, "Expected a constant integer in the range 1 - 255."); + m_pusher << prefix + "MODPOW2 " + value->str(); } else if (_node.memberName() == "sign") { pushArgs(); - m_pusher << "SGN"; - } else { + m_pusher << prefix + "SGN"; + } else cast_error(m_functionCall, "Unsupported function call"); - } } bool FunctionCallCompiler::checkBaseContractCall(MemberAccess const &_node) { @@ -3277,7 +2867,8 @@ void FunctionCallCompiler::typeConversion() { auto getDigits = [](Type const* type)-> int { if (auto fix = to(type)) return fix->fractionalDigits(); - if (isIn(type->category(), Type::Category::Integer, Type::Category::VarInteger, Type::Category::Enum)) + if (isIn(type->category(), Type::Category::Integer, Type::Category::QInteger, + Type::Category::VarInteger, Type::Category::Enum)) return 0; solUnimplemented(""); }; @@ -3300,6 +2891,7 @@ void FunctionCallCompiler::typeConversion() { case Type::Category::Enum: case Type::Category::FixedPoint: case Type::Category::Integer: + case Type::Category::QInteger: case Type::Category::VarInteger: if (argType->category() == Type::Category::FixedBytes) { // do nothing @@ -3411,7 +3003,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { constParams, nullptr, [&]() { m_pusher << "PUSHINT " + toString(TvmConst::SENDRAWMSG::SelfDestruct); }, - false, 0, nullptr); + nullptr); return true; } @@ -3546,7 +3138,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { format.erase(format.size() - 1, 1); } int width = 0; - if (format.length() > 0) + if (!format.empty()) width = std::stoi(format); if (width < 0 || width > 127) cast_error(m_functionCall, "Width should be in range of 0 to 127."); @@ -3575,7 +3167,6 @@ bool FunctionCallCompiler::checkSolidityUnits() { m_pusher.pushFragmentInCallRef(2, 1, "__convertAddressToHexString"); } else if (isStringOrStringLiteralOrBytes(argType)) { acceptExpr(m_arguments[it + 1].get()); - m_pusher << "CTOS"; m_pusher.pushFragmentInCallRef(2, 1, "__appendStringToStringBuilder"); } else if (cat == Type::Category::FixedPoint) { int power = to(argType)->fractionalDigits(); diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.hpp b/compiler/libsolidity/codegen/TVMFunctionCall.hpp index 8da3b789..30270baf 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.hpp @@ -46,11 +46,10 @@ class FunctionCallCompiler { void superFunctionCall(MemberAccess const& _node); void userDefinedValueMethods(MemberAccess const& _node); void addressMethods(MemberAccess const& _node); - bool libraryCall(MemberAccess const& ma); + bool libraryCall(); bool checkTvmABIDeployMethods(Type::Category category); void abiBuildIntMsg(); void abiBuildDataInit(); - void abiBuildExtMsg(); void abiDecodeData(); int decodeData(); int decodeFunctionParams(); @@ -58,7 +57,10 @@ class FunctionCallCompiler { void arrayMethods(MemberAccess const& _node); bool checkForOptionalMethods(MemberAccess const& _node); void builderMethods(MemberAccess const& _node); - bool checkForTvmVectorMethods(MemberAccess const& _node, Type::Category category); + void qIntOrBoolMethods(); + void stringBuilderMethods(); + void tvmVectorMethods(); + void tvmStackMethods(); void cellMethods(MemberAccess const& _node); void integerMethods(); void variantMethods(MemberAccess const& _node); @@ -114,26 +116,8 @@ class FunctionCallCompiler { ContractType const* ct ); bool checkRemoteMethodCall(FunctionCall const &_functionCall); - void checkExtMsgSend(); std::string getDefaultMsgValue(); static const FunctionDefinition* getRemoteFunctionDefinition(const MemberAccess* memberAccess); - void acceptExprOrPushFunctionId(Expression const* onerrorid); - void generateExtInboundMsg( - bool addSignature, - const Expression * destination, - const Expression *pubkey, - const Expression *expire, - const Expression *time, - const Expression *callbackid, - const Expression *onerrorid, - const Expression *stateInit, - const Expression *signBoxHandle, - const Expression *abiVer, - const Expression *flags, - const CallableDeclaration *functionDefinition, - const ast_vec& arguments - ); - void pushArgs(bool reversed = false, bool doConvert = true); void pushArgAndConvert(int index, const std::string& name = ""); diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp index 652c9e9c..adf0ca28 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp @@ -20,15 +20,15 @@ #include -#include "DictOperations.hpp" -#include "TVMABI.hpp" -#include "TVMAnalyzer.hpp" -#include "TVMConstants.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMFunctionCall.hpp" -#include "TVMFunctionCompiler.hpp" -#include "TVMStructCompiler.hpp" -#include "TVM.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace solidity::langutil; @@ -154,10 +154,6 @@ TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { pusher << "LDU 64 ; pubkey timestamp c4"; } pusher << "LDU 1 ; ctor flag"; - if (pusher.ctx().usage().hasAwaitCall()) { - pusher << "LDI 1 ; await flag"; - pusher.dropUnder(1, 1); - } pusher.getStack().change(+1); // slice // slice on stack @@ -170,7 +166,7 @@ TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { const int ss = pusher.stackSize(); ChainDataDecoder decoder{&pusher}; decoder.decodeData(pusher.ctx().getOffsetC4(), - pusher.ctx().usage().hasAwaitCall() ? 1 : 0, + 0, stateVarTypes); int const varQty = stateVarTypes.size(); @@ -222,13 +218,10 @@ Pointer TVMFunctionCompiler::generateDefaultC4(TVMCompilerContext& ctx if (ctx.storeTimestampInC4()) pusher << "STU 64"; pusher << "STZERO"; // constructor flag - if (ctx.usage().hasAwaitCall()) { - pusher << "STZERO"; - } const std::vector& memberTypes = ctx.c4StateVariableTypes(); if (!memberTypes.empty()) { ChainDataEncoder encoder{&pusher}; - DecodePositionAbiV2 position{ctx.getOffsetC4(), ctx.usage().hasAwaitCall() ? 1 : 0, memberTypes}; + DecodePositionAbiV2 position{ctx.getOffsetC4(), 0, memberTypes}; encoder.encodeParameters(memberTypes, position); } pusher << "ENDC"; @@ -477,10 +470,9 @@ TVMFunctionCompiler::generatePublicFunctionSelector(TVMCompilerContext& ctx, Con Pointer TVMFunctionCompiler::generateLibFunctionWithObject( TVMCompilerContext& ctx, - FunctionDefinition const* function + FunctionDefinition const* function, + std::string const& name ) { - bool const withObject = true; - const std::string name = TVMCompilerContext::getLibFunctionName(function, withObject); ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, 0, function, true, true, 0}; @@ -1774,13 +1766,10 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin // stack: int_msg_info ContactsUsageScanner const &sc = pusher.ctx().usage(); - if (sc.hasMsgSender() || sc.hasResponsibleFunction() || sc.hasAwaitCall()) { + if (sc.hasMsgSender() || sc.hasResponsibleFunction()) { pusher << "LDU 4 ; bounced tail"; pusher << "LDMSGADDR ; bounced src tail"; pusher.drop(); - if (sc.hasAwaitCall()) { - pusher.pushFragmentInCallRef(0, 0, "check_resume"); - } pusher.setGlob(TvmConst::C7::SenderAddress); pusher << "MODPOW2 1"; } else { @@ -1824,52 +1813,6 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin return createNode(0, 0, name, nullopt, Function::FunctionType::MainInternal, pusher.getBlock()); } -Pointer TVMFunctionCompiler::generateCheckResume(TVMCompilerContext& ctx) { - std::string const name = "check_resume"; - ctx.setCurrentFunction(nullptr, name); - StackPusher pusher{&ctx}; - // TODO: unite check resume and c4_to_c7 for not to parse c4 2 times - std::string code = R"(PUSHROOT -CTOS -PUSHINT offset -LDSLICEX ; beg_slice end_slice -LDI 1 -SWAP -PUSHCONT { - LDREFRTOS ; beg_slice end_slice ref_slice - XCHG S2 ; ref_slice end beg - NEWC - STSLICE - STZERO - STSLICE - ENDC - POPROOT - LDMSGADDR - ROTREV - SDEQ - THROWIFNOT TvmConst::RuntimeException::WrongAwaitAddress - LDCONT - DROP - NIP - CALLREF { - .inline c4_to_c7 - } - CALLX -} -PUSHCONT { - DROP2 -} -IFELSE -)"; - solAssert(!ctx.callGraph().tryToAddEdge(ctx.currentFunctionName(), "c4_to_c7"), ""); - boost::replace_all(code, "TvmConst::RuntimeException::WrongAwaitAddress", toString(TvmConst::RuntimeException::WrongAwaitAddress)); - boost::replace_all(code, "offset", toString(256 + (pusher.ctx().storeTimestampInC4() ? 64 : 0) + 1)); - vector lines = split(code); - pusher.push(createNode(lines, 0, 0, false)); - ctx.resetCurrentFunction(); - return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); -} - bool TVMFunctionCompiler::visit(PlaceholderStatement const &) { TVMFunctionCompiler funCompiler{m_pusher, m_currentModifier + 1, m_function, m_isLibraryWithObj, m_pushArgs, m_pusher.stackSize()}; funCompiler.visitFunctionWithModifiers(); diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp index 2615f4c5..6b55a0c5 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp @@ -46,10 +46,10 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable static Pointer generateFunction(TVMCompilerContext& ctx, FunctionDefinition const* function, std::string const& name); static Pointer generateMainExternal(TVMCompilerContext& ctx, ContractDefinition const *contract); static Pointer generateMainInternal(TVMCompilerContext& ctx, ContractDefinition const *contract); - static Pointer generateCheckResume(TVMCompilerContext& ctx); static Pointer generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefinition const* function); static Pointer generateOnTickTock(TVMCompilerContext& ctx, FunctionDefinition const* function); - static Pointer generateLibFunctionWithObject(TVMCompilerContext& ctx, FunctionDefinition const* function); + static Pointer generateLibFunctionWithObject(TVMCompilerContext& ctx, FunctionDefinition const* function, + std::string const& name); static Pointer generateReceive(TVMCompilerContext& ctx, FunctionDefinition const* function); static Pointer generateFallback(TVMCompilerContext& ctx, FunctionDefinition const* function); static Pointer generateOnBounce(TVMCompilerContext& ctx, FunctionDefinition const* function); diff --git a/compiler/libsolidity/codegen/TVMInlineFunctionChecker.cpp b/compiler/libsolidity/codegen/TVMInlineFunctionChecker.cpp index 1da6cebe..7ff0240f 100644 --- a/compiler/libsolidity/codegen/TVMInlineFunctionChecker.cpp +++ b/compiler/libsolidity/codegen/TVMInlineFunctionChecker.cpp @@ -13,8 +13,8 @@ #include -#include "TVMInlineFunctionChecker.hpp" -#include "TVMCommons.hpp" +#include +#include using namespace solidity::frontend; diff --git a/compiler/libsolidity/codegen/TVMPusher.cpp b/compiler/libsolidity/codegen/TVMPusher.cpp index 4e5c9548..e6011cf6 100644 --- a/compiler/libsolidity/codegen/TVMPusher.cpp +++ b/compiler/libsolidity/codegen/TVMPusher.cpp @@ -11,18 +11,18 @@ * See the GNU General Public License for more details at: https://www.gnu.org/licenses/gpl-3.0.html */ -#include - -#include "DictOperations.hpp" -#include "TVMPusher.hpp" -#include "TVMExpressionCompiler.hpp" -#include "TVMStructCompiler.hpp" -#include "TVMABI.hpp" -#include "TVMConstants.hpp" - #include #include +#include + +#include +#include +#include +#include +#include +#include + using namespace solidity::frontend; using namespace solidity::util; using namespace solidity; @@ -62,7 +62,7 @@ void StackPusher::pushLog() { } // TODO move to function compiler -Pointer StackPusher::generateC7ToC4(bool forAwait) { +Pointer StackPusher::generateC7ToC4() { const std::vector& memberTypes = m_ctx->c4StateVariableTypes(); const int stateVarQty = memberTypes.size(); if (ctx().tooMuchStateVariables()) { @@ -87,81 +87,16 @@ Pointer StackPusher::generateC7ToC4(bool forAwait) { *this << "STU 64"; } *this << "STONE"; // constructor flag - if (forAwait) { - *this << "STONE"; // remoteAddr stateVars... b - blockSwap(1, stateVarQty + 1); // stateVars... b remoteAddr - push(createNode(std::vector{ - "NEWC", // stateVars... b remoteAddr B - "STSLICE", // stateVars... b B - "PUSH c0", - "PUSH c3", - "PUSHCONT {", - " SETCONT c3", - " SETCONT c0", - // 5SysVars... funcStack... stateVars... b B cont - " PUSHINT " + toString(stateVarQty), - " ADDCONST 2", - " PUSHINT 1", - " BLKSWX", - // 5SysVars... funcStack... cont stateVars... b B - " DEPTH", - " ADDCONST -7", // 5 sys vars + 2 builders - " PUSHINT " + toString(stateVarQty), // and stateVars - " SUB", - " PUSHINT 2", - " PUSHINT " + toString(stateVarQty), - " ADD", - " BLKSWX", - // 5SysVars... stateVars... b B funcStack... cont - " GETGLOB " + toString(TvmConst::C7::MsgPubkey), - " GETGLOB " + toString(TvmConst::C7::SenderAddress), - " GETGLOB " + toString(TvmConst::C7::AwaitAnswerId), - // 5SysVars... stateVars... b B funcStack... cont msgPubKey senderAddr AwaitAnswerId - " BLKSWAP 1, 3", - // 5SysVars... stateVars... b B funcStack... msgPubKey senderAddr AwaitAnswerId cont - " DEPTH", - " ADDCONST -8", // 5 sys vars + 2 builders + cont - " PUSHINT " + toString(stateVarQty), // and stateVars - " SUB", - " PUSHINT -1", - " SETCONTVARARGS", - // 5SysVars... stateVars... b B cont - " SWAP", - // 5SysVars... stateVars... b cont B - " STCONT", - // 5SysVars... stateVars... b B - " ENDC", - // 5SysVars... stateVars... b suspendedCodeCell - " STREFR", - // 5SysVars... stateVars... b - }, 0, 0, false)); - } else { - if (m_ctx->usage().hasAwaitCall()) { - *this << "STZERO"; - } - } if (!memberTypes.empty()) { ChainDataEncoder encoder{this}; - DecodePositionAbiV2 position{m_ctx->getOffsetC4(), m_ctx->usage().hasAwaitCall() ? 1 : 0, memberTypes}; + DecodePositionAbiV2 position{m_ctx->getOffsetC4(), 0, memberTypes}; encoder.encodeParameters(memberTypes, position); } - if (forAwait) { - push(createNode(std::vector{ - // 5SysVars... b - " ENDC", - " POPROOT", - " THROW 0", - "}", - "CALLCC", - }, 0, 0, false)); - } else { - *this << "ENDC"; - popRoot(); - } + *this << "ENDC"; + popRoot(); Pointer block = getBlock(); - auto f = createNode(0, 0, (forAwait ? "c7_to_c4_for_await" : "c7_to_c4"), nullopt, Function::FunctionType::Fragment, - block); + auto f = createNode(0, 0, "c7_to_c4", nullopt, Function::FunctionType::Fragment, block); return f; } @@ -1098,141 +1033,147 @@ void StackPusher::pushInt(const bigint& i) { bool StackPusher::fastLoad(const Type* type) { // slice switch (type->category()) { - case Type::Category::Optional: { - auto optType = to(type); - auto optValueType = optType->valueType(); - auto array = to(optType->valueType()); - if (optValueType->category() == Type::Category::TvmCell || (array && array->isByteArrayOrString())) { - *this << "LDDICT"; - } else { - startOpaque(); - const int saveStackSize = stackSize(); - auto opt = to(type); + case Type::Category::Optional: { + auto optType = to(type); + auto optValueType = optType->valueType(); + auto array = to(optType->valueType()); + if (optValueType->category() == Type::Category::TvmCell || (array && array->isByteArrayOrString())) { + *this << "LDDICT"; + } else { + startOpaque(); + const int saveStackSize = stackSize(); + auto opt = to(type); - auto f = [&](bool reverseOrder) { - if (isSmallOptional(opt)) { - load(opt->valueType(), reverseOrder); + auto f = [&](bool reverseOrder) { + if (isSmallOptional(opt)) { + load(opt->valueType(), reverseOrder); + } else { + *this << "LDREFRTOS"; + std::unique_ptr sc; + if (auto st = to(opt->valueType())) { + sc = std::make_unique(this, st); + } else if (auto tt = to(opt->valueType())) { + sc = std::make_unique(this, tt); } else { - *this << "LDREFRTOS"; - std::unique_ptr sc; - if (auto st = to(opt->valueType())) { - sc = std::make_unique(this, st); - } else if (auto tt = to(opt->valueType())) { - sc = std::make_unique(this, tt); - } else { - solUnimplemented(""); - } - sc->convertSliceToTuple(); - if (!reverseOrder) { - exchange(1); - } + solUnimplemented(""); + } + sc->convertSliceToTuple(); + if (!reverseOrder) { + exchange(1); } - }; - - *this << "LDI 1"; // hasValue slice - exchange(1); // slice hasValue - fixStack(-1); // fix stack - - startContinuation(); - if (optValueAsTuple(opt->valueType())) { - f(true); - makeTuple(1); - exchange(1); - } else { - f(false); - } - endContinuation(); - fixStack(-1); // fix stack - if (!hasLock()) { - solAssert(saveStackSize == stackSize(), ""); } + }; - startContinuation(); - pushNull(); + *this << "LDI 1"; // hasValue slice + exchange(1); // slice hasValue + fixStack(-1); // fix stack + + startContinuation(); + if (optValueAsTuple(opt->valueType())) { + f(true); + makeTuple(1); exchange(1); - endContinuation(); - fixStack(-1); // fix stack - if (!hasLock()) { - solAssert(saveStackSize == stackSize(), ""); - } + } else { + f(false); + } + endContinuation(); + fixStack(-1); // fix stack + if (!hasLock()) { + solAssert(saveStackSize == stackSize(), ""); + } - ifElse(); - fixStack(+1); // fix stack - if (!hasLock()) { - solAssert(saveStackSize + 1 == stackSize(), ""); - } - endOpaque(1, 2); + startContinuation(); + pushNull(); + exchange(1); + endContinuation(); + fixStack(-1); // fix stack + if (!hasLock()) { + solAssert(saveStackSize == stackSize(), ""); } - return true; - } - case Type::Category::Tuple: { - auto tup = to(type); - for (auto t : tup->components()) { - load(t, false); + + ifElse(); + fixStack(+1); // fix stack + if (!hasLock()) { + solAssert(saveStackSize + 1 == stackSize(), ""); } - blockSwap(tup->components().size(), 1); - makeTuple(tup->components().size()); - return false; + endOpaque(1, 2); } - case Type::Category::TvmCell: - *this << "LDREF"; - return true; - case Type::Category::Struct: { - auto st = to(type); - std::vector> const& members = st->structDefinition().members(); - for (const ASTPointer& t : members) { - load(t->type(), false); - } - blockSwap(members.size(), 1); - makeTuple(members.size()); - exchange(1); - return true; + return true; + } + case Type::Category::Tuple: { + auto tup = to(type); + for (auto t : tup->components()) { + load(t, false); } - case Type::Category::Address: - case Type::Category::Contract: - *this << "LDMSGADDR"; - return true; - case Type::Category::Enum: - case Type::Category::Integer: - case Type::Category::Bool: - case Type::Category::FixedPoint: - case Type::Category::FixedBytes: { - TypeInfo ti{type}; - solAssert(ti.isNumeric, ""); + blockSwap(tup->components().size(), 1); + makeTuple(tup->components().size()); + return false; + } + case Type::Category::TvmCell: + *this << "LDREF"; + return true; + case Type::Category::Struct: { + auto st = to(type); + std::vector> const& members = st->structDefinition().members(); + for (const ASTPointer& t : members) { + load(t->type(), false); + } + blockSwap(members.size(), 1); + makeTuple(members.size()); + exchange(1); + return true; + } + case Type::Category::Address: + case Type::Category::Contract: + *this << "LDMSGADDR"; + return true; + case Type::Category::Enum: + case Type::Category::Integer: + case Type::Category::Bool: + case Type::Category::FixedPoint: + case Type::Category::FixedBytes: { + TypeInfo ti{type}; + solAssert(ti.isNumeric, ""); + if (ti.numBits == 257) { + solAssert(ti.isSigned, ""); + pushInt(ti.numBits); + *this << "LDIX"; + } else { string cmd = ti.isSigned ? "LDI " : "LDU "; *this << cmd + toString(ti.numBits); - return true; } - case Type::Category::Function: { - *this << "LDU 32"; + return true; + } + case Type::Category::Function: { + *this << "LDU 32"; + return true; + } + case Type::Category::Array: { + auto arrayType = to(type); + if (arrayType->isByteArrayOrString()) { + *this << "LDREF"; return true; - } - case Type::Category::Array: { - auto arrayType = to(type); - if (arrayType->isByteArrayOrString()) { - *this << "LDREF"; - return true; - } else { - *this << "LDU 32"; - *this << "LDDICT"; - rotRev(); - *this << "TUPLE 2"; - return false; - } - } - case Type::Category::Mapping: + } else { + *this << "LDU 32"; *this << "LDDICT"; - return true; - case Type::Category::VarInteger: { - auto varInt = to(type); - std::string cmd = "LDVAR"; - if (!varInt->asIntegerType().isSigned()) cmd += "U"; - cmd += "INT" + std::to_string(varInt->n()); - *this << cmd; - return true; + rotRev(); + *this << "TUPLE 2"; + return false; } - default: - solUnimplemented(type->toString()); + } + case Type::Category::Mapping: + *this << "LDDICT"; + return true; + case Type::Category::VarInteger: { + auto varint = to(type); + std::string cmd = "LDVAR"; + if (!varint->asIntegerType().isSigned()) cmd += "U"; + cmd += "INT" + std::to_string(varint->n()); + *this << cmd; + return true; + } + default: + solUnimplemented(type->toString()); } solUnimplemented(""); // true => value slice @@ -1252,67 +1193,73 @@ void StackPusher::preload(const Type *type) { const int stackSize = this->stackSize(); // on stack there is slice switch (type->category()) { - case Type::Category::Optional: { - load(type, false); - drop(); - break; - } - case Type::Category::Address: - case Type::Category::Contract: - *this << "LDMSGADDR"; - drop(1); - break; - case Type::Category::TvmCell: - *this << "PLDREF"; - break; - case Type::Category::Struct: { - auto structType = to(type); - StructCompiler sc{this, structType}; - sc.convertSliceToTuple(); - break; - } - case Type::Category::Integer: - case Type::Category::Enum: - case Type::Category::Bool: - case Type::Category::FixedPoint: - case Type::Category::FixedBytes: { - TypeInfo ti{type}; - solAssert(ti.isNumeric, ""); + case Type::Category::Optional: { + load(type, false); + drop(); + break; + } + case Type::Category::Address: + case Type::Category::Contract: + *this << "LDMSGADDR"; + drop(1); + break; + case Type::Category::TvmCell: + *this << "PLDREF"; + break; + case Type::Category::Struct: { + auto structType = to(type); + StructCompiler sc{this, structType}; + sc.convertSliceToTuple(); + break; + } + case Type::Category::Integer: + case Type::Category::Enum: + case Type::Category::Bool: + case Type::Category::FixedPoint: + case Type::Category::FixedBytes: { + TypeInfo ti{type}; + solAssert(ti.isNumeric, ""); + if (ti.numBits == 257) { + solAssert(ti.isSigned, ""); + pushInt(ti.numBits); + *this << "PLDIX"; + } else { string cmd = ti.isSigned ? "PLDI " : "PLDU "; *this << cmd + toString(ti.numBits); - break; - } - case Type::Category::Function: { - *this << "PLDU 32"; - break; - } - case Type::Category::Array: { - auto arrayType = to(type); - if (arrayType->isByteArrayOrString()) { - *this << "PLDREF"; - } else { - *this << "LDU 32"; - *this << "PLDDICT"; - *this << "TUPLE 2"; - // stack: array - } - break; } - case Type::Category::Mapping: + break; + } + case Type::Category::Function: { + *this << "PLDU 32"; + break; + } + case Type::Category::Array: { + auto arrayType = to(type); + if (arrayType->isByteArrayOrString()) { + *this << "PLDREF"; + } else { + *this << "LDU 32"; *this << "PLDDICT"; - break; - case Type::Category::VarInteger: - load(type, false); - drop(); - break; - case Type::Category::Tuple: { - const auto[types, names] = getTupleTypes(to(type)); - StructCompiler sc{this, types, names}; - sc.convertSliceToTuple(); - break; + *this << "TUPLE 2"; + // stack: array } - default: - solUnimplemented("Decode isn't supported for " + type->toString(true)); + break; + } + case Type::Category::Mapping: + *this << "PLDDICT"; + break; + case Type::Category::VarInteger: + load(type, false); + drop(); + break; + case Type::Category::Tuple: { + const auto[types, names] = getTupleTypes(to(type)); + StructCompiler sc{this, types, names}; + sc.convertSliceToTuple(); + break; + } + default: + solUnimplemented("Decode isn't supported for " + type->toString(true)); } ensureSize(stackSize); } @@ -1517,11 +1464,18 @@ void StackPusher::store( case Type::Category::FixedPoint: { TypeInfo ti(type); solAssert(ti.isNumeric, ""); - string cmd = ti.isSigned? "STI" : "STU"; - if (reverse) cmd += "R"; - cmd += " " + toString(ti.numBits); - *this << cmd; - solAssert(ti.numBits != 267, ""); + if (ti.numBits == 257) { + solAssert(ti.isSigned, ""); + pushInt(ti.numBits); + string cmd = "STIX"; + if (reverse) cmd += "R"; + *this << cmd; + } else { + string cmd = ti.isSigned? "STI" : "STU"; + if (reverse) cmd += "R"; + cmd += " " + toString(ti.numBits); + *this << cmd; + } break; } case Type::Category::Function: { @@ -1567,10 +1521,10 @@ void StackPusher::store( if (!reverse) exchange(1); // builder value - auto varInt = to(type); + auto varint = to(type); std::string cmd = "STVAR"; - if (!varInt->asIntegerType().isSigned()) cmd += "U"; - cmd += "INT" + std::to_string(varInt->n()); + if (!varint->asIntegerType().isSigned()) cmd += "U"; + cmd += "INT" + std::to_string(varint->n()); *this << cmd; break; } @@ -1602,6 +1556,16 @@ void StackPusher::checkFit(Type const *type) { *this << "UFITS " + toString(it->numBits()); break; } + case Type::Category::QInteger: { + auto it2 = to(type); + auto it = it2->asIntegerType(); + if (it->isSigned()) { + if (it->numBits() != 257) + *this << "QFITS " + toString(it->numBits()); + } else + *this << "QUFITS " + toString(it->numBits()); + break; + } case Type::Category::FixedPoint: { auto fp = to(type); if (fp->isSigned()) @@ -1611,8 +1575,8 @@ void StackPusher::checkFit(Type const *type) { break; } case Type::Category::VarInteger: { - auto varInt = to(type); - checkFit(&varInt->asIntegerType()); + auto varint = to(type); + checkFit(&varint->asIntegerType()); break; } case Type::Category::Enum: { @@ -1699,9 +1663,8 @@ void StackPusher::drop(int cnt) { void StackPusher::blockSwap(int down, int up) { solAssert(0 <= down, ""); solAssert(0 <= up, ""); - if (down == 0 || up == 0) { + if (down == 0 || up == 0) return; - } push(createNode(Stack::Opcode::BLKSWAP, down, up)); } @@ -1731,6 +1694,11 @@ void StackPusher::exchange(int i) { push(opcode); } +void StackPusher::exchange(int i, int j) { + Pointer opcode = makeXCH_S_S(i, j); + push(opcode); +} + void StackPusher::rot() { push(makeROT()); } @@ -1931,25 +1899,17 @@ void StackPusher::sendrawmsg() { *this << "SENDRAWMSG"; } -void StackPusher::sendIntMsg(const std::map &exprs, - const std::map &constParams, - const std::function &appendBody, - const std::function &pushSendrawmsgFlag, - bool isAwait, - size_t callParamsOnStack, - const std::function &appendStateInit) { +void StackPusher::sendIntMsg( + const std::map &exprs, + const std::map &constParams, + const std::function &appendBody, + const std::function &pushSendrawmsgFlag, + const std::function &appendStateInit +) { std::set isParamOnStack; - size_t pushedValCnt = 0; for (auto &[param, expr] : exprs | boost::adaptors::reversed) { isParamOnStack.insert(param); TVMExpressionCompiler{*this}.compileNewExpr(expr); - if (param != TvmConst::int_msg_info::dest) - ++pushedValCnt; - else if (isAwait) { - pushS(0); - ++pushedValCnt; - blockSwap(pushedValCnt + callParamsOnStack, 1); - } } sendMsg(isParamOnStack, constParams, appendBody, appendStateInit, pushSendrawmsgFlag); } @@ -2211,7 +2171,7 @@ string TVMCompilerContext::getFunctionInternalName(FunctionDefinition const* _fu else if (calledByPoint && isBaseFunction(_function)) functionName = _function->annotation().contract->name() + "_" + _function->name() + "_" + hexName; else if (_function->isFree()) - functionName = _function->name() + "_" + hexName + "_free_internal"; + functionName = (calledByPoint ? "with_obj_" : "") + _function->name() + "_" + hexName + "_free_internal"; else functionName = _function->name() + "_" + hexName + "_internal"; return functionName; @@ -2266,8 +2226,7 @@ int TVMCompilerContext::getOffsetC4() const { return 256 + // pubkey (storeTimestampInC4() ? 64 : 0) + - 1 + // constructor flag - (m_usage.hasAwaitCall() ? 1 : 0); + 1; // constructor flag } std::vector> TVMCompilerContext::getStaticVariables() const { @@ -2313,76 +2272,90 @@ void StackPusher::pushNull() { *this << "NULL"; } +void StackPusher::pushNaN() { + *this << "PUSHNAN"; +} + void StackPusher::pushEmptyCell() { pushCellOrSlice(createNode(PushCellOrSlice::Type::PUSHREF, "", nullptr)); } void StackPusher::pushDefaultValue(Type const* _type) { + int returnValues = 1; startOpaque(); Type::Category cat = _type->category(); switch (cat) { - case Type::Category::Address: - case Type::Category::Contract: - pushSlice("x2_"); // addr_none$00 = MsgAddressExt; - break; - case Type::Category::Bool: - case Type::Category::FixedBytes: - case Type::Category::Integer: - case Type::Category::Enum: - case Type::Category::VarInteger: - case Type::Category::FixedPoint: - *this << "PUSHINT 0"; - break; - case Type::Category::Array: - case Type::Category::TvmCell: - if (cat == Type::Category::TvmCell || to(_type)->isByteArrayOrString()) { - pushEmptyCell(); - break; - } - pushEmptyArray(); - break; - case Type::Category::Mapping: - *this << "NEWDICT"; - break; - case Type::Category::Struct: { - auto structType = to(_type); - StructCompiler structCompiler{this, structType}; - structCompiler.createDefaultStruct(); - break; - } - case Type::Category::TvmSlice: - pushSlice("x8_"); - break; - case Type::Category::TvmBuilder: - *this << "NEWC"; - break; - case Type::Category::Function: { - pushInt(TvmConst::FunctionId::DefaultValueForFunctionType); - break; - } - case Type::Category::Optional: - case Type::Category::Variant: - pushNull(); - break; - case Type::Category::TvmVector: - makeTuple(0); - break; - case Type::Category::UserDefinedValueType: { - auto userDefValue = to(_type); - pushDefaultValue(&userDefValue->underlyingType()); - break; - } - case Type::Category::Tuple: { - auto tuple = to(_type); - for (Type const* comp : tuple->components()) { - pushDefaultValue(comp); - } + case Type::Category::Address: + case Type::Category::Contract: + pushSlice("x2_"); // addr_none$00 = MsgAddressExt; + break; + case Type::Category::Bool: + case Type::Category::QBool: + case Type::Category::FixedBytes: + case Type::Category::Integer: + case Type::Category::Enum: + case Type::Category::VarInteger: + case Type::Category::QInteger: + case Type::Category::FixedPoint: + *this << "PUSHINT 0"; + break; + case Type::Category::Array: + case Type::Category::TvmCell: + if (cat == Type::Category::TvmCell || to(_type)->isByteArrayOrString()) { + pushEmptyCell(); break; } - default: - solUnimplemented(""); + pushEmptyArray(); + break; + case Type::Category::Mapping: + *this << "NEWDICT"; + break; + case Type::Category::Struct: { + auto structType = to(_type); + StructCompiler structCompiler{this, structType}; + structCompiler.createDefaultStruct(); + break; } - endOpaque(0, 1, true); + case Type::Category::TvmSlice: + pushSlice("x8_"); + break; + case Type::Category::TvmBuilder: + *this << "NEWC"; + break; + case Type::Category::Function: { + pushInt(TvmConst::FunctionId::DefaultValueForFunctionType); + break; + } + case Type::Category::Optional: + case Type::Category::Variant: + pushNull(); + break; + case Type::Category::TvmVector: + makeTuple(0); + break; + case Type::Category::TvmStack: + pushNull(); + break; + case Type::Category::UserDefinedValueType: { + auto userDefValue = to(_type); + pushDefaultValue(&userDefValue->underlyingType()); + break; + } + case Type::Category::Tuple: { + auto tuple = to(_type); + for (Type const* comp : tuple->components()) + pushDefaultValue(comp); + returnValues = tuple->components().size(); + break; + } + case Type::Category::StringBuilder: { + pushFragment(0, 1, "__createStringBuilder"); + break; + } + default: + solUnimplemented(""); + } + endOpaque(0, returnValues, true); } void StackPusher::getDict( @@ -2450,6 +2423,8 @@ void StackPusher::takeLast(int n) { } void TypeConversion::convert(Type const* leftType, Type const* rightType) { + // TODO separate implicit conversion and explicit conversion + // opt(opt(opt(opt(T)))) = T; // opt(opt(opt(opt(T0, T1, T2)))) = (T0, T1, T2); int lQty = optTypeQty(leftType); @@ -2505,15 +2480,19 @@ void TypeConversion::convert(Type const* leftType, Type const* rightType) { case Type::Category::Address: case Type::Category::Bool: case Type::Category::Contract: + case Type::Category::EmptyMap: case Type::Category::Enum: case Type::Category::Function: case Type::Category::Mapping: - case Type::Category::TvmVector: + case Type::Category::Null: + case Type::Category::QBool: + case Type::Category::QInteger: case Type::Category::Struct: + case Type::Category::TVMNaN: case Type::Category::TvmBuilder: case Type::Category::TvmCell: - case Type::Category::Null: - case Type::Category::EmpyMap: + case Type::Category::TvmStack: + case Type::Category::TvmVector: case Type::Category::UserDefinedValueType: break; default: @@ -2523,11 +2502,7 @@ void TypeConversion::convert(Type const* leftType, Type const* rightType) { } void TypeConversion::integerToInteger(IntegerType const* leftType, IntegerType const* rightType) { - if (rightType->isImplicitlyConvertibleTo(*leftType) || leftType->numBits() == 257) - return ; - - bool canConvert = leftType->numBits() > rightType->numBits() && leftType->isSigned() && !rightType->isSigned(); - if (canConvert) + if (rightType->isImplicitlyConvertibleTo(*leftType)) return ; bigint x = (bigint(1) << leftType->numBits()) - 1; @@ -2694,6 +2669,9 @@ void TypeConversion::fromInteger(Type const* leftType, IntegerType const* rightT case Type::Category::VarInteger: integerToInteger(&to(leftType)->asIntegerType(), rightType); break; + case Type::Category::QInteger: + integerToInteger(to(leftType)->asIntegerType(), rightType); + break; case Type::Category::Function: { m_pusher.ctx().setPragmaSaveAllFunctions(); break; @@ -2729,6 +2707,7 @@ void TypeConversion::fromRational(Type const* leftType, RationalNumberType const break; } case Type::Category::Integer: + case Type::Category::QInteger: case Type::Category::VarInteger: break; case Type::Category::Function: { diff --git a/compiler/libsolidity/codegen/TVMPusher.hpp b/compiler/libsolidity/codegen/TVMPusher.hpp index a3237ab5..4e32806c 100644 --- a/compiler/libsolidity/codegen/TVMPusher.hpp +++ b/compiler/libsolidity/codegen/TVMPusher.hpp @@ -21,9 +21,9 @@ #include #include -#include "TVMCommons.hpp" -#include "TvmAst.hpp" -#include "TVMAnalyzer.hpp" +#include +#include +#include namespace solidity::frontend { @@ -285,7 +285,7 @@ class StackPusher { void store(const Type *type, bool reverse); void pushZeroAddress(); - Pointer generateC7ToC4(bool forAwait); + Pointer generateC7ToC4(); void convert(Type const *leftType, Type const *rightType); void checkFit(Type const *type); void pushParameter(std::vector> const& params); @@ -299,6 +299,7 @@ class StackPusher { void reverse(int qty, int startIndex); void dropUnder(int droppedCount, int leftCount); void exchange(int i); + void exchange(int i, int j); void rot(); void rotRev(); void prepareKeyForDictOperations(Type const* key, bool doIgnoreBytes); @@ -356,14 +357,13 @@ class StackPusher { void pushEmptyArray(); void pushNull(); + void pushNaN(); void pushEmptyCell(); void pushDefaultValue(Type const* _type); void sendIntMsg(const std::map &exprs, const std::map &constParams, const std::function &appendBody, const std::function &pushSendrawmsgFlag, - bool isAwait, - size_t callParamsOnStack, const std::function &appendStateInit); enum class MsgType{ diff --git a/compiler/libsolidity/codegen/TVMSimulator.cpp b/compiler/libsolidity/codegen/TVMSimulator.cpp index 301e6252..f1322cb2 100644 --- a/compiler/libsolidity/codegen/TVMSimulator.cpp +++ b/compiler/libsolidity/codegen/TVMSimulator.cpp @@ -14,8 +14,8 @@ * Simulator of TVM execution */ -#include "TVMCommons.hpp" -#include "TVMSimulator.hpp" +#include +#include using namespace solidity::frontend; diff --git a/compiler/libsolidity/codegen/TVMSimulator.hpp b/compiler/libsolidity/codegen/TVMSimulator.hpp index 0e2604b8..be942b40 100644 --- a/compiler/libsolidity/codegen/TVMSimulator.hpp +++ b/compiler/libsolidity/codegen/TVMSimulator.hpp @@ -16,7 +16,7 @@ #pragma once -#include "TvmAstVisitor.hpp" +#include namespace solidity::frontend { class Simulator : public TvmAstVisitor { diff --git a/compiler/libsolidity/codegen/TVMStructCompiler.cpp b/compiler/libsolidity/codegen/TVMStructCompiler.cpp index 5b4d9398..dd33d7f6 100644 --- a/compiler/libsolidity/codegen/TVMStructCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMStructCompiler.cpp @@ -14,12 +14,12 @@ * Struct compiler for TVM */ -#include "TVMCommons.hpp" -#include "TVMPusher.hpp" -#include "TVMStructCompiler.hpp" - #include -#include "TVMABI.hpp" + +#include +#include +#include +#include using namespace solidity::frontend; using namespace solidity::util; diff --git a/compiler/libsolidity/codegen/TVMStructCompiler.hpp b/compiler/libsolidity/codegen/TVMStructCompiler.hpp index 10e6fd32..94f1dba6 100644 --- a/compiler/libsolidity/codegen/TVMStructCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMStructCompiler.hpp @@ -16,10 +16,11 @@ #pragma once +#include + #include #include -#include -#include "TVMCommons.hpp" +#include namespace solidity::frontend { diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.cpp b/compiler/libsolidity/codegen/TVMTypeChecker.cpp index bdafb33f..7c77eb59 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.cpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.cpp @@ -16,11 +16,12 @@ #include -#include "TVM.hpp" -#include "TVMCommons.hpp" -#include "TVMPusher.hpp" -#include "TVMConstants.hpp" -#include "TVMTypeChecker.hpp" +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace solidity::langutil; @@ -171,9 +172,8 @@ bool TVMTypeChecker::visit(VariableDeclaration const& _variable) { ASTString const& name = _variable.name(); if (name == "_pubkey" || name == "_timestamp" || name == "_constructorFlag") m_errorReporter.typeError(7984_error, _variable.location(), "The name \"" + name + "\" is reserved."); - Type::Category const category = _variable.type()->category(); - if (category == Type::Category::TvmSlice || category == Type::Category::TvmVector) - m_errorReporter.typeError(9191_error, _variable.location(), "This type can't be used for state variables."); + TypeChecker{langutil::EVMVersion{}, m_errorReporter}.typeCheckTvmEncodeArg(_variable.type(), _variable.location(), + "This type can not be used for state variables.", true); if (_variable.isNoStorage() && _variable.isStatic()) m_errorReporter.typeError(4161_error, _variable.location(), R"(State variable can not be marked as "nostorage" and "static" simultaneously.)"); if (_variable.isNoStorage() && _variable.value()) @@ -252,10 +252,6 @@ bool TVMTypeChecker::visit(const FunctionDefinition &f) { } } - if (!f.isFree() && f.isInlineAssembly()) { - m_errorReporter.typeError(7229_error, f.location(), "Only free functions can be marked as \"assembly\"."); - } - return true; } @@ -358,11 +354,6 @@ void TVMTypeChecker::checkDeprecation(FunctionCall const& _functionCall) { m_errorReporter.warning(4767_error, _functionCall.location(), "\"tvm.functionId()\" is deprecated. Use: \"abi.functionId()\""); break; - case FunctionType::Kind::ABIBuildExtMsg: - if (kind == MagicType::Kind::TVM) - m_errorReporter.warning(9856_error, _functionCall.location(), - "\"tvm.buildExtMsg()\" is deprecated. Use: \"abi.encodeExtMsg()\""); - break; case FunctionType::Kind::ABIBuildIntMsg: if (kind == MagicType::Kind::TVM) m_errorReporter.warning(4063_error, _functionCall.location(), @@ -423,17 +414,22 @@ void TVMTypeChecker::checkSupport(FunctionCall const& _functionCall) { default: break; } - - if (_functionCall.isAwait() && *GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(5601_error, _functionCall.location(), - "\"*.await\"" + isNotSupportedVM); - } } bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { checkDeprecation(_functionCall); checkSupport(_functionCall); + auto checkRange = [&](std::optional const& value, int limit, SourceLocation const& loc) { + if (value.has_value() && value > limit) { + m_errorReporter.syntaxError( + 8365_error, + loc, + "Too big value. The value must be in the range 0 - " + toString(limit) + "." + ); + } + }; + Type const* expressionType = _functionCall.expression().annotation().type; std::vector> const& arguments = _functionCall.arguments(); switch (expressionType->category()) { @@ -466,21 +462,30 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { } break; } + case FunctionType::Kind::TVMBuilderStoreInt: { + const auto& value = ExprUtils::constValue(*arguments.at(1)); + checkRange(value, 257, arguments.at(1)->location()); + break; + } + case FunctionType::Kind::TVMBuilderStoreUint: { + const auto& value = ExprUtils::constValue(*arguments.at(1)); + checkRange(value, 256, arguments.at(1)->location()); + break; + } case FunctionType::Kind::TVMSliceLoadInt: case FunctionType::Kind::TVMSliceLoadIntQ: + case FunctionType::Kind::TVMSlicePreLoadInt: + case FunctionType::Kind::TVMSlicePreLoadIntQ: { + const auto& value = ExprUtils::constValue(*arguments.at(0)); + checkRange(value, 257, arguments.at(0)->location()); + break; + } case FunctionType::Kind::TVMSliceLoadUint: case FunctionType::Kind::TVMSliceLoadUintQ: - case FunctionType::Kind::TVMSlicePreLoadInt: - case FunctionType::Kind::TVMSlicePreLoadIntQ: case FunctionType::Kind::TVMSlicePreLoadUint: case FunctionType::Kind::TVMSlicePreLoadUintQ: { const auto& value = ExprUtils::constValue(*arguments.at(0)); - if (value.has_value()) { - if (value > 256) { - m_errorReporter.syntaxError(8838_error, arguments.at(0)->location(), - "Too big value. The value must be in the range 0 - 256."); - } - } + checkRange(value, 256, arguments.at(0)->location()); break; } default: @@ -545,11 +550,8 @@ bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { bool TVMTypeChecker::visit(ContractDefinition const& cd) { contractDefinition = &cd; - m_inherHelper = std::make_unique(&cd); - checkOverrideAndOverload(); - return true; } diff --git a/compiler/libsolidity/codegen/TvmAst.cpp b/compiler/libsolidity/codegen/TvmAst.cpp index 3aa57da5..2b027fbd 100644 --- a/compiler/libsolidity/codegen/TvmAst.cpp +++ b/compiler/libsolidity/codegen/TvmAst.cpp @@ -21,12 +21,12 @@ #include -#include "TVM.hpp" -#include "TVMCommons.hpp" -#include "TVMConstants.hpp" -#include "TVMPusher.hpp" -#include "TvmAst.hpp" -#include "TvmAstVisitor.hpp" +#include +#include +#include +#include +#include +#include using namespace solidity::frontend; using namespace std; @@ -473,8 +473,8 @@ Pointer gen(const std::string& cmd) { if (*GlobalParams::g_tvmVersion == langutil::TVMVersion::ton()) solAssert(!isIn(op, "COPYLEFT", "INITCODEHASH", "MYCODE", "LDCONT", "STCONT"), ""); - auto f = [&](const std::string& pattert) { - return op == pattert; + auto f = [&](std::string const& pattern) { + return op == pattern; }; auto dictReplaceOrAdd = [&]() { @@ -531,10 +531,10 @@ Pointer gen(const std::string& cmd) { {"MYCODE", {0, 1, true}}, {"NEWC", {0, 1, true}}, {"NEWDICT", {0, 1, true}}, - {"NIL", {0, 1, true}}, {"NOW", {0, 1, true}}, {"NULL", {0, 1, true}}, {"PUSHINT", {0, 1, true}}, + {"PUSHNAN", {0, 1, true}}, {"RANDSEED", {0, 1, true}}, {"RANDU256", {0, 1}}, {"STORAGEFEE", {0, 1, true}}, @@ -553,6 +553,7 @@ Pointer gen(const std::string& cmd) { {"BDEPTH", {1, 1}}, {"BINDUMP", {1, 1}}, {"BITNOT", {1, 1}}, // pseudo opcode. Alias for NOT + {"QBITNOT", {1, 1, true}}, // pseudo opcode. Alias for QNOT {"BITSIZE", {1, 1, true}}, {"BLESS", {1, 1}}, {"BREFS", {1, 1, true}}, @@ -562,6 +563,7 @@ Pointer gen(const std::string& cmd) { {"CONFIGOPTPARAM", {1, 1, true}}, {"CTOS", {1, 1}}, {"DEC", {1, 1}}, + {"QDEC", {1, 1, true}}, {"DICTEMPTY", {1, 1, true}}, {"ENDC", {1, 1}}, {"EQINT", {1, 1, true}}, @@ -573,23 +575,27 @@ Pointer gen(const std::string& cmd) { {"HASHSU", {1, 1, true}}, {"HEXDUMP", {1, 1}}, {"INC", {1, 1}}, + {"QINC", {1, 1, true}}, {"INDEX2", {1, 1}}, {"INDEX3", {1, 1}}, {"INDEX_EXCEP", {1, 1}}, {"INDEX_NOEXCEP", {1, 1, true}}, + {"ISNAN", {1, 1, true}}, {"ISNEG", {1, 1, true}}, {"ISNNEG", {1, 1, true}}, {"ISNPOS", {1, 1, true}}, {"ISNULL", {1, 1, true}}, - {"ISPOS", {1, 1, true}}, - {"ISZERO", {1, 1, true}}, + {"ISPOS", {1, 1, true}}, // TODO GTINT 0 and for another {"LAST", {1, 1}}, {"LESSINT", {1, 1, true}}, {"MODPOW2", {1, 1}}, + {"QMODPOW2", {1, 1, true}}, {"MULCONST", {1, 1}}, {"NEGATE", {1, 1}}, + {"QNEGATE", {1, 1, true}}, {"NEQINT", {1, 1, true}}, {"NOT", {1, 1, true}}, // logical not + {"QNOT", {1, 1, true}}, // logical not {"PARSEMSGADDR", {1, 1}}, {"PLDDICT", {1, 1}}, {"PLDI", {1, 1}}, @@ -603,12 +609,15 @@ Pointer gen(const std::string& cmd) { {"PLDULE4", {1, 1}}, {"PLDULE8", {1, 1}}, {"POW2", {1, 1}}, + {"QFITS", {1, 1, true}}, + {"QUFITS", {1, 1, true}}, {"RAND", {1, 1}}, {"SBITS", {1, 1, true}}, {"SDEMPTY", {1, 1, true}}, {"SDEPTH", {1, 1}}, {"SEMPTY", {1, 1, true}}, {"SGN", {1, 1, true}}, + {"QSGN", {1, 1, true}}, {"SHA256U", {1, 1, true}}, {"SREFS", {1, 1, true}}, {"STONE", {1, 1}}, @@ -653,7 +662,9 @@ Pointer gen(const std::string& cmd) { {"SENDRAWMSG", {2, 0}}, {"ADD", {2, 1}}, + {"QADD", {2, 1, true}}, {"AND", {2, 1, true}}, + {"QAND", {2, 1, true}}, {"CMP", {2, 1, true}}, {"DIFF", {2, 1}}, {"DIFF_PATCH", {2, 1}}, @@ -666,21 +677,35 @@ Pointer gen(const std::string& cmd) { {"DIFF_PATCH_ZIPQ", {2, 1, true}}, {"DIFF_ZIP", {2, 1}}, {"DIV", {2, 1}}, + {"QDIV", {2, 1, true}}, {"DIVC", {2, 1}}, + {"QDIVC", {2, 1, true}}, {"DIVR", {2, 1}}, + {"QDIVR", {2, 1, true}}, {"ENDXC", {2, 1}}, {"EQUAL", {2, 1, true}}, + {"QEQUAL", {2, 1, true}}, {"GEQ", {2, 1, true}}, + {"QGEQ", {2, 1, true}}, {"GREATER", {2, 1, true}}, + {"QGREATER", {2, 1, true}}, {"INDEXVAR", {2, 1}}, // only for vector {"LEQ", {2, 1, true}}, + {"QLEQ", {2, 1, true}}, {"LESS", {2, 1, true}}, + {"QLESS", {2, 1, true}}, {"MAX", {2, 1, true}}, + {"QMAX", {2, 1, true}}, {"MIN", {2, 1, true}}, + {"QMIN", {2, 1, true}}, {"MOD", {2, 1}}, + {"QMOD", {2, 1, true}}, {"MUL", {2, 1}}, + {"QMUL", {2, 1, true}}, {"NEQ", {2, 1, true}}, + {"QNEQ", {2, 1, true}}, {"OR", {2, 1, true}}, + {"QOR", {2, 1, true}}, {"PLDIX", {2, 1}}, {"PLDREFVAR", {2, 1}}, {"PLDSLICEX", {2, 1}}, @@ -717,16 +742,20 @@ Pointer gen(const std::string& cmd) { {"STVARUINT32", {2, 1}}, {"STZEROES", {2, 1}}, {"SUB", {2, 1}}, - {"SUBR", {2, 1}}, + {"QSUB", {2, 1, true}}, + {"SUBR", {2, 1}}, // TODO add QSUBR ? {"TPUSH", {2, 1}}, {"XOR", {2, 1, true}}, + {"QXOR", {2, 1, true}}, {"DIVMOD", {2, 2}}, + {"QDIVMOD", {2, 2, true}}, {"LDIX", {2, 2}}, {"LDSAME", {2, 2, true}}, {"LDSLICEX", {2, 2}}, {"LDUX", {2, 2}}, {"MINMAX", {2, 2, true}}, + {"QMINMAX", {2, 2, true}}, {"CDATASIZE", {2, 3}}, {"SDATASIZE", {2, 3}}, @@ -737,13 +766,17 @@ Pointer gen(const std::string& cmd) { {"CHKSIGNU", {3, 1}}, {"CONDSEL", {3, 1}}, {"MULDIV", {3, 1}}, + {"QMULDIV", {3, 1, true}}, {"MULDIVC", {3, 1}}, + {"QMULDIVC", {3, 1, true}}, {"MULDIVR", {3, 1}}, + {"QMULDIVR", {3, 1, true}}, {"SCHKBITREFSQ", {3, 1, true}}, {"SCUTFIRST", {3, 1}}, {"SETINDEXVAR", {3, 1}}, {"SETINDEXVARQ", {3, 1, true}}, {"SSKIPFIRST", {3, 1}}, + {"STIX", {3, 1}}, {"STIXR", {3, 1}}, {"STSAME", {3, 1}}, {"STUX", {3, 1}}, @@ -753,6 +786,7 @@ Pointer gen(const std::string& cmd) { {"DICTIDEL", {3, 2}}, {"DICTUDEL", {3, 2}}, {"MULDIVMOD", {3, 2}}, + {"QMULDIVMOD", {3, 2, true}}, {"SPLIT", {3, 2}} }; @@ -773,21 +807,18 @@ Pointer gen(const std::string& cmd) { } else if (f("UNPACKFIRST")) { int ret = boost::lexical_cast(param); opcode = createNode(cmd, 1, ret); - } else if (f("LSHIFT") || f("RSHIFT")) { - if (param.empty()) { + } else if (f("LSHIFT") || f("QLSHIFT") || f("RSHIFT") || f("QRSHIFT")) { + if (param.empty()) opcode = createNode(cmd, 2, 1); - } else { + else opcode = createNode(cmd, 1, 1); - } } else if (f("MULRSHIFT")) { - if (param.empty()) { + if (param.empty()) opcode = createNode(cmd, 3, 1); - } else { + else opcode = createNode(cmd, 2, 1); - } - } else { + } else solUnimplemented("Unknown opcode: " + cmd); - } solAssert(opcode != nullptr, ""); return opcode; } diff --git a/compiler/libsolidity/codegen/TvmAst.hpp b/compiler/libsolidity/codegen/TvmAst.hpp index 96395d9a..588d2b69 100644 --- a/compiler/libsolidity/codegen/TvmAst.hpp +++ b/compiler/libsolidity/codegen/TvmAst.hpp @@ -24,7 +24,7 @@ #include #include -#include "libsolidity/ast/AST.h" +#include template using Pointer = std::shared_ptr; diff --git a/compiler/libsolidity/codegen/TvmAstVisitor.cpp b/compiler/libsolidity/codegen/TvmAstVisitor.cpp index b122369d..ef65dcc6 100644 --- a/compiler/libsolidity/codegen/TvmAstVisitor.cpp +++ b/compiler/libsolidity/codegen/TvmAstVisitor.cpp @@ -19,7 +19,7 @@ #include #include -#include "TVMCommons.hpp" +#include using namespace solidity::frontend; @@ -94,6 +94,7 @@ bool Printer::visit(TvmException &_node) { bool Printer::visit(StackOpcode &_node) { tabs(); if (_node.fullOpcode() == "BITNOT") m_out << "NOT"; + else if (_node.fullOpcode() == "QBITNOT") m_out << "QNOT"; //else if (_node.fullOpcode() == "STVARUINT16") m_out << "STGRAMS"; //else if (_node.fullOpcode() == "LDVARUINT16") m_out << "LDGRAMS"; else if (_node.fullOpcode() == "TUPLE 1") m_out << "SINGLE"; diff --git a/compiler/libsolidity/parsing/Parser.cpp b/compiler/libsolidity/parsing/Parser.cpp index 6e9c623b..6f41bb42 100644 --- a/compiler/libsolidity/parsing/Parser.cpp +++ b/compiler/libsolidity/parsing/Parser.cpp @@ -39,7 +39,7 @@ #include #include -#include "libsolidity/codegen/TVMConstants.hpp" +#include using namespace solidity::langutil; using namespace std::string_literals; @@ -260,7 +260,7 @@ ASTPointer Parser::parsePragmaDirective(bool const _finishedPar nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - if (literals.size() >= 3 && (literals[0] == "ton" || literals[0] == "ever")) + if (literals.size() >= 3 && (literals[0] == "ton" || literals[0] == "ever" || literals[0] == "tvm")) { parsePragmaVersion( nodeFactory.location(), @@ -1273,6 +1273,10 @@ ASTPointer Parser::parseTypeName() ASTNodeFactory nodeFactory(*this); ASTPointer type; Token token = m_scanner->currentToken(); + if (token == Token::VarInt) parserWarning(7184_error, R"("Deprecated. Use "varint".)"); + if (token == Token::VarUint) parserWarning(1832_error, R"("Deprecated. Use "varuint".)"); + if (token == Token::VarIntM) parserWarning(4875_error, R"("Deprecated. Use "varintM".)"); + if (token == Token::VarUintM) parserWarning(8909_error, R"("Deprecated. Use "varuintM".)"); if (TokenTraits::isElementaryTypeName(token)) { unsigned firstSize; @@ -1309,6 +1313,8 @@ ASTPointer Parser::parseTypeName() type = parseOptional(); else if (token == Token::TvmVector) type = parseTvmVector(); + else if (token == Token::TvmStack) + type = parseTvmStack(); else if (token == Token::Identifier) type = parseUserDefinedTypeName(); else @@ -1407,6 +1413,18 @@ ASTPointer Parser::parseTvmVector() return nodeFactory.createNode(type); } +ASTPointer Parser::parseTvmStack() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + expectToken(Token::TvmStack); + expectToken(Token::LParen); + ASTPointer type = parseTypeName(); + expectToken(Token::RParen); + nodeFactory.markEndPosition(); + return nodeFactory.createNode(type); +} + ASTPointer Parser::parseParameterList( VarDeclParserOptions const& _options, bool _allowEmpty @@ -1768,8 +1786,7 @@ ASTPointer Parser::parseEmitStatement(ASTPointer const eventName, functionCallArguments.arguments, functionCallArguments.parameterNames, - functionCallArguments.parameterNameLocations, - FunctionCall::Kind::Usual + functionCallArguments.parameterNameLocations ); // TODO DELETE add optionList.location return nodeFactory.createNode(_docString, eventCall, optionList.arguments, optionList.parameterNames); @@ -1804,8 +1821,7 @@ ASTPointer Parser::parseRevertStatement(ASTPointer c errorName, functionCallArguments.arguments, functionCallArguments.parameterNames, - functionCallArguments.parameterNameLocations, - FunctionCall::Kind::Usual + functionCallArguments.parameterNameLocations ); return nodeFactory.createNode(_docString, errorCall); } @@ -2373,21 +2389,11 @@ ASTPointer Parser::parseLeftHandSideExpression( advance(); auto functionCallArguments = parseFunctionCallArguments(); expectToken(Token::RParen); - FunctionCall::Kind kind = FunctionCall::Kind::Usual; - auto nextToken = m_scanner->peekNextToken(); - if (m_scanner->currentToken() == Token::Period && (nextToken == Token::Await || nextToken == Token::ExtMsg)) - { - bool isAwait = m_scanner->peekNextToken() == Token::Await; - m_scanner->next(); - m_scanner->next(); - kind = isAwait ? FunctionCall::Kind::Await : FunctionCall::Kind::ExtMsg; - } nodeFactory.markEndPosition(); expression = nodeFactory.createNode( expression, functionCallArguments.arguments, functionCallArguments.parameterNames, - functionCallArguments.parameterNameLocations, - kind + functionCallArguments.parameterNameLocations ); break; } @@ -2479,6 +2485,7 @@ ASTPointer Parser::parsePrimaryExpression() case Token::FalseLiteral: case Token::NullLiteral: case Token::EmptyMap: + case Token::TVMNaN: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; @@ -2606,10 +2613,6 @@ Parser::FunctionCallArguments Parser::parseNamedArguments() if (!first) expectToken(Token::Comma); - if (m_scanner->currentToken() == Token::ExtMsg) { - fatalParserError(3299_error, "\"extMsg\" call option is deprecated, use suffix \".extMsg\".\nFor example: Foo(addr).bar{...}(...).extMsg;\n"); - } - auto identifierWithLocation = expectIdentifierWithLocation(); // Add name ret.parameterNames.emplace_back(std::move(identifierWithLocation.first)); @@ -2667,6 +2670,7 @@ bool Parser::variableDeclarationStart() currentToken == Token::Mapping || currentToken == Token::Optional || currentToken == Token::TvmVector || + currentToken == Token::TvmStack || currentToken == Token::LBrack || TokenTraits::isElementaryTypeName(currentToken) || (currentToken == Token::Function && m_scanner->peekNextToken() == Token::LParen); @@ -2719,7 +2723,8 @@ Parser::LookAheadInfo Parser::peekStatementType() const Token token(m_scanner->currentToken()); bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); - if (token == Token::Mapping || token == Token::Optional || token == Token::TvmVector || token == Token::Function || token == Token::Var || token == Token::LBrack) + if (token == Token::Mapping || token == Token::Optional || token == Token::TvmVector || token == Token::TvmStack || + token == Token::Function || token == Token::Var || token == Token::LBrack) return LookAheadInfo::VariableDeclaration; if (mightBeTypeName) { diff --git a/compiler/libsolidity/parsing/Parser.h b/compiler/libsolidity/parsing/Parser.h index 690c52bd..c4f982eb 100644 --- a/compiler/libsolidity/parsing/Parser.h +++ b/compiler/libsolidity/parsing/Parser.h @@ -131,6 +131,7 @@ class Parser: public langutil::ParserBase ASTPointer parseMapping(); ASTPointer parseOptional(); ASTPointer parseTvmVector(); + ASTPointer parseTvmStack(); ASTPointer parseParameterList( VarDeclParserOptions const& _options = {}, bool _allowEmpty = true diff --git a/lib/stdlib.sol b/lib/stdlib.sol new file mode 100644 index 00000000..921d434e --- /dev/null +++ b/lib/stdlib.sol @@ -0,0 +1,618 @@ +pragma tvm-solidity >= 0.72.0; +pragma ignoreIntOverflow; + +struct DigitStack { + uint digit; + optional(DigitStack) tail; +} + +function getUnchecked(optional(DigitStack) x) assembly pure returns (DigitStack) { +} + +function __QAND(qint a, qint b) assembly pure returns (qint) { + "QAND" +} + +function __QOR(qint a, qint b) assembly pure returns (qint) { + "QOR" +} + +contract stdlib { + function __replayProtection(uint64 msg_timestamp) view private { + require(tvm.replayProtTime() < msg_timestamp, 52); + require(msg_timestamp < block.timestamp * 1000 + tvm.replayProtInterval(), 52); + tvm.setReplayProtTime(msg_timestamp); + } + + function __tonToGas(uint128 _ton, int8 wid) private pure returns(uint128) { + return math.muldiv(_ton, 65536, __gasGasPrice(wid)); // round down + } + + function __gasToTon(uint128 gas, int8 wid) private pure returns(uint128) { + return math.muldivc(gas, __gasGasPrice(wid), 65536); // round up + } + + function __gasGasPrice(int8 wid) private pure returns(uint64 gasPrice) { + require(wid == 0 || wid == -1, 67); + optional(TvmCell) optCell = tvm.rawConfigParam(wid == 0 ? int32(21) : int32(20)); + require(optCell.hasValue(), 68); + TvmSlice s = optCell.get().toSlice(); + (, , , , gasPrice) = s.load(uint8, uint64, uint64, uint8, uint64); + } + + // a ** n + function __exp(int a, uint n) private pure returns (int) { + require(a | n != 0, 69); + int res = 1; + while (n != 0) { + if (n % 2 != 0) + res *= a; + a *= a; + n >>= 1; + } + return res; + } + + function __qexp(qint _a, quint _n) private pure returns (qint) { + if (_n.isNaN() || + (_n.get() == 0 && !_a.isNaN() && _a.get() == 0) + ) + return NaN; + uint n = _n.get(); + qint res = 1; + while (n != 0) { + if (n % 2 != 0) + res *= _a; + _a *= _a; + n >>= 1; + } + return res; + } + + function __parseInteger(uint integer, uint8 modulo) internal pure returns (optional(DigitStack), uint8) { + if (integer == 0) { + return (DigitStack(0, null), 1); + } + optional(DigitStack) digits; + uint8 length = 0; + while (integer != 0) { + uint dig; + (integer, dig) = math.divmod(integer, modulo); + digits = DigitStack(dig, digits); + ++length; + } + return (digits, length); + } + + // StringBuilder constructor + function __createStringBuilder() pure private returns (stack(TvmBuilder)) { + stack(TvmBuilder) st; + st.push(TvmBuilder()); + return st; + } + + // .toString() + function __makeString(stack(TvmBuilder) st) pure private returns(TvmCell) { + TvmBuilder res = st.pop(); + while (!st.empty()) { + TvmBuilder b = st.pop(); + b.storeRef(res); + res = b; + } + return res.toCell(); + } + + // .append() + function __appendBytes1(stack(TvmBuilder) st, bytes1 ch) pure private returns(stack(TvmBuilder)) { + if (st.top().remBits() < 8) + st.push(TvmBuilder()); + st.top().store(ch); + return st; + } + + // .append(, ) + function __appendBytes1NTimes(stack(TvmBuilder) st, bytes1 ch, uint31 n) pure private returns(stack(TvmBuilder)) { + repeat(n) { + st = __appendBytes1(st, ch); + } + return st; + } + + function __appendSliceToStringBuilder(stack(TvmBuilder) st, TvmSlice s) pure private returns(stack(TvmBuilder)) { + uint10 restBits = st.top().remBits() - 7; // 1023 - 7 = 1016 = 127 * 8 + TvmSlice ss = s.loadSlice(math.min(restBits, s.bits()), 0); + st.top().store(ss); + if (!s.empty()) { + TvmBuilder b; + b.store(s); + st.push(b); + } + return st; + } + + function __appendStringToStringBuilderWithNoShift(stack(TvmBuilder) st, TvmCell _str) pure private returns(stack(TvmBuilder)) { + TvmSlice str = _str.toSlice(); + while(true) { + st.top().store(str.loadSlice(str.bits(), 0)); + if (str.empty()) + break; + st.push(TvmBuilder()); + str = str.loadRefAsSlice(); + } + return st; + } + + // .append() + function __appendStringToStringBuilder(stack(TvmBuilder) st, TvmCell cell) private pure returns (stack(TvmBuilder)) { + if (st.top().bits() == 0) { + return __appendStringToStringBuilderWithNoShift(st, cell); + } + TvmSlice s = cell.toSlice(); + while (true) { + TvmSlice curSlice = s.loadSlice(s.bits(), 0); + st = __appendSliceToStringBuilder(st, curSlice); + if (s.empty()) + break; + s = s.loadRefAsSlice(); + } + return st; + } + + function __convertIntToString(stack(TvmBuilder) st, int257 _integer, uint16 width, bool leadingZeros) + private pure returns (stack(TvmBuilder)) + { + bool addMinus = _integer < 0; + uint integer = math.abs(_integer); + uint16 remBytes = st.top().remBits() / 8; + + if (addMinus) { + if (remBytes == 0) { + st.push(TvmBuilder()); + remBytes = 127; + } + st.top().store(bytes1("-")); // store "-" + --remBytes; + } + + (optional(DigitStack) digits, uint8 length) = __parseInteger(integer, 10); + + if (width > length) { + bytes1 fill = leadingZeros ? bytes1("0") : bytes1(" "); + uint16 zeroes = width - length; + repeat(math.min(zeroes, remBytes)) { + st.top().store(fill); + } + if (zeroes > remBytes) { + st.push(TvmBuilder()); + repeat(zeroes - remBytes) { + st.top().store(fill); + } + remBytes = remBytes + 127 - zeroes; + } else { + remBytes -= zeroes; + } + } + + repeat(math.min(length, remBytes)) { + uint dig; + (dig, digits) = getUnchecked(digits).unpack(); + st.top().storeUint(dig + uint8(bytes1("0")), 8); + } + if (length > remBytes) { + st.push(TvmBuilder()); + repeat(length - remBytes) { + uint dig; + (dig, digits) = getUnchecked(digits).unpack(); + st.top().storeUint(dig + uint8(bytes1("0")), 8); + } + } + + return st; + } + + function __convertAddressToHexString(stack(TvmBuilder) st, address addr) private pure returns (stack(TvmBuilder)) { + (int32 wid, uint value) = addr.unpack(); + st = __convertIntToHexString(st, wid, 0, false, true); + uint16 remBits = st.top().remBits(); + if (remBits < 8) { + st.push(TvmBuilder()); + } + st.top().store(bytes1(":")); + return __convertIntToHexString(st, value, 64, true, true); + } + + function __convertFixedPointToString(stack(TvmBuilder) st, int257 value, uint16 fractionalDigits, uint fractionPow10) private pure returns (stack(TvmBuilder)) { + if (value < 0) { + if (st.top().remBits() < 8) { + st.push(TvmBuilder()); + } + st.top().store(bytes1("-")); // store "-" + } + (uint integer, uint fractional) = math.divmod(math.abs(value), fractionPow10); + st = __convertIntToString(st, integer, 0, false); + if (st.top().remBits() < 8) { + st.push(TvmBuilder()); + } + st.top().store(bytes1(".")); + return __convertIntToString(st, fractional, fractionalDigits, true); + } + + function __convertIntToHexString(stack(TvmBuilder) st, int257 _integer, uint16 width, bool leadingZeros, bool isLow) + private pure returns (stack(TvmBuilder)) + { + bool addMinus = _integer < 0; + uint integer = math.abs(_integer); + uint16 remBytes = st.top().remBits() / 8; + + if (addMinus) { + if (remBytes == 0) { + st.push(TvmBuilder()); + remBytes = 127; + } + st.top().store(bytes1("-")); // store "-" + --remBytes; + } + + (optional(DigitStack) digits, uint8 length) = __parseInteger(integer, 16); + + if (width > length) { + bytes1 fill = leadingZeros ? bytes1("0") : bytes1(" "); + uint16 zeroes = width - length; + repeat(math.min(zeroes, remBytes)) { + st.top().store(fill); + } + if (zeroes > remBytes) { + st.push(TvmBuilder()); + repeat(zeroes - remBytes) { + st.top().store(fill); + } + remBytes = remBytes + 127 - zeroes; + } else { + remBytes -= zeroes; + } + } + + uint8 letterA = uint8(isLow ? bytes1("a") : bytes1("A")) - 10; + repeat(math.min(length, remBytes)) { + uint dig; + (dig, digits) = digits.get().unpack(); + st.top().storeUint(dig + (dig < 10 ? uint8(bytes1("0")) : letterA), 8); + } + if (length > remBytes) { + st.push(TvmBuilder()); + repeat(length - remBytes) { + uint dig; + (dig, digits) = digits.get().unpack(); + st.top().storeUint(dig + (dig < 10 ? uint8(bytes1("0")) : letterA), 8); + } + } + return st; + } + + function __stoi(TvmCell _str) private pure returns (optional(int)) { + TvmSlice str = _str.toSlice(); + if (str.bits() < 8) { + return null; + } + + bool isNeg = str.bits() >= 8 && str.preload(uint8) == uint8(bytes1("-")); + if (isNeg) + str.skip(8); + + bool isHex = str.bits() >= 16 && str.preload(uint16) == uint16(bytes2("0x")); + if (isHex) + str.skip(16); + + int res = 0; + uint16 digits = str.bits() >> 3; + if (isHex) { + repeat(digits) { + uint8 dig = str.load(uint8); + res *= 16; + if (dig >= 0x30 && dig <= 0x39) { + res += dig - 0x30; + } else if (dig >= 0x41 && dig <= 0x46) { + res += dig - 0x41 + 10; + } else if (dig >= 0x61 && dig <= 0x66) { + res += dig - 0x61 + 10; + } else { + return null; + } + } + } else { + repeat(digits) { + uint8 dig = str.load(uint8); + if (dig < uint8(bytes1("0")) || dig > uint8(bytes1("9"))) + return null; + res = res * 10 + int(uint(dig - uint8(bytes1("0")))); + } + } + if (isNeg) + res = -res; + return res; + } + + // string text = ...; + // string sub = text[3:10]; + function __arraySlice(TvmCell data, uint10 from, uint end) private pure returns (TvmCell) { + require(from <= end, 70); + uint count = end - from; + return __subCell(data, from, count, false); + } + + function __subCell(TvmCell _str, uint10 from, uint count, bool notStrictCount) private pure returns (TvmCell) { + (uint10 skipCells, uint10 skipBytes) = math.divmod(from, 127); + if (skipCells != 0 && skipBytes == 0) { + --skipCells; + skipBytes = 127; + } + + TvmSlice str = _str.toSlice(); + repeat(skipCells) { + require(str.refs() == 1, 70); + str = str.loadRefAsSlice(); + } + + skipBytes *= 8; + require(str.bits() >= skipBytes, 70); + str.skip(skipBytes); + + count = 8 * count; + stack(TvmBuilder) st = __createStringBuilder(); + while (true) { + uint10 take = uint10(math.min(str.bits(), count)); + count -= take; + TvmSlice curSlice = str.loadSlice(take, 0); + st = __appendSliceToStringBuilder(st, curSlice); + if (count == 0 || str.empty()) { + break; + } + str = str.loadRefAsSlice(); + } + require(notStrictCount || count == 0, 70); + return __makeString(st); + } + + function __compareStrings(TvmCell lstr, TvmCell rstr) private pure returns (int8) { + // 1 lstr > rstr + // 0 lstr == rstr + // -1 lstr < rstr + TvmSlice left = lstr.toSlice(); + TvmSlice right = rstr.toSlice(); + while (true) { + int8 res = left.compare(right); + if (res != 0) { + return res; + } + uint8 lRefs = left.refs(); + uint8 rRefs = right.refs(); + if (lRefs > rRefs) + return 1; + if (rRefs > lRefs) + return -1; + if (lRefs + rRefs == 0) + return 0; + left = left.loadRefAsSlice(); + right = right.loadRefAsSlice(); + } + return 0; + } + + function __concatenateStrings(TvmCell a, TvmCell b) pure private returns(TvmCell) { + stack(TvmBuilder) st = __createStringBuilder(); + st = __appendStringToStringBuilderWithNoShift(st, a); + st = __appendStringToStringBuilder(st, b); + return __makeString(st); + } + + function __strchr(bytes str, bytes1 char) private pure returns (optional(uint32) res) { + uint32 i = 0; + for (bytes1 b : str) { + if (b == char) + return i; + ++i; + } + } + + function __strrchr(bytes str, bytes1 char) private pure returns (optional(uint32) res) { + uint32 i = 0; + for (bytes1 b : str) { + if (b == char) + res.set(i); + ++i; + } + } + + // check whether `str` starts with `prefix` + function __isPrefix(TvmSlice str, TvmSlice prefix) private pure inline returns (bool equal) { + uint10 strBits = str.bits(); + uint10 prefixBits = prefix.bits(); + while (!prefix.empty()) { + if (strBits == 0) { + if (str.refs() == 0) + return false; + str = str.loadRefAsSlice(); + strBits = str.bits(); + } + if (prefixBits == 0) { + prefix = prefix.loadRefAsSlice(); + prefixBits = prefix.bits(); + } + uint10 len = math.min(strBits, prefixBits); + TvmSlice s1 = str.loadSlice(len); + TvmSlice s2 = prefix.loadSlice(len); + if (s2 != s1) + return false; + strBits -= len; + prefixBits -= len; + } + return true; + } + + function __strstr(TvmCell _str, TvmCell _substr) private pure returns (optional(uint32)) { + TvmSlice str = _str.toSlice(); + TvmSlice substr = _substr.toSlice(); + for (uint32 pos = 0; ; ) { + if (__isPrefix(str, substr)) + return pos; + if (str.empty()) + break; + if (str.bits() == 0) + str = str.loadRefAsSlice(); + str.load(uint8); + ++pos; + } + return null; + } + + // .toLowerCase() + function __toLowerCase(string s) private pure returns (string) { + StringBuilder sb; + for (bytes1 b : s) { + uint8 ch = uint8(b); + if (uint8(bytes1("A")) <= ch && ch <= uint8(bytes1("Z"))) + ch = ch - uint8(bytes1("A")) + uint8(bytes1("a")) ; // to 'a'..'a' + sb.append(bytes1(ch)); + } + return sb.toString(); + } + + // .toUpperCase() + function __toUpperCase(string s) private pure returns (string) { + StringBuilder sb; + for (bytes1 b : s) { + uint8 ch = uint8(b); + if (uint8(bytes1("a")) <= ch && ch <= uint8(bytes1("z"))) + ch = ch - uint8(bytes1("a")) + uint8(bytes1("A")) ; // to 'A'..'Z' + sb.append(bytes1(ch)); + } + return sb.toString(); + } + + // tvm.stateInitHash() + function __stateInitHash(uint256 codeHash, uint256 dataHash, uint16 codeDepth, uint16 dataDepth) private pure returns (uint256) { + TvmBuilder builder; + // amount of refs - code + data = 2 + builder.storeUint(2, 8); + // amount of data bytes - 1 byte + builder.storeUint(1, 8); + // data bits + // split_depth:(Maybe (## 5)) special:(Maybe TickTock) + // code:(Maybe ^Cell) data:(Maybe ^Cell) + // library:(Maybe ^Cell) + // b00110 + builder.storeUint(6, 5); + // completion tag b100 + builder.storeUint(4, 3); + // refs cell depth + builder.storeUint(codeDepth,16); + builder.storeUint(dataDepth,16); + // refs cell hash + builder.storeUint(codeHash,256); + builder.storeUint(dataHash,256); + return sha256(builder.toSlice()); + } + + function __forwardFee() private pure returns (varuint16) { + TvmSlice s = msg.data.toSlice(); + uint1 tag = s.load(uint1); + if (tag == 0) { + // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + // src:MsgAddressInt dest:MsgAddressInt + // value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + // created_lt:uint64 created_at:uint32 = CommonMsgInfo; + s.load(uint3, + address, address, + varuint16, mapping(uint32 => varuint32), varuint16 + ); + return s.load(varuint16); + } else { + return 0; + } + } + + function __importFee() private pure returns (varuint16) { + TvmSlice s = msg.data.toSlice(); + uint2 tag = s.load(uint2); + if (tag == 2) { + // ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt + // import_fee:Grams = CommonMsgInfo; + s.load(address, address); + return s.load(varuint16); + } else { + return 0; + } + } + + struct Type { + int foo; + } + + // .sort() + function __stackSort(stack(Type) st, function(Type, Type) internal pure returns(bool) isLess) + pure private returns(stack(Type)) + { + if (st.empty()) { + stack(Type) s; + return s; + } + stack(stack(Type)) arr; + int size = 0; + for (; !st.empty(); ++size) { + stack(Type) arr2; + arr2.push(st.pop()); + arr.push(arr2); + } + + while (size > 1) { + stack(stack(Type)) newArr; + if (size % 2 != 0) + newArr.push(arr.pop()); + while (!arr.empty()) { + stack(Type) res; + stack(Type) left = arr.pop(); + stack(Type) right = arr.pop(); + while (!left.empty() && !right.empty()) { + if (isLess(left.top(), right.top())) + res.push(left.pop()); + else + res.push(right.pop()); + } + while (!left.empty()) + res.push(left.pop()); + while (!right.empty()) + res.push(right.pop()); + res.reverse(); + newArr.push(res); + } + arr = newArr; + size = (size + 1) / 2; + } + return arr.pop(); + } + + // .reverse() + function __stackReverse(stack(Type) st) pure private returns(stack(Type)) + { + stack(Type) res; + while (!st.empty()) + res.push(st.pop()); + return res; + } + + function __qand(qint a, qint b) pure private returns(qint) { + if ((a.isNaN() || a.get() != 0) && + (b.isNaN() || b.get() != 0) + ) + return __QAND(a, b); + return 0; + } + + function __qor(qint a, qint b) pure private returns(qint) { + if ((a.isNaN() || a.get() != -1) && + (b.isNaN() || b.get() != -1) + ) + return __QOR(a, b); + return -1; + } +} diff --git a/lib/stdlib_sol.tvm b/lib/stdlib_sol.tvm index 661f8d4f..78da41bb 100644 --- a/lib/stdlib_sol.tvm +++ b/lib/stdlib_sol.tvm @@ -1,190 +1,290 @@ +.fragment __appendBytes1, { + .loc stdlib.sol, 107 + OVER + FIRST + BREMBITS + LESSINT 8 + PUSHCONT { + .loc stdlib.sol, 108 + NEWC + PUSH S2 + PAIR + POP S2 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 109 + SWAP + UNPAIR + ROTREV + STU 8 + SWAP + PAIR + .loc stdlib.sol, 0 +} + +.fragment __appendBytes1NTimes, { + .loc stdlib.sol, 115 + PUSHCONT { + .loc stdlib.sol, 116 + DUP2 + CALLREF { + .inline __appendBytes1 + } + POP S2 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 118 + DROP + .loc stdlib.sol, 0 +} + .fragment __appendSliceToStringBuilder, { - .loc stdlib.sol, 198 + .loc stdlib.sol, 122 OVER FIRST BREMBITS ADDCONST -7 - .loc stdlib.sol, 199 + .loc stdlib.sol, 123 PUXCPU S1, S-1, S0 SBITS MIN LDSLICEX POP S2 - PUSH2 S2, S2 - .loc stdlib.sol, 200 - FIRST - XCHG S1, S2 + .loc stdlib.sol, 124 + PUSH S2 + UNPAIR + ROTREV STSLICE - SETINDEX 0 + SWAP + PAIR POP S2 - .loc stdlib.sol, 201 + .loc stdlib.sol, 125 DUP SEMPTY PUSHCONT { - .loc stdlib.sol, 203 + .loc stdlib.sol, 127 DUP NEWC STSLICE - .loc stdlib.sol, 204 + .loc stdlib.sol, 128 PUSH S2 PAIR POP S2 .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 206 + .loc stdlib.sol, 130 DROP .loc stdlib.sol, 0 } +.fragment __appendStringToStringBuilderWithNoShift, { + .loc stdlib.sol, 134 + DUP + CTOS + .loc stdlib.sol, 135 + PUSHCONT { + .loc stdlib.sol, 136 + PUSH S2 + UNPAIR + XCPU2 S1, S2, S2 + SBITS + LDSLICEX + POP S4 + STSLICER + SWAP + PAIR + POP S3 + .loc stdlib.sol, 137 + DUP + SEMPTY + IFRETALT + .loc stdlib.sol, 139 + NEWC + PUSH S3 + PAIR + POP S3 + .loc stdlib.sol, 140 + LDREFRTOS + NIP + .loc stdlib.sol, 0 + } + AGAINBRK + .loc stdlib.sol, 142 + DROP2 + .loc stdlib.sol, 0 +} + .fragment __appendStringToStringBuilder, { - .loc stdlib.sol, 210 + .loc stdlib.sol, 146 + NULL + .loc stdlib.sol, 147 + PUSH S2 + FIRST + BBITS + PUSHCONT { + .loc stdlib.sol, 148 + ROTREV + CALLREF { + .inline __appendStringToStringBuilderWithNoShift + } + NIP + .loc stdlib.sol, 0 + } + IFNOTJMP + .loc stdlib.sol, 150 + OVER + CTOS + .loc stdlib.sol, 151 PUSHCONT { - .loc stdlib.sol, 211 + .loc stdlib.sol, 152 BLKPUSH 2, 0 SBITS LDSLICEX POP S2 - PUXC S2, S-1 - .loc stdlib.sol, 212 + PUXC S4, S-1 + .loc stdlib.sol, 153 CALLREF { .inline __appendSliceToStringBuilder } - POP S2 - .loc stdlib.sol, 213 + POP S4 + .loc stdlib.sol, 154 DUP SEMPTY IFRETALT - .loc stdlib.sol, 215 + .loc stdlib.sol, 156 LDREFRTOS NIP .loc stdlib.sol, 0 } AGAINBRK - .loc stdlib.sol, 217 - DROP + .loc stdlib.sol, 158 + BLKDROP 3 + .loc stdlib.sol, 0 +} + +.fragment __createStringBuilder, { + .loc stdlib.sol, 90 + NEWC + NULL + PAIR .loc stdlib.sol, 0 } .fragment __makeString, { - .loc stdlib.sol, 344 + .loc stdlib.sol, 96 + UNPAIR + SWAP + .loc stdlib.sol, 97 PUSHCONT { - DUP - SECOND + OVER ISNULL NOT } PUSHCONT { - .loc stdlib.sol, 345 - DUP - FIRST - .loc stdlib.sol, 346 - SWAP - SECOND - DUP - ISNULL - THROWIF 63 - .loc stdlib.sol, 347 - DUP - FIRST - ROT - STBREFR - SETINDEX 0 + .loc stdlib.sol, 98 + OVER + UNPAIR + POP S3 + .loc stdlib.sol, 99 + STBREF .loc stdlib.sol, 0 } WHILE - .loc stdlib.sol, 349 - FIRST + .loc stdlib.sol, 102 ENDC + NIP .loc stdlib.sol, 0 } .fragment __subCell, { - .loc stdlib.sol, 272 + .loc stdlib.sol, 344 PUSH S2 PUSHINT 127 DIVMOD - .loc stdlib.sol, 273 + .loc stdlib.sol, 345 OVER NEQINT 0 OVER EQINT 0 AND PUSHCONT { - .loc stdlib.sol, 274 + .loc stdlib.sol, 346 DROP DEC - .loc stdlib.sol, 275 + .loc stdlib.sol, 347 PUSHINT 127 .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 278 + .loc stdlib.sol, 350 PUSH S5 CTOS - .loc stdlib.sol, 279 + .loc stdlib.sol, 351 PUSH S2 PUSHCONT { - .loc stdlib.sol, 280 + .loc stdlib.sol, 352 DUP SREFS EQINT 1 THROWIFNOT 70 - .loc stdlib.sol, 281 + .loc stdlib.sol, 353 LDREFRTOS NIP .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 284 + .loc stdlib.sol, 356 OVER MULCONST 8 POP S2 - .loc stdlib.sol, 285 + .loc stdlib.sol, 357 DUP SBITS PUSH S2 GEQ THROWIFNOT 70 - .loc stdlib.sol, 286 + .loc stdlib.sol, 358 OVER SDSKIPFIRST - .loc stdlib.sol, 288 + .loc stdlib.sol, 360 PUSH S4 MULCONST 8 POP S5 - .loc stdlib.sol, 289 - NEWC - NULL - PAIR - .loc stdlib.sol, 290 + .loc stdlib.sol, 361 + CALLREF { + .inline __createStringBuilder + } + .loc stdlib.sol, 362 PUSHCONT { - .loc stdlib.sol, 291 + .loc stdlib.sol, 363 OVER SBITS PUSH S6 MIN UFITS 10 - .loc stdlib.sol, 292 + .loc stdlib.sol, 364 PUSH2 S6, S0 SUB POP S7 PUXC S2, S-1 - .loc stdlib.sol, 293 + .loc stdlib.sol, 365 LDSLICEX POP S3 - .loc stdlib.sol, 294 + .loc stdlib.sol, 366 CALLREF { .inline __appendSliceToStringBuilder } - .loc stdlib.sol, 295 + .loc stdlib.sol, 367 PUSH S5 EQINT 0 PUSH S2 SEMPTY OR IFRETALT - .loc stdlib.sol, 298 + .loc stdlib.sol, 370 OVER LDREFRTOS NIP @@ -192,13 +292,13 @@ .loc stdlib.sol, 0 } AGAINBRK - .loc stdlib.sol, 300 + .loc stdlib.sol, 372 BLKSWAP 2, 4 SWAP EQINT 0 OR THROWIFNOT 70 - .loc stdlib.sol, 301 + .loc stdlib.sol, 373 CALLREF { .inline __makeString } @@ -207,14 +307,14 @@ } .fragment __arraySlice, { - .loc stdlib.sol, 266 + .loc stdlib.sol, 338 DUP2 LEQ THROWIFNOT 70 - .loc stdlib.sol, 267 + .loc stdlib.sol, 339 OVER SUB - .loc stdlib.sol, 268 + .loc stdlib.sol, 340 FALSE CALLREF { .inline __subCell @@ -222,57 +322,22 @@ .loc stdlib.sol, 0 } -.fragment __stringToStringBuilder, { - .loc stdlib.sol, 330 - NULL - .loc stdlib.sol, 331 - OVER - CTOS - .loc stdlib.sol, 332 - PUSHCONT { - .loc stdlib.sol, 334 - BLKPUSH 2, 0 - SBITS - LDSLICEX - POP S2 - NEWC - STSLICE - .loc stdlib.sol, 335 - PUSH S2 - PAIR - POP S2 - .loc stdlib.sol, 336 - DUP - SEMPTY - IFRETALT - .loc stdlib.sol, 338 - LDREFRTOS - NIP - .loc stdlib.sol, 0 - } - AGAINBRK - .loc stdlib.sol, 340 - DROP - DUP - ISNULL - THROWIF 63 - NIP - .loc stdlib.sol, 0 -} - .fragment __concatenateStrings, { - .loc stdlib.sol, 353 - SWAP + .loc stdlib.sol, 402 CALLREF { - .inline __stringToStringBuilder + .inline __createStringBuilder } - .loc stdlib.sol, 354 + .loc stdlib.sol, 403 + ROT + CALLREF { + .inline __appendStringToStringBuilderWithNoShift + } + .loc stdlib.sol, 404 SWAP - CTOS CALLREF { .inline __appendStringToStringBuilder } - .loc stdlib.sol, 355 + .loc stdlib.sol, 405 CALLREF { .inline __makeString } @@ -280,10 +345,10 @@ } .fragment __parseInteger, { - .loc stdlib.sol, 47 + .loc stdlib.sol, 73 OVER PUSHCONT { - .loc stdlib.sol, 48 + .loc stdlib.sol, 74 PUSHINT 0 NULL PAIR @@ -292,136 +357,141 @@ .loc stdlib.sol, 0 } IFNOTJMP - .loc stdlib.sol, 50 + .loc stdlib.sol, 76 NULL - .loc stdlib.sol, 51 + .loc stdlib.sol, 77 PUSHINT 0 - .loc stdlib.sol, 52 + .loc stdlib.sol, 78 PUSHCONT { PUSH S3 NEQINT 0 } PUSHCONT { - .loc stdlib.sol, 54 + .loc stdlib.sol, 80 OVER2 DIVMOD POP S5 XCPU S4, S2 - .loc stdlib.sol, 55 + .loc stdlib.sol, 81 PAIR POP S2 - .loc stdlib.sol, 56 + .loc stdlib.sol, 82 INC .loc stdlib.sol, 0 } WHILE - .loc stdlib.sol, 58 + .loc stdlib.sol, 84 BLKDROP2 2, 2 .loc stdlib.sol, 0 } .fragment __convertIntToHexString, { - .loc stdlib.sol, 148 + .loc stdlib.sol, 243 PUSH S3 LESSINT 0 - .loc stdlib.sol, 149 + .loc stdlib.sol, 244 ROLL 4 ABS - .loc stdlib.sol, 150 + .loc stdlib.sol, 245 PUSH S5 FIRST BREMBITS RSHIFT 3 - .loc stdlib.sol, 152 + .loc stdlib.sol, 247 ROT PUSHCONT { - .loc stdlib.sol, 153 + .loc stdlib.sol, 248 DUP PUSHCONT { - .loc stdlib.sol, 154 + .loc stdlib.sol, 249 NEWC PUSH S6 PAIR POP S6 - .loc stdlib.sol, 155 + .loc stdlib.sol, 250 DROP PUSHINT 127 .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 157 - PUSH2 S5, S5 - FIRST - STSLICECONST x2d - SETINDEX 0 + .loc stdlib.sol, 252 + PUSH S5 + UNPAIR + PUSHINT 45 + ROT + STU 8 + SWAP + PAIR POP S6 - .loc stdlib.sol, 158 + .loc stdlib.sol, 253 DEC .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 161 + .loc stdlib.sol, 256 SWAP PUSHINT 16 CALLREF { .inline __parseInteger } - .loc stdlib.sol, 163 + .loc stdlib.sol, 258 PUSH2 S5, S0 GREATER PUSHCONT { - .loc stdlib.sol, 164 + .loc stdlib.sol, 259 PUSH S4 PUSHINT 48 PUSHINT 32 CONDSEL - .loc stdlib.sol, 165 + .loc stdlib.sol, 260 PUSH2 S6, S1 SUB - .loc stdlib.sol, 166 + .loc stdlib.sol, 261 PUSH2 S0, S4 MIN PUSHCONT { - .loc stdlib.sol, 167 - PUSH2 S8, S8 - FIRST - PUSH S3 - STUR 8 - SETINDEX 0 + .loc stdlib.sol, 262 + PUSH S8 + UNPAIR + PUXC S3, S1 + STU 8 + SWAP + PAIR POP S9 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 169 + .loc stdlib.sol, 264 PUSH2 S0, S4 GREATER PUSHCONT { - .loc stdlib.sol, 170 + .loc stdlib.sol, 265 NEWC PUSH S9 PAIR POP S9 - .loc stdlib.sol, 171 + .loc stdlib.sol, 266 PUSH2 S0, S4 SUB PUSHCONT { - .loc stdlib.sol, 172 - PUSH2 S8, S8 - FIRST - PUSH S3 - STUR 8 - SETINDEX 0 + .loc stdlib.sol, 267 + PUSH S8 + UNPAIR + PUXC S3, S1 + STU 8 + SWAP + PAIR POP S9 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 174 + .loc stdlib.sol, 269 PUSH S4 ADDCONST 127 OVER } PUSHCONT { - .loc stdlib.sol, 176 + .loc stdlib.sol, 271 PUSH2 S4, S0 } IFELSE @@ -431,67 +501,69 @@ DROP2 } IF - .loc stdlib.sol, 180 + .loc stdlib.sol, 275 ROLL 3 PUSHINT 97 PUSHINT 65 CONDSEL ADDCONST -10 - .loc stdlib.sol, 181 + .loc stdlib.sol, 276 PUSH2 S1, S3 MIN PUSHCONT { - .loc stdlib.sol, 183 + .loc stdlib.sol, 278 PUSH2 S2, S2 ISNULL THROWIF 63 UNPAIR POP S4 - .loc stdlib.sol, 184 - PUSH2 S7, S7 - FIRST - XC2PU S0, S2, S0 + .loc stdlib.sol, 279 + PUSH S7 + UNPAIR + XCPU S2, S0 LESSINT 10 PUSHINT 48 PUSH S5 CONDSEL ADD STUR 8 - SETINDEX 0 + SWAP + PAIR POP S7 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 186 + .loc stdlib.sol, 281 PUSH2 S1, S3 GREATER PUSHCONT { - .loc stdlib.sol, 187 + .loc stdlib.sol, 282 NEWC PUSH S7 PAIR POP S7 + .loc stdlib.sol, 283 PUSH2 S1, S3 - .loc stdlib.sol, 188 SUB PUSHCONT { - .loc stdlib.sol, 190 + .loc stdlib.sol, 285 PUSH2 S2, S2 ISNULL THROWIF 63 UNPAIR POP S4 - .loc stdlib.sol, 191 - PUSH2 S7, S7 - FIRST - XC2PU S0, S2, S0 + .loc stdlib.sol, 286 + PUSH S7 + UNPAIR + XCPU S2, S0 LESSINT 10 PUSHINT 48 PUSH S5 CONDSEL ADD STUR 8 - SETINDEX 0 + SWAP + PAIR POP S7 .loc stdlib.sol, 0 } @@ -499,15 +571,15 @@ .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 194 + .loc stdlib.sol, 289 BLKDROP 6 .loc stdlib.sol, 0 } .fragment __convertAddressToHexString, { - .loc stdlib.sol, 119 + .loc stdlib.sol, 214 REWRITESTDADDR - .loc stdlib.sol, 120 + .loc stdlib.sol, 215 PUXC S2, S1 PUSHINT 0 DUP @@ -516,14 +588,14 @@ .inline __convertIntToHexString } POP S2 - .loc stdlib.sol, 121 + .loc stdlib.sol, 216 OVER FIRST BREMBITS - .loc stdlib.sol, 122 + .loc stdlib.sol, 217 LESSINT 8 PUSHCONT { - .loc stdlib.sol, 123 + .loc stdlib.sol, 218 NEWC PUSH S2 PAIR @@ -531,13 +603,16 @@ .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 125 - PUSH2 S1, S1 - FIRST - STSLICECONST x3a - SETINDEX 0 + .loc stdlib.sol, 220 + OVER + UNPAIR + PUSHINT 58 + ROT + STU 8 + SWAP + PAIR POP S2 - .loc stdlib.sol, 126 + .loc stdlib.sol, 221 PUSHINT 64 TRUE DUP @@ -548,107 +623,112 @@ } .fragment __convertIntToString, { - .loc stdlib.sol, 69 + .loc stdlib.sol, 164 PUSH S2 LESSINT 0 - .loc stdlib.sol, 70 + .loc stdlib.sol, 165 ROLL 3 ABS - .loc stdlib.sol, 71 + .loc stdlib.sol, 166 PUSH S4 FIRST BREMBITS RSHIFT 3 - .loc stdlib.sol, 73 + .loc stdlib.sol, 168 ROT PUSHCONT { - .loc stdlib.sol, 74 + .loc stdlib.sol, 169 DUP PUSHCONT { - .loc stdlib.sol, 75 + .loc stdlib.sol, 170 NEWC PUSH S5 PAIR POP S5 - .loc stdlib.sol, 76 + .loc stdlib.sol, 171 DROP PUSHINT 127 .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 78 - PUSH2 S4, S4 - FIRST - STSLICECONST x2d - SETINDEX 0 + .loc stdlib.sol, 173 + PUSH S4 + UNPAIR + PUSHINT 45 + ROT + STU 8 + SWAP + PAIR POP S5 - .loc stdlib.sol, 79 + .loc stdlib.sol, 174 DEC .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 82 + .loc stdlib.sol, 177 SWAP PUSHINT 10 CALLREF { .inline __parseInteger } - .loc stdlib.sol, 84 + .loc stdlib.sol, 179 PUSH2 S4, S0 GREATER PUSHCONT { - .loc stdlib.sol, 85 + .loc stdlib.sol, 180 PUSH S3 PUSHINT 48 PUSHINT 32 CONDSEL - .loc stdlib.sol, 86 + .loc stdlib.sol, 181 PUSH2 S5, S1 SUB - .loc stdlib.sol, 87 + .loc stdlib.sol, 182 PUSH2 S0, S4 MIN PUSHCONT { - .loc stdlib.sol, 88 - PUSH2 S7, S7 - FIRST - PUSH S3 - STUR 8 - SETINDEX 0 + .loc stdlib.sol, 183 + PUSH S7 + UNPAIR + PUXC S3, S1 + STU 8 + SWAP + PAIR POP S8 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 90 + .loc stdlib.sol, 185 PUSH2 S0, S4 GREATER PUSHCONT { - .loc stdlib.sol, 91 + .loc stdlib.sol, 186 NEWC PUSH S8 PAIR POP S8 - .loc stdlib.sol, 92 + .loc stdlib.sol, 187 PUSH2 S0, S4 SUB PUSHCONT { - .loc stdlib.sol, 93 - PUSH2 S7, S7 - FIRST - PUSH S3 - STUR 8 - SETINDEX 0 + .loc stdlib.sol, 188 + PUSH S7 + UNPAIR + PUXC S3, S1 + STU 8 + SWAP + PAIR POP S8 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 95 + .loc stdlib.sol, 190 PUSH S4 ADDCONST 127 OVER } PUSHCONT { - .loc stdlib.sol, 97 + .loc stdlib.sol, 192 PUSH2 S4, S0 } IFELSE @@ -658,49 +738,51 @@ DROP2 } IF - .loc stdlib.sol, 101 + .loc stdlib.sol, 196 PUSH2 S0, S2 MIN PUSHCONT { - .loc stdlib.sol, 103 + .loc stdlib.sol, 198 OVER UNPAIR POP S3 - PUSH2 S6, S6 - .loc stdlib.sol, 104 - FIRST - ROT + .loc stdlib.sol, 199 + PUSH S6 + UNPAIR + XCHG S2 ADDCONST 48 STUR 8 - SETINDEX 0 + SWAP + PAIR POP S6 .loc stdlib.sol, 0 } REPEAT - .loc stdlib.sol, 106 + .loc stdlib.sol, 201 PUSH2 S0, S2 GREATER PUSHCONT { - .loc stdlib.sol, 107 + .loc stdlib.sol, 202 NEWC PUSH S6 PAIR POP S6 PUSH2 S0, S2 - .loc stdlib.sol, 108 + .loc stdlib.sol, 203 SUB PUSHCONT { - .loc stdlib.sol, 110 + .loc stdlib.sol, 205 OVER UNPAIR POP S3 - PUSH2 S6, S6 - .loc stdlib.sol, 111 - FIRST - ROT + .loc stdlib.sol, 206 + PUSH S6 + UNPAIR + XCHG S2 ADDCONST 48 STUR 8 - SETINDEX 0 + SWAP + PAIR POP S6 .loc stdlib.sol, 0 } @@ -708,146 +790,348 @@ .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 115 + .loc stdlib.sol, 210 BLKDROP 5 .loc stdlib.sol, 0 } .fragment __convertFixedPointToString, { - .loc stdlib.sol, 130 + .loc stdlib.sol, 225 PUSH S2 LESSINT 0 PUSHCONT { - .loc stdlib.sol, 131 + .loc stdlib.sol, 226 PUSH S3 FIRST BREMBITS LESSINT 8 PUSHCONT { - .loc stdlib.sol, 132 + .loc stdlib.sol, 227 NEWC PUSH S4 PAIR - POP S4 + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 229 + PUSH S3 + UNPAIR + PUSHINT 45 + ROT + STU 8 + SWAP + PAIR + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 231 + ROT + ABS + SWAP + DIVMOD + .loc stdlib.sol, 232 + PUXC S3, S1 + PUSHINT 0 + DUP + CALLREF { + .inline __convertIntToString + } + POP S3 + .loc stdlib.sol, 233 + PUSH S2 + FIRST + BREMBITS + LESSINT 8 + PUSHCONT { + .loc stdlib.sol, 234 + NEWC + PUSH S3 + PAIR + POP S3 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 236 + PUSH S2 + UNPAIR + PUSHINT 46 + ROT + STU 8 + SWAP + PAIR + POP S3 + SWAP + .loc stdlib.sol, 237 + TRUE + CALLREF { + .inline __convertIntToString + } + .loc stdlib.sol, 0 +} + +.fragment __gasGasPrice, { + .loc stdlib.sol, 36 + DUP + EQINT 0 + OVER + EQINT -1 + OR + THROWIFNOT 67 + .loc stdlib.sol, 37 + PUSHINT 20 + PUSHINT 21 + CONDSEL + CONFIGOPTPARAM + .loc stdlib.sol, 38 + DUP + ISNULL + THROWIF 68 + .loc stdlib.sol, 39 + DUP + ISNULL + THROWIF 63 + CTOS + .loc stdlib.sol, 40 + LDU 8 + LDU 64 + LDU 64 + LDU 8 + PLDU 64 + BLKDROP2 4, 1 + .loc stdlib.sol, 0 +} + +.fragment __gasToTon, { + .loc stdlib.sol, 32 + CALLREF { + .inline __gasGasPrice + } + PUSHPOW2 16 + MULDIVC + .loc stdlib.sol, 0 +} + +.fragment __stackReverse, { + .loc stdlib.sol, 597 + NULL + .loc stdlib.sol, 598 + PUSHCONT { + OVER + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 599 + OVER + UNPAIR + POP S3 + SWAP + PAIR + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 600 + NIP + .loc stdlib.sol, 0 +} + +.fragment __stackSort, { + .loc stdlib.sol, 555 + OVER + ISNULL + PUSHCONT { + .loc stdlib.sol, 556 + DROP2 + NULL + .loc stdlib.sol, 0 + } + IFJMP + .loc stdlib.sol, 559 + NULL + .loc stdlib.sol, 560 + PUSHINT 0 + .loc stdlib.sol, 561 + PUSHCONT { + PUSH S3 + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 563 + PUSH S3 + UNPAIR + POP S5 + NULL + PAIR + .loc stdlib.sol, 564 + PUSH S2 + PAIR + POP S2 + .loc stdlib.sol, 561 + INC + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 567 + PUSHCONT { + DUP + GTINT 1 + } + PUSHCONT { + .loc stdlib.sol, 568 + NULL + .loc stdlib.sol, 569 + OVER + MODPOW2 1 + PUSHCONT { + .loc stdlib.sol, 570 + PUSH S2 + UNPAIR + POP S4 + SWAP + PAIR + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 571 + PUSHCONT { + PUSH S2 + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 572 + NULL + .loc stdlib.sol, 573 + PUSH S3 + UNPAIR + .loc stdlib.sol, 574 + UNPAIR + POP S6 + .loc stdlib.sol, 575 + PUSHCONT { + OVER + ISNULL + NOT + OVER + ISNULL + NOT + AND + } + PUSHCONT { + .loc stdlib.sol, 576 + OVER + FIRST + OVER + FIRST + PUSH S8 + PUSH C3 + EXECUTE + PUSHCONT { + .loc stdlib.sol, 577 + BLKPUSH 2, 2 + UNPAIR + POP S4 + } + PUSHCONT { + .loc stdlib.sol, 579 + PUSH2 S2, S0 + UNPAIR + POP S3 + } + IFELSE + SWAP + PAIR + POP S3 + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 581 + PUSHCONT { + OVER + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 582 + BLKPUSH 2, 2 + UNPAIR + POP S4 + SWAP + PAIR + POP S3 + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 583 + PUSHCONT { + DUP + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 584 + PUSH2 S2, S0 + UNPAIR + POP S3 + SWAP + PAIR + POP S3 + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 585 + DROP2 + CALLREF { + .inline __stackReverse + } + .loc stdlib.sol, 586 + SWAP + PAIR .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 134 - PUSH2 S3, S3 - FIRST - STSLICECONST x2d - SETINDEX 0 - POP S4 - .loc stdlib.sol, 0 - } - IF - .loc stdlib.sol, 136 - ROT - ABS - SWAP - DIVMOD - .loc stdlib.sol, 137 - PUXC S3, S1 - PUSHINT 0 - DUP - CALLREF { - .inline __convertIntToString - } - POP S3 - .loc stdlib.sol, 138 - PUSH S2 - FIRST - BREMBITS - LESSINT 8 - PUSHCONT { - .loc stdlib.sol, 139 - NEWC - PUSH S3 - PAIR - POP S3 + WHILE + .loc stdlib.sol, 588 + POP S2 + .loc stdlib.sol, 589 + INC + RSHIFT 1 .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 141 - PUSH2 S2, S2 - FIRST - STSLICECONST x2e - SETINDEX 0 - POP S3 - SWAP - .loc stdlib.sol, 142 - TRUE - CALLREF { - .inline __convertIntToString - } - .loc stdlib.sol, 0 -} - -.fragment __gasGasPrice, { - .loc stdlib.sol, 28 - DUP - EQINT 0 - OVER - EQINT -1 - OR - THROWIFNOT 67 - .loc stdlib.sol, 29 - PUSHINT 20 - PUSHINT 21 - CONDSEL - CONFIGOPTPARAM - .loc stdlib.sol, 30 - DUP - ISNULL - THROWIF 68 - .loc stdlib.sol, 31 - DUP - ISNULL - THROWIF 63 - CTOS - .loc stdlib.sol, 32 - LDU 8 - LDU 64 - LDU 64 - LDU 8 - PLDU 64 - BLKDROP2 4, 1 - .loc stdlib.sol, 0 -} - -.fragment __gasToTon, { - .loc stdlib.sol, 24 - CALLREF { - .inline __gasGasPrice - } - PUSHPOW2 16 - MULDIVC + WHILE + .loc stdlib.sol, 591 + DROP + UNPAIR + DROP + BLKDROP2 2, 1 .loc stdlib.sol, 0 } .fragment __strstr, { - .loc stdlib.sol, 402 + .loc stdlib.sol, 452 NULL - .loc stdlib.sol, 403 + .loc stdlib.sol, 453 PUSH S2 CTOS - .loc stdlib.sol, 404 + .loc stdlib.sol, 454 PUSH S2 CTOS - .loc stdlib.sol, 405 + .loc stdlib.sol, 455 PUSHINT 0 FALSE ; decl return flag PUSHCONT { - .loc stdlib.sol, 406 + .loc stdlib.sol, 456 OVER2 PUSHCONT { - .loc stdlib.sol, 378 + .loc stdlib.sol, 428 OVER SBITS - .loc stdlib.sol, 379 + .loc stdlib.sol, 429 OVER SBITS - .loc stdlib.sol, 380 + .loc stdlib.sol, 430 FALSE ; decl return flag PUSHCONT { PUSH S3 @@ -855,10 +1139,10 @@ NOT } PUSHCONT { - .loc stdlib.sol, 381 + .loc stdlib.sol, 431 PUSH S2 PUSHCONT { - .loc stdlib.sol, 382 + .loc stdlib.sol, 432 PUSH S4 SREFS PUSHCONT { @@ -868,43 +1152,43 @@ RETALT } IFNOTJMP - .loc stdlib.sol, 384 + .loc stdlib.sol, 434 PUSH S4 LDREFRTOS XCPU S6, S6 BLKDROP2 2, 1 - .loc stdlib.sol, 385 + .loc stdlib.sol, 435 SBITS POP S3 .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 387 + .loc stdlib.sol, 437 OVER PUSHCONT { - .loc stdlib.sol, 388 + .loc stdlib.sol, 438 PUSH S3 LDREFRTOS XCPU S5, S5 BLKDROP2 2, 1 - .loc stdlib.sol, 389 + .loc stdlib.sol, 439 SBITS POP S2 .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 391 + .loc stdlib.sol, 441 BLKPUSH 2, 2 MIN - .loc stdlib.sol, 392 + .loc stdlib.sol, 442 PUSH2 S5, S0 LDSLICEX POP S7 + .loc stdlib.sol, 443 PUSH2 S5, S1 - .loc stdlib.sol, 393 LDSLICEX POP S7 - .loc stdlib.sol, 394 + .loc stdlib.sol, 444 SDEQ PUSHCONT { BLKDROP 6 @@ -913,11 +1197,11 @@ RETALT } IFNOTJMP - .loc stdlib.sol, 396 + .loc stdlib.sol, 446 PUSH2 S3, S0 SUB POP S4 - .loc stdlib.sol, 397 + .loc stdlib.sol, 447 PUSH S2 SUBR POP S2 @@ -925,10 +1209,10 @@ } WHILEBRK IFRET - .loc stdlib.sol, 399 + .loc stdlib.sol, 449 BLKDROP 4 TRUE - .loc stdlib.sol, 377 + .loc stdlib.sol, 427 } CALLX .loc stdlib.sol, 0 @@ -939,15 +1223,15 @@ RETALT } IFJMP - .loc stdlib.sol, 408 + .loc stdlib.sol, 458 PUSH S3 SEMPTY IFRETALT - .loc stdlib.sol, 410 + .loc stdlib.sol, 460 PUSH S3 SBITS PUSHCONT { - .loc stdlib.sol, 411 + .loc stdlib.sol, 461 PUSH S3 LDREFRTOS NIP @@ -955,12 +1239,12 @@ .loc stdlib.sol, 0 } IFNOT - .loc stdlib.sol, 412 + .loc stdlib.sol, 462 PUSH S3 LDU 8 XCPU S5, S3 BLKDROP2 2, 1 - .loc stdlib.sol, 413 + .loc stdlib.sol, 463 INC POP S2 .loc stdlib.sol, 0 @@ -969,17 +1253,15 @@ EQINT 4 IFRET BLKDROP 6 - .loc stdlib.sol, 415 + .loc stdlib.sol, 465 NULL .loc stdlib.sol, 0 } .fragment __toLowerCase, { - .loc stdlib.sol, 419 - NEWC - NULL - PAIR - .loc stdlib.sol, 420 + .loc stdlib.sol, 470 + .inline __createStringBuilder + .loc stdlib.sol, 471 SWAP CTOS NULL @@ -999,43 +1281,28 @@ IFNOT BLKDROP2 2, 2 XCPU2 S1, S0, S0 - .loc stdlib.sol, 422 + .loc stdlib.sol, 473 GTINT 64 OVER LESSINT 91 AND PUSHCONT { - .loc stdlib.sol, 423 + .loc stdlib.sol, 474 ADDCONST 32 .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 425 - PUSH S3 - FIRST - BREMBITS - LESSINT 8 - PUSHCONT { - .loc stdlib.sol, 426 - NEWC - PUSH S4 - PAIR - POP S4 - .loc stdlib.sol, 0 + .loc stdlib.sol, 475 + PUXC S3, S-1 + CALLREF { + .inline __appendBytes1 } - IF - .loc stdlib.sol, 428 - PUSH2 S3, S3 - FIRST - XCHG S1, S2 - STU 8 - SETINDEX 0 POP S3 .loc stdlib.sol, 0 } WHILE DROP2 - .loc stdlib.sol, 430 + .loc stdlib.sol, 477 CALLREF { .inline __makeString } @@ -1043,11 +1310,9 @@ } .fragment __toUpperCase, { - .loc stdlib.sol, 434 - NEWC - NULL - PAIR - .loc stdlib.sol, 435 + .loc stdlib.sol, 482 + .inline __createStringBuilder + .loc stdlib.sol, 483 SWAP CTOS NULL @@ -1067,43 +1332,28 @@ IFNOT BLKDROP2 2, 2 XCPU2 S1, S0, S0 - .loc stdlib.sol, 437 + .loc stdlib.sol, 485 GTINT 96 OVER LESSINT 123 AND PUSHCONT { - .loc stdlib.sol, 438 + .loc stdlib.sol, 486 ADDCONST -32 .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 440 - PUSH S3 - FIRST - BREMBITS - LESSINT 8 - PUSHCONT { - .loc stdlib.sol, 441 - NEWC - PUSH S4 - PAIR - POP S4 - .loc stdlib.sol, 0 + .loc stdlib.sol, 487 + PUXC S3, S-1 + CALLREF { + .inline __appendBytes1 } - IF - .loc stdlib.sol, 443 - PUSH2 S3, S3 - FIRST - XCHG S1, S2 - STU 8 - SETINDEX 0 POP S3 .loc stdlib.sol, 0 } WHILE DROP2 - .loc stdlib.sol, 445 + .loc stdlib.sol, 489 CALLREF { .inline __makeString } @@ -1111,7 +1361,7 @@ } .fragment __tonToGas, { - .loc stdlib.sol, 20 + .loc stdlib.sol, 28 PUSHPOW2 16 SWAP CALLREF { @@ -1122,12 +1372,12 @@ } .fragment __replayProtection, { - .loc stdlib.sol, 14 + .loc stdlib.sol, 22 GETGLOB 3 OVER LESS THROWIFNOT 52 - .loc stdlib.sol, 15 + .loc stdlib.sol, 23 DUP NOW PUSHINT 1000 @@ -1136,61 +1386,134 @@ ADD LESS THROWIFNOT 52 - .loc stdlib.sol, 16 + .loc stdlib.sol, 24 SETGLOB 3 .loc stdlib.sol, 0 } .fragment __exp, { - .loc stdlib.sol, 36 + .loc stdlib.sol, 45 + DUP2 + OR + THROWIFNOT 69 + .loc stdlib.sol, 46 PUSHINT 1 - .loc stdlib.sol, 37 + .loc stdlib.sol, 47 PUSHCONT { OVER NEQINT 0 } PUSHCONT { - .loc stdlib.sol, 38 + .loc stdlib.sol, 48 OVER MODPOW2 1 PUSHCONT { - .loc stdlib.sol, 39 + .loc stdlib.sol, 49 PUSH S2 MUL .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 40 + .loc stdlib.sol, 50 PUSH2 S2, S2 MUL POP S3 - .loc stdlib.sol, 41 + .loc stdlib.sol, 51 + OVER + RSHIFT 1 + POP S2 + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 53 + BLKDROP2 2, 1 + .loc stdlib.sol, 0 +} + +.fragment __qexp, { + .loc stdlib.sol, 57 + DUP + ISNAN + DUP + PUSHCONT { + DROP + BLKPUSH 2, 0 + ISNAN + THROWIF 80 + EQINT 0 + PUSH S2 + ISNAN + NOT + AND + DUP + PUSHCONT { + DROP + PUSH2 S1, S1 + ISNAN + THROWIF 80 + EQINT 0 + } + IF + } + IFNOT + PUSHCONT { + DROP2 + PUSHNAN + } + IFJMP + .loc stdlib.sol, 61 + DUP + ISNAN + THROWIF 80 + .loc stdlib.sol, 62 + PUSHINT 1 + .loc stdlib.sol, 63 + PUSHCONT { + OVER + NEQINT 0 + } + PUSHCONT { + .loc stdlib.sol, 64 + OVER + MODPOW2 1 + PUSHCONT { + .loc stdlib.sol, 65 + PUSH S2 + QMUL + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 66 + PUSH2 S2, S2 + QMUL + POP S3 + .loc stdlib.sol, 67 OVER RSHIFT 1 POP S2 .loc stdlib.sol, 0 } WHILE - .loc stdlib.sol, 43 + .loc stdlib.sol, 69 BLKDROP2 2, 1 .loc stdlib.sol, 0 } .fragment __stoi, { - .loc stdlib.sol, 221 + .loc stdlib.sol, 293 CTOS - .loc stdlib.sol, 222 + .loc stdlib.sol, 294 DUP SBITS LESSINT 8 PUSHCONT { - .loc stdlib.sol, 223 + .loc stdlib.sol, 295 DROP NULL .loc stdlib.sol, 0 } IFJMP - .loc stdlib.sol, 226 + .loc stdlib.sol, 298 DUP SBITS GTINT 7 @@ -1202,10 +1525,10 @@ EQINT 45 } IF - .loc stdlib.sol, 227 + .loc stdlib.sol, 299 DUP PUSHCONT { - .loc stdlib.sol, 228 + .loc stdlib.sol, 300 OVER PUSHINT 8 SDSKIPFIRST @@ -1213,7 +1536,7 @@ .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 230 + .loc stdlib.sol, 302 OVER SBITS GTINT 15 @@ -1226,10 +1549,10 @@ EQUAL } IF - .loc stdlib.sol, 231 + .loc stdlib.sol, 303 DUP PUSHCONT { - .loc stdlib.sol, 232 + .loc stdlib.sol, 304 PUSH S2 PUSHINT 16 SDSKIPFIRST @@ -1237,36 +1560,36 @@ .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 234 + .loc stdlib.sol, 306 PUSHINT 0 - .loc stdlib.sol, 235 + .loc stdlib.sol, 307 PUSH S3 SBITS RSHIFT 3 - .loc stdlib.sol, 236 + .loc stdlib.sol, 308 FALSE ; decl return flag ROLL 3 PUSHCONT { - .loc stdlib.sol, 237 + .loc stdlib.sol, 309 FALSE ; decl return flag PUSH S2 PUSHCONT { - .loc stdlib.sol, 238 + .loc stdlib.sol, 310 PUSH S5 LDU 8 POP S7 - .loc stdlib.sol, 239 + .loc stdlib.sol, 311 PUSH S4 MULCONST 16 POP S5 - .loc stdlib.sol, 240 + .loc stdlib.sol, 312 DUP GTINT 47 OVER LESSINT 58 AND PUSHCONT { - .loc stdlib.sol, 241 + .loc stdlib.sol, 313 DUP ADDCONST -48 PUSH S5 @@ -1281,7 +1604,7 @@ LESSINT 71 AND PUSHCONT { - .loc stdlib.sol, 243 + .loc stdlib.sol, 315 DUP ADDCONST -55 PUSH S5 @@ -1296,7 +1619,7 @@ LESSINT 103 AND PUSHCONT { - .loc stdlib.sol, 245 + .loc stdlib.sol, 317 DUP ADDCONST -87 PUSH S5 @@ -1305,7 +1628,7 @@ .loc stdlib.sol, 0 } PUSHCONT { - .loc stdlib.sol, 247 + .loc stdlib.sol, 319 BLKDROP 7 NULL PUSHINT 4 @@ -1327,15 +1650,15 @@ .loc stdlib.sol, 0 } PUSHCONT { - .loc stdlib.sol, 251 + .loc stdlib.sol, 323 FALSE ; decl return flag PUSH S2 PUSHCONT { - .loc stdlib.sol, 252 + .loc stdlib.sol, 324 PUSH S5 LDU 8 POP S7 - .loc stdlib.sol, 253 + .loc stdlib.sol, 325 DUP LESSINT 48 OVER @@ -1348,7 +1671,7 @@ RETALT } IFJMP - .loc stdlib.sol, 255 + .loc stdlib.sol, 327 PUSH S4 MULCONST 10 SWAP @@ -1365,51 +1688,51 @@ } IFELSE IFRET - .loc stdlib.sol, 258 + .loc stdlib.sol, 330 DROP SWAP PUSHCONT { - .loc stdlib.sol, 259 + .loc stdlib.sol, 331 NEGATE .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 260 + .loc stdlib.sol, 332 NIP .loc stdlib.sol, 0 } .fragment __compareStrings, { - .loc stdlib.sol, 308 + .loc stdlib.sol, 380 SWAP CTOS - .loc stdlib.sol, 309 + .loc stdlib.sol, 381 SWAP CTOS - .loc stdlib.sol, 310 + .loc stdlib.sol, 382 FALSE ; decl return flag PUSHCONT { - .loc stdlib.sol, 311 + .loc stdlib.sol, 383 BLKPUSH 2, 2 SDLEXCMP - .loc stdlib.sol, 312 + .loc stdlib.sol, 384 DUP PUSHCONT { - .loc stdlib.sol, 313 + .loc stdlib.sol, 385 BLKDROP2 3, 1 PUSHINT 4 RETALT .loc stdlib.sol, 0 } IFJMP - .loc stdlib.sol, 315 + .loc stdlib.sol, 387 DROP PUSH S2 SREFS - .loc stdlib.sol, 316 + .loc stdlib.sol, 388 PUSH S2 SREFS - .loc stdlib.sol, 317 + .loc stdlib.sol, 389 DUP2 GREATER PUSHCONT { @@ -1419,7 +1742,7 @@ RETALT } IFJMP - .loc stdlib.sol, 319 + .loc stdlib.sol, 391 PUSH2 S0, S1 GREATER PUSHCONT { @@ -1429,7 +1752,7 @@ RETALT } IFJMP - .loc stdlib.sol, 321 + .loc stdlib.sol, 393 ADD PUSHCONT { BLKDROP 3 @@ -1438,12 +1761,12 @@ RETALT } IFNOTJMP - .loc stdlib.sol, 323 + .loc stdlib.sol, 395 PUSH S2 LDREFRTOS XCPU S4, S3 BLKDROP2 2, 1 - .loc stdlib.sol, 324 + .loc stdlib.sol, 396 LDREFRTOS NIP POP S2 @@ -1451,18 +1774,18 @@ } AGAINBRK IFRET - .loc stdlib.sol, 326 + .loc stdlib.sol, 398 DROP2 PUSHINT 0 .loc stdlib.sol, 0 } .fragment __strchr, { - .loc stdlib.sol, 358 + .loc stdlib.sol, 408 NULL - .loc stdlib.sol, 359 + .loc stdlib.sol, 409 PUSHINT 0 - .loc stdlib.sol, 360 + .loc stdlib.sol, 410 ROLL 3 CTOS NULL @@ -1483,7 +1806,7 @@ IFNOT BLKDROP2 2, 3 XCPU2 S2, S1, S5 - .loc stdlib.sol, 361 + .loc stdlib.sol, 411 EQUAL PUSHCONT { XCHG S3 @@ -1492,7 +1815,7 @@ RETALT } IFJMP - .loc stdlib.sol, 363 + .loc stdlib.sol, 413 PUSH S3 INC POP S4 @@ -1506,11 +1829,11 @@ } .fragment __strrchr, { - .loc stdlib.sol, 367 + .loc stdlib.sol, 417 NULL - .loc stdlib.sol, 368 + .loc stdlib.sol, 418 PUSHINT 0 - .loc stdlib.sol, 369 + .loc stdlib.sol, 419 ROLL 3 CTOS NULL @@ -1530,16 +1853,16 @@ IFNOT BLKDROP2 2, 2 XCPU2 S1, S0, S4 - .loc stdlib.sol, 370 + .loc stdlib.sol, 420 EQUAL PUSHCONT { - .loc stdlib.sol, 371 + .loc stdlib.sol, 421 PUSH S2 POP S4 .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 372 + .loc stdlib.sol, 422 PUSH S2 INC POP S3 @@ -1552,21 +1875,21 @@ } .fragment __stateInitHash, { - .loc stdlib.sol, 449 + .loc stdlib.sol, 494 NEWC - .loc stdlib.sol, 451 + .loc stdlib.sol, 496 STSLICECONST x020134 - .loc stdlib.sol, 463 + .loc stdlib.sol, 508 ROT STUR 16 - .loc stdlib.sol, 464 + .loc stdlib.sol, 509 STU 16 - .loc stdlib.sol, 466 + .loc stdlib.sol, 511 ROT STUR 256 - .loc stdlib.sol, 467 + .loc stdlib.sol, 512 STU 256 - .loc stdlib.sol, 468 + .loc stdlib.sol, 513 ENDC CTOS SHA256U @@ -1574,23 +1897,23 @@ } .fragment __forwardFee, { - .loc stdlib.sol, 472 + .loc stdlib.sol, 517 DEPTH ADDCONST -3 PICK CTOS - .loc stdlib.sol, 473 + .loc stdlib.sol, 518 LDU 1 SWAP - .loc stdlib.sol, 474 + .loc stdlib.sol, 519 PUSHCONT { - .loc stdlib.sol, 485 + .loc stdlib.sol, 530 DROP PUSHINT 0 .loc stdlib.sol, 0 } PUSHCONT { - .loc stdlib.sol, 479 + .loc stdlib.sol, 524 LDU 3 LDMSGADDR LDMSGADDR @@ -1598,7 +1921,7 @@ LDDICT LDVARUINT16 BLKDROP2 6, 1 - .loc stdlib.sol, 483 + .loc stdlib.sol, 528 LDVARUINT16 DROP .loc stdlib.sol, 0 @@ -1608,28 +1931,28 @@ } .fragment __importFee, { - .loc stdlib.sol, 490 + .loc stdlib.sol, 535 DEPTH ADDCONST -3 PICK CTOS - .loc stdlib.sol, 491 + .loc stdlib.sol, 536 LDU 2 SWAP - .loc stdlib.sol, 492 + .loc stdlib.sol, 537 EQINT 2 PUSHCONT { - .loc stdlib.sol, 495 + .loc stdlib.sol, 540 LDMSGADDR LDMSGADDR BLKDROP2 2, 1 - .loc stdlib.sol, 496 + .loc stdlib.sol, 541 LDVARUINT16 DROP .loc stdlib.sol, 0 } PUSHCONT { - .loc stdlib.sol, 498 + .loc stdlib.sol, 543 DROP PUSHINT 0 .loc stdlib.sol, 0 @@ -1638,3 +1961,81 @@ .loc stdlib.sol, 0 } +.fragment __qand, { + .loc stdlib.sol, 604 + OVER + ISNAN + DUP + PUSHCONT { + DROP + PUSH2 S1, S1 + ISNAN + THROWIF 80 + NEQINT 0 + } + IFNOT + DUP + PUSHCONT { + DROP + DUP + ISNAN + DUP + PUSHCONT { + DROP + BLKPUSH 2, 0 + ISNAN + THROWIF 80 + NEQINT 0 + } + IFNOT + } + IF + PUSHCONT { + QAND + } + IFJMP + .loc stdlib.sol, 608 + DROP2 + PUSHINT 0 + .loc stdlib.sol, 0 +} + +.fragment __qor, { + .loc stdlib.sol, 612 + OVER + ISNAN + DUP + PUSHCONT { + DROP + PUSH2 S1, S1 + ISNAN + THROWIF 80 + NEQINT -1 + } + IFNOT + DUP + PUSHCONT { + DROP + DUP + ISNAN + DUP + PUSHCONT { + DROP + BLKPUSH 2, 0 + ISNAN + THROWIF 80 + NEQINT -1 + } + IFNOT + } + IF + PUSHCONT { + QOR + } + IFJMP + .loc stdlib.sol, 616 + DROP2 + PUSHINT -1 + .loc stdlib.sol, 0 +} + diff --git a/sold/Cargo.toml b/sold/Cargo.toml index 143546a7..450179b2 100644 --- a/sold/Cargo.toml +++ b/sold/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2021' name = 'sold' -version = '0.73.0' +version = '0.74.0' [[bin]] name = 'sold' @@ -11,22 +11,22 @@ path = 'src/main.rs' atty = '0.2' dunce = '1.0' failure = '0.1' -once_cell = '1.18' +once_cell = '1.19' serde_json = { features = [ 'unbounded_depth' ], version = '1.0' } strip-ansi-escapes = '0.2' -clap = { features = [ 'derive' ], version = '4.3' } +clap = { features = [ 'derive' ], version = '4.5' } serde = { features = [ 'derive' ], version = '1.0' } -ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.4.14' } -ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.122' } -ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.32' } -ton_labs_assembler = { features = [ 'gosh' ], git = 'https://github.com/tonlabs/ever-assembler.git', tag = '1.4.39' } +ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.4.25' } +ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.141' } +ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.39' } +ton_labs_assembler = { features = [ 'gosh' ], git = 'https://github.com/tonlabs/ever-assembler.git', tag = '1.4.52' } [build-dependencies] cmake = '0.1' [dev-dependencies] assert_cmd = '2.0' -predicates = '3.0' +predicates = '3.1' [lib] name = 'sold_lib' diff --git a/sold/tests/AbiJson.sol b/sold/tests/AbiJson.sol index 2995ef4e..f3f1853f 100644 --- a/sold/tests/AbiJson.sol +++ b/sold/tests/AbiJson.sol @@ -1,3 +1,3 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; abstract contract Contract { } diff --git a/sold/tests/Abstract.sol b/sold/tests/Abstract.sol index 6903eba9..0e14d237 100644 --- a/sold/tests/Abstract.sol +++ b/sold/tests/Abstract.sol @@ -1,3 +1,3 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; abstract contract Abstract { } diff --git a/sold/tests/Combined.sol b/sold/tests/Combined.sol index e835211e..45793902 100644 --- a/sold/tests/Combined.sol +++ b/sold/tests/Combined.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; interface Interface { } contract Combined { diff --git a/sold/tests/CycleA.sol b/sold/tests/CycleA.sol index 7efdded7..3b4e1e20 100644 --- a/sold/tests/CycleA.sol +++ b/sold/tests/CycleA.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; import "CycleB.sol"; contract CycleA { } diff --git a/sold/tests/CycleB.sol b/sold/tests/CycleB.sol index 7aefe3b2..1e6d2538 100644 --- a/sold/tests/CycleB.sol +++ b/sold/tests/CycleB.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; import "CycleA.sol"; contract CycleB { } diff --git a/sold/tests/ErrorReporting.sol b/sold/tests/ErrorReporting.sol index b0f60f29..54eb09a8 100644 --- a/sold/tests/ErrorReporting.sol +++ b/sold/tests/ErrorReporting.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; contract Require { function foo() public pure { require(false, 65536); diff --git a/sold/tests/FunctionId.sol b/sold/tests/FunctionId.sol index fdc501fe..d1e3b67d 100644 --- a/sold/tests/FunctionId.sol +++ b/sold/tests/FunctionId.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.5.0; +pragma tvm-solidity >=0.5.0; library Math { function mul(uint a, uint b) public returns (uint) { diff --git a/sold/tests/ImportRemote.sol b/sold/tests/ImportRemote.sol index 8b60acca..48f576a0 100644 --- a/sold/tests/ImportRemote.sol +++ b/sold/tests/ImportRemote.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.66.0; +pragma tvm-solidity >=0.66.0; import "github.com/tonlabs/debots/Remote.sol"; contract ImportRemote { } diff --git a/sold/tests/Init.sol b/sold/tests/Init.sol index 2ca0a3a0..c8c78a72 100644 --- a/sold/tests/Init.sol +++ b/sold/tests/Init.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; contract Init { uint256 field1; string field2; diff --git a/sold/tests/Library.sol b/sold/tests/Library.sol index 9808192a..8ea03305 100644 --- a/sold/tests/Library.sol +++ b/sold/tests/Library.sol @@ -1,3 +1,3 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; library Library { } diff --git a/sold/tests/Multi.sol b/sold/tests/Multi.sol index b74a24ba..99c42ae6 100644 --- a/sold/tests/Multi.sol +++ b/sold/tests/Multi.sol @@ -1,4 +1,4 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; contract Contract1 { } contract Contract2 { diff --git a/sold/tests/Trivial.sol b/sold/tests/Trivial.sol index 7aa59bf5..b9f864ac 100644 --- a/sold/tests/Trivial.sol +++ b/sold/tests/Trivial.sol @@ -1,3 +1,3 @@ -pragma ever-solidity >=0.50.0; +pragma tvm-solidity >=0.50.0; contract Trivial { } diff --git a/sold/tests/remote/Remote.sol b/sold/tests/remote/Remote.sol index 60787214..d8e6d223 100644 --- a/sold/tests/remote/Remote.sol +++ b/sold/tests/remote/Remote.sol @@ -1,3 +1,3 @@ -pragma ever-solidity >=0.66.0; +pragma tvm-solidity >=0.66.0; contract Remote { }