diff --git a/API.md b/API.md index 3a10925c..51e35f69 100644 --- a/API.md +++ b/API.md @@ -13,6 +13,8 @@ TVM Solidity compiler expands Solidity language with different API functions to * [\.dataSize()](#tvmcelldatasize) * [\.dataSizeQ()](#tvmcelldatasizeq) * [\.toSlice()](#tvmcelltoslice) + * [\.exoticToSlice()](#tvmcellexotictoslice) + * [\.loadExoticCell() and \.loadExoticCellQ()](#tvmcellloadexoticcell-and-tvmcellloadexoticcellq) * [TvmSlice](#tvmslice) * [\.empty()](#tvmsliceempty) * [\.size()](#tvmslicesize) @@ -48,6 +50,7 @@ TVM Solidity compiler expands Solidity language with different API functions to * [TvmBuilder](#tvmbuilder) * [\.toSlice()](#tvmbuildertoslice) * [\.toCell()](#tvmbuildertocell) + * [\.toExoticCell()](#tvmbuildertoexoticcell) * [\.size()](#tvmbuildersize) * [\.bits()](#tvmbuilderbits) * [\.refs()](#tvmbuilderrefs) @@ -62,7 +65,6 @@ TVM Solidity compiler expands Solidity language with different API functions to * [Store little-endian integers](#store-little-endian-integers) * [\.storeRef()](#tvmbuilderstoreref) * [\.storeTons()](#tvmbuilderstoretons) - * [ExtraCurrencyCollection](#extracurrencycollection) * [optional(T)](#optionalt) * [constructing an optional](#constructing-an-optional) * [\.hasValue()](#optionalthasvalue) @@ -83,6 +85,7 @@ TVM Solidity compiler expands Solidity language with different API functions to * [Range-based for loop](#range-based-for-loop) * [repeat](#repeat) * [try-catch](#try-catch) + * [unchecked block](#unchecked-block) * [Changes and extensions in Solidity types](#changes-and-extensions-in-solidity-types) * [Integers](#integers) * [bitSize() and uBitSize()](#bitsize-and-ubitsize) @@ -284,6 +287,7 @@ TVM Solidity compiler expands Solidity language with different API functions to * [gasToValue](#gastovalue) * [valueToGas](#valuetogas) * [gasleft](#gasleft) +* [TVM capabilities](#tvm-capabilities) * [TVM exception codes](#tvm-exception-codes) * [Solidity runtime errors](#solidity-runtime-errors) * [Division and rounding](#division-and-rounding) @@ -310,7 +314,7 @@ tonos-cli decode tvc [--tvc] [--boc] ### TVM specific types -TVM Solidity compiler expands functionality of some existing types and adds several new TVM specific types: TvmCell, TvmSlice, TvmBuilder and ExtraCurrencyCollection. Full description of these types can be found in [TVM][1] and [Blockchain][2] specifications. +TVM Solidity compiler expands functionality of some existing types and adds several new TVM specific types: TvmCell, TvmSlice and TvmBuilder. Full description of these types can be found in [TVM][1] and [Blockchain][2] specifications. #### TVM units @@ -388,9 +392,34 @@ If **c** is a Null instead of a Cell, returns zero. Returns the number of distinct cells, data bits in the distinct cells and cell references in the distinct cells. If number of the distinct cells -exceeds `n+1` then a cell overflow [exception](#tvm-exception-codes) is thrown. +exceeds `n+1`, then a cell overflow [exception](#tvm-exception-codes) is thrown. This function is a wrapper for the `CDATASIZE` opcode ([TVM][1] - A.11.7). +If `CapFastStorageStatBugfix` and `CapFastStorageStat` are set, then calculate number of total (not distinct) cells and bits. For example: + +```TVMSolidity +TvmBuilder b2; +b2.storeOnes(5); + +TvmBuilder b; +b.storeOnes(10); +b.storeRef(b2); +b.storeRef(b2); +b.storeRef(b2); + +TvmCell cell = b.toCell(); +(uint cells, uint bits, uint refs) = cell.dataSize(100); + +// If `CapFastStorageStatBugfix` and `CapFastStorageStat` are set +// cells == 4 +// bits == 10 + 3 * 5 +// refs == 3 +// Otherwise: +// cells == 2 +// bits == 5 + 10 +// refs == 3 +``` + #### \.dataSizeQ() ```TVMSolidity @@ -399,7 +428,7 @@ This function is a wrapper for the `CDATASIZE` opcode ([TVM][1] - A.11.7). Returns the number of distinct cells, data bits in the distinct cells and cell references in the distinct cells. If number of the distinct cells -exceeds `n+1` then this function returns an `optional` that has no value. +exceeds `n+1`, then this function returns an `optional` that has no value. This function is a wrapper for the `CDATASIZEQ` opcode ([TVM][1] - A.11.7). ##### \.toSlice() @@ -408,7 +437,97 @@ This function is a wrapper for the `CDATASIZEQ` opcode ([TVM][1] - A.11.7). .toSlice() returns (TvmSlice); ``` -Converts a `TvmCell` to `TvmSlice`. +Converts a `TvmCell` to `TvmSlice`. + +If the cell is exotic, then the cell is automatically loaded and converted to `TvmSlice`. For example: + +``` +TvmCell cellProof = ...; +TvmBuilder b; +b.store( + uint8(3), // type of MerkleProof exotic cell + tvm.hash(cellProof), + cellProof.depth(), + cellProof +); +TvmCell merkleProof = b.toExoticCell(); +TvmSlice s = merkleProof.toSlice(); +// `s` has the same data and references as `cellProof` +``` +If you want load the cell as is, then see [\.exoticToSlice()](#tvmcellexotictoslice). + +##### \.exoticToSlice() + +```TVMSolidity +.exoticToSlice() returns (TvmSlice, bool) +``` + +Converts an ordinary or exotic cell into a `TvmSlice`, as if it were an ordinary cell. A flag is returned indicating whether +`TvmCell` is exotic. If that be the case, its type can later be deserialized from the first eight bits of `TvmSlice`. + +Example: + +```TVMSolidity +TvmCell cellProof = ...; +TvmBuilder b; +b.store( + uint8(3), // type of MerkleProof exotic cell + tvm.hash(cellProof), + cellProof.depth(), + cellProof +); + +{ + // convert builder to exotic cell + TvmCell merkleProof = b.toExoticCell(); + (TvmSlice s, bool isExotic) = merkleProof.exoticToSlice(); + // isExotic == true + uint8 flag = s.load(uint8); // flag == 3 +} + +{ + // convert builder to ordinary cell + TvmCell cell = b.toCell(); + (TvmSlice s, bool isExotic) = cell.exoticToSlice(); + // isExotic == false + uint8 flag = s.load(uint8); // flag == 3 +} +``` + +See also: + * [\.toExoticCell()](#tvmbuildertoexoticcell) + + +#### \.loadExoticCell() and \.loadExoticCellQ() + +```TVMSolidity +(1) +.loadExoticCell() returns (TvmCell) +(2) +.loadExoticCellQ() returns (TvmCell cell, bool ok) +``` +(1) Loads an exotic cell and returns an ordinary cell. If the cell is already ordinary, does nothing. If it cannot be loaded, throws an exception. It is wrapper for opcode `XLOAD`. + +(2) Same as (1) but if it cannot be loaded, does not throw exception and `ok` is equal to `false`. It is wrapper for opcode `XLOADQ`. + +Example: + +```TVMSolidity +TvmCell cellProof = ...; +TvmBuilder b; +b.store( + uint8(3), // type of MerkleProof exotic cell + tvm.hash(cellProof), + cellProof.depth(), + cellProof +); + +TvmCell cell = merkleProof.loadExoticCell(); // cell == cellProof + +(TvmCell cell, bool ok) = merkleProof.loadExoticCellQ(); +// cell == cellProof +// ok == true +``` #### TvmSlice @@ -418,7 +537,7 @@ operators and functions to work with this type: Comparison operators: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to bool). -Note: only data bits from the root cells are compared. References are ignored. +**Note:** only data bits from the root cells are compared. References are ignored. `TvmSlice` can be converted to `bytes`. It costs at least 500 gas units. @@ -462,7 +581,7 @@ Returns the number of references in the `TvmSlice`. Returns the number of distinct cells, data bits in the distinct cells and cell references in the distinct cells. If number of the distinct cells -exceeds `n+1` then a cell overflow [exception](#tvm-exception-codes) is thrown. +exceeds `n+1`, then a cell overflow [exception](#tvm-exception-codes) is thrown. Note that the returned `count of distinct cells` does not take into account the cell that contains the slice itself. This function is a wrapper for `SDATASIZE` opcode ([TVM][1] - A.11.7). @@ -475,7 +594,7 @@ This function is a wrapper for `SDATASIZE` opcode ([TVM][1] - A.11.7). Returns the number of distinct cells, data bits in the distinct cells and cell references in the distinct cells. If number of the distinct cells -exceeds `n+1` then this function returns an `optional` that has no value. +exceeds `n+1`, then this function returns an `optional` that has no value. Note that the returned `count of distinct cells` does not take into account the cell that contains the slice itself. This function is a wrapper for `SDATASIZEQ` opcode ([TVM][1] - A.11.7). @@ -523,7 +642,7 @@ All `load*` functions below modify the `TvmSlice` object. If you wants to load s Sequentially loads values of the specified types from the `TvmSlice`. Supported types: `uintN`, `intN`, `bytesN`, `bool`, `ufixedMxN`, `fixedMxN`, `address`, `contract`, -`TvmCell`, `bytes`, `string`, `mapping`, `ExtraCurrencyCollection`, `array`, `optional` and +`TvmCell`, `bytes`, `string`, `mapping`, `array`, `optional` and `struct`. Example: ```TVMSolidity @@ -637,7 +756,9 @@ Loads (deserializes) **VarUInteger 16** and returns an unsigned 128-bit integer. ``` (1) Loads the first `bits` bits from `TvmSlice`. + (2) Loads the first `bits` bits and `refs` references from `TvmSlice`. + (3) and (4) are same as (1) and (2) but return `optional` type. ###### \.loadFunctionParams() @@ -704,7 +825,7 @@ contract B { ```TVMSolidity .skip(uint10 bits); -.skip(uint10 bits, uint2 refs); +.skip(uint10 bits, uint3 refs); ``` Skips the first `bits` bits and `refs` references from the `TvmSlice`. @@ -835,6 +956,28 @@ Converts a `TvmBuilder` into `TvmSlice`. Converts a `TvmBuilder` into `TvmCell`. +##### \.toExoticCell() + +```TVMSolidity +.toExoticCell() returns (TvmCell); +``` + +Creates an exotic cell from `TvmBuilder`. It is wrapper for opcodes `TRUE ENDXC`. + +Examples: + +```TVMSolidity +TvmCell cellProof = getCell(); +TvmBuilder b; +b.store( + uint8(3), // type of MerkleProof exotic cell + tvm.hash(cellProof), + cellProof.depth(), + cellProof +); +TvmCell merkleProof = b.toExoticCell(); +``` + ##### \.size() ```TVMSolidity @@ -913,8 +1056,8 @@ For example, `uint8(100), int16(-3), bytes2(0xaabb)` stored as `0x64fffdaabb`. * `TvmSlice`/`TvmBuilder` - all data bits and references of the `TvmSlice` or the `TvmBuilder` are appended to the `TvmBuilder`, not in a reference as `TvmCell`. To store `TvmSlice`/`TvmBuilder` in the references use [\.storeRef()](#tvmbuilderstoreref). -* `mapping`/`ExtraCurrencyCollection` - stored according to the [TL-B scheme][3] of `HashmapE`: if map is -empty then stored as a binary zero, otherwise as a binary one and the dictionary `Hashmap` in a reference. +* `mapping` - stored according to the [TL-B scheme][3] of `HashmapE`: if map is +empty, then stored as a binary zero, otherwise as a binary one and the dictionary `Hashmap` in a reference. * `array` - stored as a 32 bit value - size of the array and a `HashmapE` that contains all values of the array. * `optional` - stored as a binary zero if the `optional` doesn't contain value. Otherwise, stored as @@ -1009,32 +1152,6 @@ See example of how to work with TVM specific types: * [Message_construction](https://github.com/tonlabs/samples/blob/master/solidity/15_MessageSender.sol) * [Message_parsing](https://github.com/tonlabs/samples/blob/master/solidity/15_MessageReceiver.sol) -#### ExtraCurrencyCollection - -ExtraCurrencyCollection represents TVM type ExtraCurrencyCollection. It has the same functions as [**mapping(uint32 => uint256)**](#mapping): - -```TVMSolidity -ExtraCurrencyCollection curCol; -uint32 key; -uint256 value; -optional(uint32, uint256) res = curCol.min(); -optional(uint32, uint256) res = curCol.next(key); -optional(uint32, uint256) res = curCol.prev(key); -optional(uint32, uint256) res = curCol.nextOrEq(key); -optional(uint32, uint256) res = curCol.prevOrEq(key); -optional(uint32, uint256) res = curCol.delMin(); -optional(uint32, uint256) res = curCol.delMax(); -optional(uint256) optValue = curCol.fetch(key); -bool exists = curCol.exists(key); -bool isEmpty = curCol.empty(); -bool success = curCol.replace(key, value); -bool success = curCol.add(key, value); -optional(uint256) res = curCol.getSet(key, value); -optional(uint256) res = curCol.getAdd(key, value); -optional(uint256) res = curCol.getReplace(key, value); -uint256 uintValue = curCol[index]; -``` - #### optional(T) The template optional type manages an optional contained value, i.e. a value that may or may not be present. @@ -1047,7 +1164,7 @@ There are many ways to set a value: optional(uint) opt; opt.set(11); // just sets value opt = 22; // just sets value, too -opt.get() = 33; // if 'opt' has value then set value. Otherwise throws an exception. +opt.get() = 33; // if 'opt' has value, then set value. Otherwise throws an exception. optional(uint) another = 44; opt = another; @@ -1269,7 +1386,7 @@ repeat(a - 1) { #### try-catch -It is an experimental feature available only in certain blockchain networks. +[Capabilities](#tvm-capabilities) required: `CapsTvmBugfixes2022`. The `try` statement allows you to define a block of code to be tested for errors while it is executed. The `catch` statement allows you to define a block of code to be executed, if an error occurs in the try block. @@ -1329,6 +1446,33 @@ try { } ``` +#### unchecked block + +```TVMSolidity +unchecked { + /* */ +} +``` + +Turns off reverting on over- and underflow for arithmetic operations in `unchecked` block. It is up to programmer to control range of integer types if they use `unchecked` blocks. For example: + +```TVMSolidity + function f(uint8 a, uint8 b) pure private returns (uint8) { + unchecked { + uint8 c = a - b; + return c; + } + } + function g(uint8 a, uint8 b) pure private returns (uint8) { + uint8 c = a - b; + return c; + } +``` + +The call to `f(2, 3)` will return `-1`, while `g(2, 3)` will cause a failing assertion. + +See also: [pragma ignoreIntOverflow](#pragma-ignoreintoverflow). + ### Changes and extensions in Solidity types #### Integers @@ -1456,7 +1600,7 @@ uint[] arr = new uint[](N); // create 5-length array uint[] arr = new uint[](10); // create 10-length array ``` -Note: If `N` is constant expression or integer literal then the complexity of array creation - +Note: If `N` is constant expression or integer literal, then the complexity of array creation - `O(1)`. Otherwise, `O(N)`. ##### \.empty() @@ -1498,7 +1642,7 @@ bytes a = "abzABZ0129"; bytes b = hex"01239abf"; ``` -`bytes` can be converted to `TvmSlice`. Warning: if length of the array is greater than 127 then extra bytes are stored in the first reference of the slice. Use [\.loadRef()](#tvmsliceloadref) to load that extra bytes. +`bytes` can be converted to `TvmSlice`. Warning: if length of the array is greater than 127, then extra bytes are stored in the first reference of the slice. Use [\.loadRef()](#tvmsliceloadref) to load that extra bytes. ##### \.empty() @@ -1857,7 +2001,7 @@ Returns balance of the current contract account in nanoevers. ##### \.currencies ```TVMSolidity -address(this).currencies returns (ExtraCurrencyCollection); +address(this).currencies returns (mapping(uint32 => varUint32)); ``` Returns currencies on the balance of the current contract account. @@ -1919,7 +2063,7 @@ workchain `wid` and the 256-bit address `value`. If the address `value` is not 2 `address` is not a valid serialization of `MsgAddressInt`, throws a cell deserialization [exception](#tvm-exception-codes). -It's wrapper for opcode `REWRITESTDADDR`. +It is wrapper for opcode `REWRITESTDADDR`. Example: @@ -1930,17 +2074,17 @@ Example: ##### \.transfer() ```TVMSolidity -
.transfer(uint128 value, bool bounce, uint16 flag, TvmCell body, ExtraCurrencyCollection currencies, TvmCell stateInit); +
.transfer(uint128 value, bool bounce, uint16 flag, TvmCell body, mapping(uint32 => varUint32) currencies, TvmCell stateInit); ``` Sends an internal outbound message to the `address`. Function parameters: * `value` (`uint128`) - 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` (`ExtraCurrencyCollection`) - 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 +(only at the computing phase, not at the action phase!), then funds will be returned. Otherwise, (flag isn't set or transaction terminated successfully) the address accepts the funds even if the account doesn't exist or is frozen. Defaults to `true`. * `flag` (`uint16`) - flag that used to send the internal outbound message. Defaults to `0`. @@ -1979,7 +2123,7 @@ uint128 value = ...; bool bounce = ...; uint16 flag = ...; TvmCell body = ...; -ExtraCurrencyCollection c = ...; +mapping(uint32 => varUint32) c = ...; TvmCell stateInit = ...; // sequential order of parameters addr.transfer(value); @@ -2029,7 +2173,7 @@ Struct `KeyType` can be used to sort keys of the mapping in ascending order. In above addresses in the mapping are sorted by their keys. `x` field of the Point struct is used in comparison first, second is `y` and the last is `z`. -If `bytes`, `string` or `TvmCell` types are used as `KeyType` then `mapping` stores +If `bytes`, `string` or `TvmCell` types are used as `KeyType`, then `mapping` stores only hashes of mapping keys. That's why for these types the `delMin`/`min`/`next` and another mapping methods return `uint256` as key (not `bytes`/`string`/`TvmCell`). @@ -2135,7 +2279,7 @@ If KeyType is an integer type, argument for this functions can not possibly fit .delMax() returns (optional(KeyType, ValueType)); ``` -If mapping is not empty then this function computes the minimal (maximum) key of the `mapping`, +If mapping is not empty, then this function computes the minimal (maximum) key of the `mapping`, deletes that key and the associated value from the `mapping` and returns an `optional` value containing that key and the associated value. Returns an empty `optional` if there is no such key. @@ -2244,8 +2388,10 @@ Operators: ``` (1) Returns all mapping's keys/values. + (2) Returns all values of the mapping as an array. -Note: these functions iterate over the whole mapping, thus the cost is proportional to the + +**Note:** these functions iterate over the whole mapping, thus the cost is proportional to the mapping's size. ```TVMSolidity @@ -2263,7 +2409,7 @@ Function types are the types of functions. Variables of function type can be ass and function parameters of function type can be used to pass functions to and return functions from function calls. -If unassigned variable of function type is called then exception with code 65 is thrown. +If unassigned variable of function type is called, then exception with code 65 is thrown. ```TVMSolidity function getSum(int a, int b) internal pure returns (int) { @@ -2281,7 +2427,7 @@ function process(int a, int b, uint8 mode) public returns (int) { } else if (mode == 1) { fun = getSub; } - return fun(a, b); // if `fun` isn't initialized then exception is thrown + return fun(a, b); // if `fun` isn't initialized, then exception is thrown } ``` @@ -2290,19 +2436,23 @@ function process(int a, int b, uint8 mode) public returns (int) { In case of exception state variables of the contract are reverted to the state before [tvm.commit()](#tvmcommit) or to the state of the contract before it was called. Use error codes that are greater than 100 because other error codes can be -[reserved](#solidity-runtime-errors). +[reserved](#solidity-runtime-errors). **Note**: if a nonconstant error code is passed as the function argument and the error code -is less than 2 then the error code will be set to 100. +is less than 2, then the error code will be set to 100. ##### require ```TVMSolidity +(1) require(bool condition, [uint errorCode = 100, [Type exceptionArgument]]); +(2) +require(bool condition, string text); ``` -**require** function can be used to check the condition and throw an exception if the condition -is not met. The function takes condition and optional parameters: error code (unsigned integer) -and the object of any type. +`require` function can be used to check the condition and throw an exception if the condition +is not met. +(1) Takes condition and optional parameters: error code and the object of any type. +(2) Takes condition and error text. Error code will be equal to 100. Example: @@ -2310,10 +2460,12 @@ Example: uint a = 5; require(a == 5); // ok + require(a == 6); // throws an exception with code 100 require(a == 6, 101); // throws an exception with code 101 require(a == 6, 101, "a is not equal to six"); // throws an exception with code 101 and string require(a == 6, 101, a); // throws an exception with code 101 and number a +require(a == 6, "a is not equal to six"); // throws an exception with code 100 and string ``` ##### revert @@ -2389,7 +2541,7 @@ contract MyContract { In TVM Solidity **arguments of a function call passed by value not by reference**. It's effective for numbers and even for huge arrays. See ([TVM][1] - A.2.3.2). -**But if a library function is called like `obj.func(b, c)` then the +**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 @@ -2489,7 +2641,7 @@ Used to restrict source file compilation to the particular compiler versions. pragma copyleft , ; ``` -It is an experimental feature available only in certain blockchain deployments. +[Capabilities](#tvm-capabilities) required: `CapCopyleft`. Parameters: * `` (`uint8`) - copyleft type. @@ -2510,7 +2662,18 @@ pragma copyleft 0, 0x2cfbdc31c9c4478b61472c72615182e9567595b857b1bba9e0c31cd9942 pragma ignoreIntOverflow; ``` -Turns off binary operation result overflow check. +All arithmetic operations revert on over- and underflow by default. This pragma turns off such behavior. It is up to programmer to control range of integer types if they use the pragma. + +For example: +```TVMSolidity +pragma ignoreIntOverflow; + +uint8 a = 2; +uint8 b = 3; +uint8 c = a - b; // c == -1, no exception thrown +``` + +See also: [unchecked block](#unchecked-block). #### pragma AbiHeader @@ -2629,7 +2792,7 @@ contract C { 2. [msg.body](#msgbody) starts with 32-bit zero. Then message body may contain data, for example [string](#string) with comment. -If in the contract there is no `receive` function then the contract has an implicit empty `receive` +If in the contract there is no `receive` function, then the contract has an implicit empty `receive` function. ```TVMSolidity @@ -2639,7 +2802,7 @@ contract Sink { uint public msgWithPayload = 0; receive() external { ++counter; - // if the inbound internal message has payload then we can get it using `msg.body` + // if the inbound internal message has payload, then we can get it using `msg.body` TvmSlice s = msg.body; if (!s.empty()) { ++msgWithPayload; @@ -2673,10 +2836,10 @@ contract Bomber { 2. Bit-length of the message is from 1 to 31 (including). 3. Bit-length of the message is equal to zero, but the message contains reference(s). -**Note**: if the message has correct function id but invalid encoded function parameters then +**Note**: if the message has correct function id but invalid encoded function parameters, then the transaction fail with an exception (e.g. [cell underflow exception](#tvm-exception-codes)). -If in the contract there is no fallback function then contract has implicit fallback function +If in the contract there is no fallback function, then contract has implicit fallback function that throws [exception](#solidity-runtime-errors). Example: @@ -2741,23 +2904,34 @@ onBounce(TvmSlice body) external { `onBounce` function is executed when contract receives a bounced inbound internal message. The message is generated by the network if the contract sends an internal message with `bounce: true` and either - -* called contract doesn't exist; -* called contract fails at the storage/credit/computing phase (not at the action phase!) + * called contract doesn't exist; + * called contract fails at the storage/credit/computing phase (not at the action phase!) The message is generated only if the remaining message value is enough for sending one back. -`body` is empty or contains at most **256** data bits of the original message (without references). -The function id takes **32** bits and parameters can take at most **224** bits. -It depends on the network config. If `onBounce` function is not defined then the contract does -nothing on receiving a bounced inbound internal message. +`body` depends on TVM realisation and settings: + 1. `body` can be empty. + 2. If `CapBounceMsgBody` [capability](#tvm-capabilities) is set, then `body` contains at most 256 data bits of the original message (without references). Note: the function id takes `32` bits and the function's parameters can take at most `224` bits. + 3. If `CapBounceMsgBody` and `CapFullBodyInBounced` [capabilities](#tvm-capabilities) are set, then `body` is the same as in the second option, but `body` contains the full original message in the first reference. -If the `onBounce` function throws an exception then another bounced messages are not generated. +If `onBounce` function is not defined, then the contract does +nothing on receiving a bounced message. -Example of how to use `onBounce` function: +If the `onBounce` function throws an exception, then another bounced messages are not generated. + +Example of how to use `onBounce` function for option 2: * [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) +Example of getting function ID if `CapBounceMsgBody` and `CapFullBodyInBounced` [capabilities](#tvm-capabilities) are set: + +```TVMSolidity +onBounce(TvmSlice body) external { + TvmSlice fullBody = body.loadRef().toSlice(); + uint32 functionId = fullBody.load(uint32); +} +``` + #### onTickTock `onTickTock` function is executed on tick/tock transaction. @@ -3014,7 +3188,7 @@ be omitted. See [\.transfer()](#addresstransfer) where these options a values are described. **Note**: if the function `f` below is called with `n = 5` and internal/external message must be -generated then only one message is sent with result `120` (120 = 5 * 4 * 3 * 2 * 1). +generated, then only one message is sent with result `120` (120 = 5 * 4 * 3 * 2 * 1). **Hint:** Use `{value: 0, bounce: false, flag: 64}` for `responsible` function. Because `flag: 0` is used by default. @@ -3040,11 +3214,11 @@ callee function will be called after termination of the current transaction. where these options are described. **Note:** if `value` isn't set, then the default value is equal to 0.01 ever, or 10^7 nanoever. It's equal to 10_000 units of gas in workchain. -If the callee function returns some value and marked as `responsible` then `callback` option must be set. +If the callee function returns some value and marked as `responsible`, then `callback` option must be set. This callback function will be called by another contract. Remote function will pass its return values as function arguments for the callback function. That's why types of return values of the callee function must be equal to function arguments of the callback function. -If the function marked as `responsible` then field `answerId` appears in the list of input parameters of the +If the function marked as `responsible`, then field `answerId` appears in the list of input parameters of the function in `*abi.json` file. `answerId` is function id that will be called. Example of the external call of the function that returns nothing: @@ -3061,7 +3235,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); - ExtraCurrencyCollection cc; + mapping(uint32 => varUint32) cc; cc[12] = 1000; IContract(addr).f{value: 10 ever, currencies:cc}(123); } @@ -3254,7 +3428,7 @@ Returns: ##### msg.currencies ```TVMSolidity -msg.currencies (ExtraCurrencyCollection) +msg.currencies (mapping(uint32 => varUint32)) ``` Collections of arbitrary currencies contained in the balance of @@ -3266,7 +3440,7 @@ the inbound message. msg.pubkey() returns (uint256); ``` -Returns public key that is used to check the message signature. If the message isn't signed then it's equal to `0`. +Returns public key that is used to check the message signature. If the message isn't signed, then it's equal to `0`. See also: [Contract execution](#contract-execution), [pragma AbiHeader](#pragma-abiheader). ##### msg.isInternal, msg.isExternal and msg.isTickTock @@ -3390,7 +3564,7 @@ tvm.commit(); ``` Creates a "check point" of the state variables (by copying them from c7 to c4) and register c5. -If the contract throws an exception at the computing phase then the state variables and register c5 +If the contract throws an exception at the computing phase, then the state variables and register c5 will roll back to the "check point", and the computing phase will be considered "successful". If contract doesn't throw an exception, it has no effect. @@ -3542,7 +3716,7 @@ integer index **paramNumber** as a `TvmCell` and a boolean status. ```TVMSolidity tvm.rawReserve(uint value, uint8 flag); -tvm.rawReserve(uint value, ExtraCurrencyCollection 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 @@ -3579,11 +3753,11 @@ All other values of `flag` are invalid. To make it clear, let's consider the order of `reserve` calculation: -1. if `flag` has bit `+8` then `value = -value`. -2. if `flag` has bit `+4` then `value += original_balance`. +1. if `flag` has bit `+8`, then `value = -value`. +2. if `flag` has bit `+4`, then `value += original_balance`. 3. Check `value >= 0`. -4. if `flag` has bit `+2` then `value = min(value, remaining_balance)`. -5. if `flag` has bit `+1` then `value = remaining_balance - value`. +4. if `flag` has bit `+2`, then `value = min(value, remaining_balance)`. +5. if `flag` has bit `+1`, then `value = remaining_balance - value`. 6. `reserve = value`. 7. Check `0 <= reserve <= remaining_balance`. @@ -3639,14 +3813,16 @@ tvm.checkSign(uint256 dataHash, TvmSlice signature, uint256 pubkey) returns (boo tvm.checkSign(TvmSlice data, TvmSlice signature, uint256 pubkey) returns (bool); ``` -Executes TVM instruction "CHKSIGNU" ([TVM][1] - A.11.6. - F910) for variants 1 and 2. +Executes TVM instruction "CHKSIGNU" ([TVM][1] - A.11.6. - F910) for options 1 and 2. This command checks the Ed25519-signature of the **dataHash** using public key **pubkey**. Signature is represented by two uint256 **SignHighPart** and **SignLowPart** in the -first variant and by the slice **signature** in the second variant. -In the third variant executes TVM instruction "CHKSIGNS" ([TVM][1] - A.11.6. - F911). +first option and by the slice **signature** in the second option. +In the third option executes TVM instruction "CHKSIGNS" ([TVM][1] - A.11.6. - F911). This command checks Ed25519-signature of the **data** using public key **pubkey**. Signature is represented by the slice **signature**. +If `CapSignatureWithId` [capability](#tvm-capabilities) is set, then TVM use some predefined ID during signature check. Usually ID is `global_id` that can be found in the last block for example. + Example: ```TVMSolidity @@ -3678,7 +3854,7 @@ bool signatureIsValid = tvm.checkSign(data, signature, pubkey); tvm.insertPubkey(TvmCell stateInit, uint256 pubkey) returns (TvmCell); ``` -Inserts a public key into the `stateInit` data field. If the `stateInit` has wrong format then throws an exception. +Inserts a public key into the `stateInit` data field. If the `stateInit` has wrong format, then throws an exception. ##### tvm.buildStateInit() @@ -3698,8 +3874,8 @@ Member `splitDepth` of the tree of cell `StateInit`: 1) is not set. Has no value. 2) is set. `0 <= splitDepth <= 31` 3) Arguments can also be set with names. -List of possible names: +List of possible names: * `code` (`TvmCell`) - defines the code field of the `StateInit`. Must be specified. * `data` (`TvmCell`) - defines the data field of the `StateInit`. Conflicts with `pubkey` and `varInit`. Can be omitted, in this case data field would be built from `pubkey` and `varInit`. @@ -3792,6 +3968,7 @@ tvm.stateInitHash(uint256 codeHash, uint256 dataHash, uint16 codeDepth, uint16 d ``` Calculates hash of the stateInit for given code and data specifications. +[Capabilities](#tvm-capabilities) required: `CapInitCodeHash`. Example: @@ -3881,10 +4058,10 @@ The following options can be used with both `stateInit` and `code`: * `value` (`uint128`) - funds attached to the outbound internal message, that creates new account. This value must be set. -* `currencies` (`ExtraCurrencyCollection`) - 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) +phase!), then funds will be returned. Otherwise, (flag isn't set or deploying terminated successfully) the address accepts the funds. Defaults to `true`. * `wid` (`uint8`) - workchain id of the new account address. Defaults to `0`. * `flag` (`uint16`) - flag used to send the outbound internal message. Defaults to `0`. @@ -3920,7 +4097,7 @@ Let's consider how to protect against this problem: 1. Constructor is called by external message. We must Check if we didn't forget to set the public key in the contract and the inbound message is signed by that key. If hacker doesn't have your private -key then he can't sign message to call the constructor. +key, then he can't sign message to call the constructor. See [constructor of WalletProducer](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.sol). 2. Constructor is called by internal message. We should define static variable in the new contract that will contain @@ -3929,7 +4106,7 @@ And in the constructor we must check address of the message sender. See [function `deployWallet` how to deploy contract](https://github.com/tonlabs/samples/blob/master/solidity/17_ContractProducer.sol). See [constructor of SimpleWallet](https://github.com/tonlabs/samples/blob/master/solidity/17_SimpleWallet.sol). If some contract should deploy plenty of contracts (with some contract's -public key) then it's a good idea to declare static variable in the deployed +public key), then it's a good idea to declare static variable in the deployed contract. This variable can contain some sequence number. It will allow each new contact to have unique `stateInit`. See [SimpleWallet](https://github.com/tonlabs/samples/blob/master/solidity/17_SimpleWallet.sol). @@ -3943,7 +4120,7 @@ See [SimpleWallet](https://github.com/tonlabs/samples/blob/master/solidity/17_Si tvm.code() returns (TvmCell); ``` -Returns contract's code. +Returns contract's code. [Capabilities](#tvm-capabilities) required: `CapMycode`. See [SelfDeployer](https://github.com/tonlabs/samples/blob/master/solidity/21_self_deploy.sol). @@ -3953,7 +4130,7 @@ See [SelfDeployer](https://github.com/tonlabs/samples/blob/master/solidity/21_se tvm.codeSalt(TvmCell code) returns (optional(TvmCell) optSalt); ``` -If **code** contains salt then **optSalt** contains one. Otherwise, **optSalt** doesn't contain any value. +If **code** contains salt, then **optSalt** contains one. Otherwise, **optSalt** doesn't contain any value. ##### tvm.setCodeSalt() @@ -4244,14 +4421,14 @@ tvm.buildIntMsg({ value: uint128, call: {function, [callbackFunction,] arg0, arg1, arg2, ...}, bounce: bool, - currencies: ExtraCurrencyCollection + currencies: mapping(uint32 => varUint32), stateInit: TvmCell }) returns (TvmCell); ``` Generates an internal outbound message that contains a function call. The result `TvmCell` can be used to send a -message using [tvm.sendrawmsg()](#tvmsendrawmsg). If the `function` is `responsible` then +message using [tvm.sendrawmsg()](#tvmsendrawmsg). If the `function` is `responsible`, then `callbackFunction` parameter must be set. `dest`, `value` and `call` parameters are mandatory. Another parameters can be omitted. See @@ -4283,7 +4460,7 @@ tvm.sendrawmsg(msg, 2); ``` If the function is called by external message and `msg` has a wrong format (for example, the field -`init` of `Message X` is not valid) then the transaction will be replayed despite the usage of flag 2. +`init` of `Message X` is not valid), then the transaction will be replayed despite the usage of flag 2. It will happen because the transaction will fail at the action phase. #### **math** namespace @@ -4461,13 +4638,11 @@ Returns the logical time of the current transaction. ##### tx.storageFee -It's an experimental feature and is available only in certain blockchain networks. - ```TVMSolidity tx.storageFee returns (uint120); ``` -Returns the storage fee paid in the current transaction. +Returns the storage fee paid in the current transaction. [Capabilities](#tvm-capabilities) required: `CapStorageFeeToTvm`. ##### **block** namespace @@ -4506,9 +4681,9 @@ rnd.next([Type limit]) returns (Type); Generates a new pseudo-random number. 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`. +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: @@ -4547,17 +4722,19 @@ rnd.shuffle(); ``` Randomizes the random seed. + (1) Mixes the random seed and `someNumber`. The result is set as the random seed. + (2) Mixes the random seed and the logical time of the current transaction. The result is set as the random seed. Example: ```TVMSolidity -// (1) +(1) uint256 someNumber = ...; rnd.shuffle(someNumber); -// (2) +(2) rnd.shuffle(); ``` @@ -4592,7 +4769,7 @@ TvmCell cell = abi.encode(uint(1), uint(2), uint(3), uint(4)); ### **gosh** namespace All `gosh.*` functions are experimental features and are available only in certain blockchain -networks. +networks in which `CapDiff` [capability](#tvm-capabilities) is set. #### gosh.diff and gosh.zipDiff @@ -4603,7 +4780,8 @@ gosh.diff(string oldText, string newText) returns (string patch) (2) gosh.zipDiff(bytes oldText, bytes newText) returns (bytes patch) ``` -(1) Calculates [patch](https://en.wikipedia.org/wiki/Diff) between `oldText` and `newText`. Example: +(1) Calculates [patch](https://en.wikipedia.org/wiki/Diff) between `oldText` and `newText`. + (2) It's the same as `gosh.diff` but it calculates `patch` between compressed strings. @@ -4630,15 +4808,14 @@ gosh.applyZipBinPatch(bytes oldText, bytes patch) returns (bytes newText) gosh.applyZipBinPatchQ(bytes oldText, bytes patch) returns (optional(bytes) newText) ``` -(1) -Applies `patch` to the `oldText`. If it's impossible (bad patch), `gosh.applyPatch` throws an exception with type check -error code (-8) but`gosh.applyPatchQ` returns `null`. Example: -(2) -These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to binary arrays. -(3) -These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to compressed strings. -(4) -These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to compressed binary arrays. +(1) Applies `patch` to the `oldText`. If it's impossible (bad patch), `gosh.applyPatch` throws an exception with type check +error code (-8) but`gosh.applyPatchQ` returns `null`. + +(2) These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to binary arrays. + +(3) These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to compressed strings. + +(4) These are the same as `gosh.applyPatch`/`gosh.applyPatchQ` but these functions are applied to compressed binary arrays. ```TVMSolidity string oldText = ...; @@ -4687,18 +4864,18 @@ See example of how to use the `selfdestruct` function: #### sha256 ```TVMSolidity -// (1) +(1) sha256(TvmSlice slice) returns (uint256) -// (2) +(2) sha256(bytes b) returns (uint256) -// (3) +(3) sha256(string str) returns (uint256) ``` 1. Computes the SHA-256 hash. If the bit-length of `slice` is not divisible by eight, throws a cell underflow [exception](#tvm-exception-codes). References of `slice` are not used to compute the hash. Only data bits located in the root cell of `slice` are used. -2. Computes the SHA-256 hash only for the first 127 bytes. If `bytes.length > 127` then `b[128], +2. Computes the SHA-256 hash only for the first 127 bytes. If `bytes.length > 127`, then `b[128], b[129], b[130] ...` elements are ignored. 3. Same as for `bytes`: only the first 127 bytes are taken into account. @@ -4731,8 +4908,18 @@ If `wid` is omitted than used the contract's `wid`. ```TVMSolidity gasleft() returns (uint64) ``` +[Capabilities](#tvm-capabilities) required: `CapsTvmBugfixes2022`. + +Returns the remaining gas. -Returns the remaining gas. Supported only if `CapGasRemainingInsn` capability is set. +### TVM capabilities + +Rust implementation of TVM has capabilities. Capabilities are flags that can be set to turn on +some features or behavior of TVM. Full list of capabilities can be found in `enum GlobalCapabilities` in [ever-block](https://github.com/tonlabs/ever-block/blob/master/src/config_params.rs) repo. +Set capabilities store in 8th parameter of the global config of the blockchain. To get it you can use command: +```bash +tonos-cli --json getconfig 8 +``` ### TVM exception codes @@ -4778,7 +4965,6 @@ Solidity runtime error codes: * **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. - * **73** - The value can't be converted to enum type. * **74** - Await answer message has wrong source address. * **75** - Await answer message has wrong function id. * **76** - Public function was called before constructor. @@ -4792,10 +4978,10 @@ contract only for updating another contracts. Let consider we have `x` and `y` and we want to divide `x` by `y`. Compute the quotient `q` and the remainder `r` of the division of `x` by `y`: `x = y*q + r` where `|r| < |y|`. -In TVM there are 3 variants of rounding: +In TVM there are 3 options of rounding: * **floor** - quotient `q` is rounded to −∞. `q = ⌊x/y⌋`, `r` has the same sign as `y`. This -rounding variant is used for operator `/`. +rounding option is used for operator `/`. Example: ```TVMSolidity @@ -4835,24 +5021,24 @@ function of special function like `receive`, `fallback`, `onBounce`, `onTickTock Before calling contract's function `main_external` does: 1. Checks the message signature. Let's consider how the signature is checked: - - If signature exists and `pubkey` header isn't defined then `tvm.pubkey()` is used + - If signature exists and `pubkey` header isn't defined, then `tvm.pubkey()` is used for checking. - - If signature isn't exists and `pubkey` header isn't defined then signature isn't checked. + - If signature isn't exists and `pubkey` header isn't defined, then signature isn't checked. - If signature exists, `pubkey` header is defined and `pubkey` isn't exists in the - message then `tvm.pubkey()` is used for checking. + message, then `tvm.pubkey()` is used for checking. - If signature exists, `pubkey` header is defined and `pubkey` exists in the - message then `msg.pubkey()` is used for checking. + message, then `msg.pubkey()` is used for checking. - If signature isn't exists, `pubkey` header is defined and `pubkey` exists in the - message then an [exception with code 58](#solidity-runtime-errors) is thrown. + message, then an [exception with code 58](#solidity-runtime-errors) is thrown. 2. Replay protection: - - [*time* header](#pragma-abiheader) exists (`pragma AbiHeader notime` is not used) then the contract checks whether - `oldTime` < `time` < `now * 1000 + 30 minutes`. If it's true then `oldTime` is updated by new `time`. + - [*time* header](#pragma-abiheader) exists (`pragma AbiHeader notime` is not used), then the contract checks whether + `oldTime` < `time` < `now * 1000 + 30 minutes`. If it's true, then `oldTime` is updated by new `time`. Otherwise, an exception is thrown. - - there is `afterSignatureCheck` (despite usage of `time`) then make your own replay protection. + - there is `afterSignatureCheck` (despite usage of `time`), then make your own replay protection. 3. Message expiration: - - `expire` exists and there is no `afterSignatureCheck` then the contract checks whether + - `expire` exists and there is no `afterSignatureCheck`, then the contract checks whether `expire` > `now`. - - there is `afterSignatureCheck` (despite usage of `expire`) then make your own check. + - there is `afterSignatureCheck` (despite usage of `expire`), then make your own check. See also: [pragma AbiHeader](#pragma-abiheader), [afterSignatureCheck](#aftersignaturecheck). diff --git a/Cargo.lock b/Cargo.lock index f905a1ad..6af72adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] @@ -73,41 +73,31 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -123,26 +113,14 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", ] -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "assert_cmd" version = "2.0.12" @@ -150,7 +128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", - "bstr 1.6.0", + "bstr", "doc-comment", "predicates", "predicates-core", @@ -164,7 +142,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -177,9 +155,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -206,27 +184,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" - -[[package]] -name = "blake2b_simd" -version = "0.5.11" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "block-buffer" @@ -248,18 +209,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "memchr", -] - -[[package]] -name = "bstr" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "regex-automata", @@ -268,9 +220,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" @@ -280,11 +232,11 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ - "jobserver", + "libc", ] [[package]] @@ -295,17 +247,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -319,59 +270,43 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.3.19" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "cmake" @@ -389,10 +324,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "const-oid" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "core-foundation-sys" @@ -424,15 +359,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -454,9 +380,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "3.2.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest 0.9.0", @@ -466,20 +392,49 @@ dependencies = [ ] [[package]] -name = "difflib" -version = "0.4.0" +name = "curve25519-dalek" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] [[package]] -name = "diffy" -version = "0.2.2" +name = "curve25519-dalek-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27ec7cef89a63c063e06570bb861b7d35e406d6885551b346d77c459b34d3db" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "ansi_term", + "proc-macro2", + "quote", + "syn 2.0.37", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -499,17 +454,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -533,10 +477,11 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ + "pkcs8", "signature 2.1.0", ] @@ -546,7 +491,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519 1.5.3", "rand 0.7.3", "serde", @@ -555,31 +500,24 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "errno" -version = "0.3.1" +name = "ed25519-dalek" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", + "curve25519-dalek 4.1.1", + "ed25519 2.2.2", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "zeroize", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "either" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "failure" @@ -603,6 +541,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + [[package]] name = "float-cmp" version = "0.9.0" @@ -646,9 +590,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "heck" @@ -665,12 +609,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "hex" version = "0.3.2" @@ -706,22 +644,11 @@ dependencies = [ "cc", ] -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.2", - "rustix", - "windows-sys", -] - [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -732,15 +659,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.64" @@ -758,15 +676,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "lockfree" @@ -778,15 +690,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "miniz_oxide" @@ -819,9 +731,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -830,9 +742,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] @@ -892,9 +804,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -918,10 +830,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30fceb411f9a12ff9222c5f824026be368ff15dc2f13468d850c7d3f502205d6" [[package]] -name = "pkg-config" -version = "0.3.27" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" [[package]] name = "ppv-lite86" @@ -931,9 +853,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ "anstyle", "difflib", @@ -962,18 +884,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1049,28 +971,11 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_users" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -dependencies = [ - "getrandom 0.1.16", - "redox_syscall", - "rust-argon2", -] - [[package]] name = "regex" -version = "1.9.1" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -1080,9 +985,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", @@ -1091,21 +996,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64 0.13.1", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustc-demangle" @@ -1114,16 +1007,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustix" -version = "0.38.4" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", + "semver", ] [[package]] @@ -1132,31 +1021,37 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "semver" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" + [[package]] name = "serde" -version = "1.0.175" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.175" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1178,9 +1073,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1199,39 +1094,19 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -[[package]] -name = "similar" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" -dependencies = [ - "bstr 0.2.17", -] - -[[package]] -name = "simplelog" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9c948a5a26cd38340ddbeaa557a8c8a5ce4442408eb60453bee2bb3c84a3fb" -dependencies = [ - "chrono", - "log", - "term", -] - [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "sold" -version = "0.71.0" +version = "0.72.0" dependencies = [ "assert_cmd", "atty", - "clap 4.3.19", + "clap", "cmake", "dunce", "failure", @@ -1242,24 +1117,28 @@ dependencies = [ "strip-ansi-escapes", "ton_abi", "ton_block", + "ton_labs_assembler", "ton_types", - "tvm_linker", ] [[package]] -name = "strip-ansi-escapes" -version = "0.1.1" +name = "spki" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ - "vte", + "base64ct", + "der", ] [[package]] -name = "strsim" -version = "0.8.0" +name = "strip-ansi-escapes" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] [[package]] name = "strsim" @@ -1286,9 +1165,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -1307,53 +1186,20 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "term" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" -dependencies = [ - "byteorder", - "dirs", - "winapi", -] - [[package]] name = "termtree" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "ton_abi" -version = "2.3.129" -source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.3.129#6445189dbf5aa9b7ecac93c06421eacc09461a0b" +version = "2.3.147" +source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.3.147#a3194277b02417e4f01221f5cf6b943fc4bd6653" dependencies = [ "base64 0.10.1", "byteorder", "chrono", - "ed25519 1.5.3", - "ed25519-dalek", "failure", "hex 0.3.2", "num-bigint", @@ -1361,34 +1207,35 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "ton_block", "ton_types", ] [[package]] name = "ton_block" -version = "1.9.88" -source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.88#d38a615860e38e4a6ff3c56c6bf1c2a459e7c80a" +version = "1.9.104" +source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.104#2c6cf392260d047cff5962572a628c207ea28309" dependencies = [ "base64 0.13.1", "crc", "ed25519 1.5.3", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "failure", "hex 0.4.3", "log", "num", "num-traits", - "sha2 0.10.7", + "sha2 0.10.8", "ton_types", ] [[package]] name = "ton_labs_assembler" -version = "1.2.130" -source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.2.130#625bd9075e27b4d3bee21481ccc504dc8d891a1e" +version = "1.4.15" +source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.4.15#0d0db5b69a5eec932dd53b83fb6c02c89897c1fe" dependencies = [ + "clap", "failure", "hex 0.4.3", "log", @@ -1397,19 +1244,20 @@ dependencies = [ "serde", "serde_json", "ton_types", + "ton_vm", ] [[package]] name = "ton_types" -version = "2.0.18" -source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.18#cc9e204f9122967b0f1d5af3208f8ccc36e706ef" +version = "2.0.28" +source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.28#e6ce975e741634638e4d42f4e8b273ea4d046370" dependencies = [ "aes-ctr", "base64 0.13.1", "crc", - "curve25519-dalek", - "ed25519 1.5.3", - "ed25519-dalek", + "curve25519-dalek 4.1.1", + "ed25519 2.2.2", + "ed25519-dalek 2.0.0", "failure", "hex 0.4.3", "lazy_static", @@ -1418,22 +1266,21 @@ dependencies = [ "num", "num-derive", "num-traits", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "smallvec", "x25519-dalek", ] [[package]] name = "ton_vm" -version = "1.8.189" -source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.8.189#5bc2dece1944b22a9cfafee3d7ad7f577f5fb10f" +version = "1.8.211" +source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.8.211#ca3e6e70793a86ec20d27d1463030ca864bf1f59" dependencies = [ - "diffy", "ed25519 1.5.3", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "failure", "hex 0.4.3", "lazy_static", @@ -1441,58 +1288,21 @@ dependencies = [ "num", "num-traits", "rand 0.7.3", - "similar", "ton_block", "ton_types", - "zstd", -] - -[[package]] -name = "tvm_linker" -version = "0.20.4" -source = "git+https://github.com/tonlabs/TVM-linker.git?tag=0.20.4#307243825a49f5f589657cfa235903e49207efe7" -dependencies = [ - "base64 0.13.1", - "clap 2.34.0", - "crc", - "ed25519 2.2.1", - "ed25519-dalek", - "failure", - "hex 0.4.3", - "lazy_static", - "log", - "num", - "num-traits", - "rand 0.8.5", - "regex", - "serde", - "serde_json", - "sha2 0.10.7", - "simplelog", - "ton_abi", - "ton_block", - "ton_labs_assembler", - "ton_types", - "ton_vm", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-width" -version = "0.1.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-xid" @@ -1506,12 +1316,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -1520,11 +1324,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vte" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ - "arrayvec", "utf8parse", "vte_generate_state_changes", ] @@ -1554,12 +1357,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1587,7 +1384,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", "wasm-bindgen-shared", ] @@ -1609,7 +1406,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1662,9 +1459,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1677,62 +1474,63 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "x25519-dalek" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", + "curve25519-dalek 4.1.1", + "rand_core 0.6.4", + "serde", "zeroize", ] [[package]] name = "zeroize" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] @@ -1745,35 +1543,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" -dependencies = [ - "cc", - "libc", - "pkg-config", + "syn 2.0.37", ] diff --git a/Changelog.md b/Changelog.md index 0ffccf51..7b61ecf4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,28 @@ +### 0.72.0 (2023-??-??) + +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. + +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))`. + * Deleted `ExtraCurrencyCollection` type. Use `mapping(uint32 => varUint32)` instead of it. + +Bugfixes: + * Fixed bug when recursive calling of library function via object corrupted the stack. + * Fixed minor bug that caused compilation failure. + * Fixed bug when you use string literals for `bytesN constant`. + +Compiler features: + * Supported [unchecked blocks](API.md#unchecked-block). + * Supported defining events in libraries. + * Supported [require(bool condition, string text)](API.md#require). + * Supported functions for working with exotic cells: + * [\.exoticToSlice()](API.md#tvmcellexotictoslice) + * [\.loadExoticCell() and \.loadExoticCellQ()](API.md#tvmcellloadexoticcell-and-tvmcellloadexoticcellq) + * [\.toExoticCell()](API.md#tvmbuildertoexoticcell) + * Supported command line option `--overwrite` that is used together with `--ast-compact-json -o` options. + * Warning about storing too big structs via `.store()`. + * Change function signature `.skip(uint10 bits, uint2 refs)` -> `.skip(uint10 bits, uint3 refs)` to allow to skip 4 references. + ### 0.71.0 (2023-07-20) Bugfixes: diff --git a/README.md b/README.md index 57afc874..d020109a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -# the TVM Solidity compiler +# The TVM Solidity compiler [![GitHub](https://img.shields.io/github/license/tonlabs/TON-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/) @@ -22,14 +22,14 @@ Port of the Solidity smart-contract [compiler](https://github.com/ethereum/solid ## Build and Install -Original Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source). - ### Sold driver -Documentation is available at [README.md](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/sold/README.md) (RECOMMENDED). +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). ### Building compiler +Original Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source). + #### Ubuntu Linux ```shell @@ -42,12 +42,6 @@ cmake ../compiler/ -DCMAKE_BUILD_TYPE=Release cmake --build . -- -j8 ``` -Make other toolchain utilities aware of the language runtime library location via an environment variable: specify path to `stdlib_sol.tvm`. - -```shell -sh ./compiler/scripts/install_lib_variable.sh -``` - #### Windows 10 Install Visual Studio Build Tools 2019, Git bash, cmake. @@ -63,14 +57,12 @@ cmake -DBoost_DIR="..\compiler\deps\boost\lib\cmake\Boost-1.77.0" -DCMAKE_MSVC_R cmake --build . --config Release -- /m ``` -To facilitate work with other toolchain utilities add path to `stdlib_sol.tvm` into environment variable `TVM_LINKER_LIB_PATH`. - ## Links + * [Ever assembler and disassembler](https://github.com/tonlabs/ever-assembler) * [Code samples](https://github.com/tonlabs/samples/tree/master/solidity) in TVM Solidity - * [TVM linker repository](https://github.com/tonlabs/TVM-linker) * [tonos-cli](https://github.com/tonlabs/tonos-cli) command line interface for TVM compatible blockchains - * Example of usage `tonos-cli` for working (deploying, calling and 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) + * 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) ## License diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 722c04ec..75a8c5fe 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.71.0") +set(PROJECT_VERSION "0.72.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.h b/compiler/liblangutil/Token.h index ba682905..2553a74e 100644 --- a/compiler/liblangutil/Token.h +++ b/compiler/liblangutil/Token.h @@ -247,7 +247,6 @@ namespace solidity::langutil K(TvmCell, "TvmCell", 0) \ K(TvmSlice, "TvmSlice", 0) \ K(TvmBuilder, "TvmBuilder", 0) \ - K(ExtraCurrencyCollection, "ExtraCurrencyCollection", 0) \ K(Variant, "variant", 0) \ K(Fixed, "fixed", 0) \ K(UFixed, "ufixed", 0) \ diff --git a/compiler/libsolidity/analysis/ContractLevelChecker.cpp b/compiler/libsolidity/analysis/ContractLevelChecker.cpp index bfb400e3..0883e7ee 100644 --- a/compiler/libsolidity/analysis/ContractLevelChecker.cpp +++ b/compiler/libsolidity/analysis/ContractLevelChecker.cpp @@ -135,7 +135,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co { if (onBounce) m_errorReporter.declarationError( - 228_error, + 9645_error, function->location(), SecondarySourceLocation().append("Another declaration is here:", onBounce->location()), "Only one onBounce function is allowed." @@ -157,7 +157,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co { if (onTickTock) { m_errorReporter.declarationError( - 228_error, + 2306_error, function->location(), SecondarySourceLocation().append("Another declaration is here:", receive->location()), "Only one onTickTock function is allowed." diff --git a/compiler/libsolidity/analysis/SyntaxChecker.cpp b/compiler/libsolidity/analysis/SyntaxChecker.cpp index 42a776d2..99418d33 100644 --- a/compiler/libsolidity/analysis/SyntaxChecker.cpp +++ b/compiler/libsolidity/analysis/SyntaxChecker.cpp @@ -88,7 +88,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) solAssert(m_sourceUnit, ""); if (_pragma.literals().size() >= 2) { - m_errorReporter.warning(228_error, _pragma.location(), "Has no effect. Delete this."); + m_errorReporter.warning(9089_error, _pragma.location(), "Has no effect. Delete this."); } vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); @@ -154,13 +154,13 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) to_string(recommendedVersion.patch()) + string(";\"") + " to set a version of the compiler."; - m_errorReporter.warning(228_error, _pragma.location(), errorString); + m_errorReporter.warning(6413_error, _pragma.location(), errorString); } else if (_pragma.literals()[0] == "ever" || _pragma.literals()[0] == "ton") // ever-solidity { if (m_versionPragma.has_value()) { m_errorReporter.fatalTypeError( - 228_error, + 8884_error, _pragma.location(), SecondarySourceLocation().append("Previous definition:", *m_versionPragma.value()), "Compiler version is defined more than once."); @@ -186,7 +186,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) const std::string base = "Correct format: pragma AbiHeader [pubkey|expire|notime]"; if (_pragma.literals().size() != 2) { auto err = "Empty pragma. " + base; - m_errorReporter.syntaxError(228_error, _pragma.location(), err); + m_errorReporter.syntaxError(1112_error, _pragma.location(), err); } else { auto literal = _pragma.literals()[1]; if (literal != "pubkey" && literal != "expire" && literal != "notime") { @@ -195,14 +195,14 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) err += "\nNote: timestamp in header of external message " "is on by default, so delete this pragma."; } - m_errorReporter.syntaxError(228_error, _pragma.location(), err); + m_errorReporter.syntaxError(2632_error, _pragma.location(), err); } } } else if (_pragma.literals()[0] == "upgrade") { if (_pragma.literals().size() != 2 || (_pragma.literals()[1] != "func" && _pragma.literals()[1] != "oldsol")) { - m_errorReporter.syntaxError(228_error, _pragma.location(), R"(Unknown pragma. Use: "pragma upgrade func;" or "pragma upgrade oldsol;")"); + m_errorReporter.syntaxError(3323_error, _pragma.location(), R"(Unknown pragma. Use: "pragma upgrade func;" or "pragma upgrade oldsol;")"); } } else if (_pragma.literals()[0] == "ignoreIntOverflow") @@ -212,10 +212,10 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) else if (_pragma.literals()[0] == "msgValue") { if (m_msgValuePragmaFound) { - m_errorReporter.syntaxError(228_error, _pragma.location(), "msgValue pragma shouldn't be specified more than once."); + m_errorReporter.syntaxError(2995_error, _pragma.location(), "msgValue pragma shouldn't be specified more than once."); } if (_pragma.parameter().empty()) { - m_errorReporter.syntaxError(228_error, _pragma.location(), "Correct format: pragma msgValue "); + m_errorReporter.syntaxError(6562_error, _pragma.location(), "Correct format: pragma msgValue "); } m_msgValuePragmaFound = true; } @@ -223,7 +223,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { if (m_FirstCopyleft) { m_errorReporter.declarationError( - 228_error, + 1595_error, _pragma.location(), SecondarySourceLocation().append("The previous declaration is here:", *m_FirstCopyleft), "Pragma already defined." diff --git a/compiler/libsolidity/analysis/TypeChecker.cpp b/compiler/libsolidity/analysis/TypeChecker.cpp index 1fd8472c..b2a1383d 100644 --- a/compiler/libsolidity/analysis/TypeChecker.cpp +++ b/compiler/libsolidity/analysis/TypeChecker.cpp @@ -132,7 +132,7 @@ TypePointers TypeChecker::getReturnTypesForTVMConfig(FunctionCall const& _functi vector> arguments = _functionCall.arguments(); if (arguments.size() != 1) m_errorReporter.typeError( - 228_error, + 7750_error, _functionCall.location(), "This function takes one argument, but " + toString(arguments.size()) + @@ -144,7 +144,7 @@ TypePointers TypeChecker::getReturnTypesForTVMConfig(FunctionCall const& _functi if (!paramNumberLiteral) m_errorReporter.typeError( - 228_error, + 1961_error, _functionCall.location(), "This function takes only param number literal." ); @@ -155,7 +155,7 @@ TypePointers TypeChecker::getReturnTypesForTVMConfig(FunctionCall const& _functi if (!availableParams.count(paramNumber)) m_errorReporter.typeError( - 228_error, + 2100_error, _functionCall.location(), "Wrong config param number. Available numbers are: 1, 15, 17, 34." ); @@ -279,7 +279,7 @@ void TypeChecker::typeCheckTVMBuildStateInit( size_t argCnt = args.size(); if (!hasNames && argCnt != 3 && argCnt != 2) m_errorReporter.typeError( - 228_error, + 6303_error, _functionCall.location(), string("If parameters are set without names, only 2 or 3 arguments can be specified.") ); @@ -293,21 +293,21 @@ void TypeChecker::typeCheckTVMBuildStateInit( if (!hasCode) { m_errorReporter.typeError( - 228_error, + 6128_error, _functionCall.location(), string("Parameter \"code\" must be set.") ); } if (hasData && (hasVarInit || hasPubkey)) { m_errorReporter.typeError( - 228_error, + 6578_error, _functionCall.location(), string(R"(Parameter "data" can't be specified with "pubkey" or "varInit".)") ); } if (hasVarInit != hasContr) { m_errorReporter.typeError( - 228_error, + 1476_error, _functionCall.location(), string(R"(Parameter "varInit" requires parameter "contr" and there is no need in "contr" without "varInit".)") ); @@ -319,7 +319,7 @@ void TypeChecker::typeCheckTVMBuildStateInit( ContractType const* ct = getContractType(contrArg.get()); if (ct == nullptr) { m_errorReporter.typeError( - 228_error, + 8286_error, contrArg->location(), "Expected contract type." ); @@ -353,7 +353,7 @@ void TypeChecker::typeCheckTVMBuildDataInit( if (hasVarInit != hasContr) { m_errorReporter.typeError( - 228_error, + 2957_error, _functionCall.location(), string(R"(Parameter "varInit" requires parameter "contr" and there is no need in "contr" without "varInit".)") ); @@ -365,7 +365,7 @@ void TypeChecker::typeCheckTVMBuildDataInit( ContractType const* ct = getContractType(contrArg.get()); if (ct == nullptr) { m_errorReporter.typeError( - 228_error, + 9417_error, contrArg->location(), "Expected contract type." ); @@ -389,7 +389,7 @@ void TypeChecker::typeCheckCallBack(FunctionType const* remoteFunction, Expressi auto calleeDefinition = dynamic_cast(&remoteFunction->declaration()); if (calleeDefinition == nullptr) { m_errorReporter.typeError( - 228_error, + 4220_error, option.location(), R"("callback" option can be used only for function type.)" ); @@ -398,7 +398,7 @@ void TypeChecker::typeCheckCallBack(FunctionType const* remoteFunction, Expressi if (remoteFunction->returnParameterTypes().empty() || !calleeDefinition->isResponsible()) { m_errorReporter.typeError( - 228_error, + 5326_error, option.location(), SecondarySourceLocation().append("Declaration of callee function", calleeDefinition->location()), R"("callback" option can be used only for responsible functions.)" @@ -409,7 +409,7 @@ void TypeChecker::typeCheckCallBack(FunctionType const* remoteFunction, Expressi FunctionDefinition const *callbackFunc = getFunctionDefinition(&option); if (callbackFunc == nullptr) { m_errorReporter.typeError( - 228_error, + 4191_error, option.location(), "Expected function type but got " + option.annotation().type->toString() + "." ); @@ -418,7 +418,7 @@ void TypeChecker::typeCheckCallBack(FunctionType const* remoteFunction, Expressi if (!callbackFunc->returnParameters().empty()) { m_errorReporter.typeError( - 228_error, + 5801_error, option.location(), SecondarySourceLocation() .append("Declaration of the callback function:", callbackFunc->location()), @@ -449,7 +449,6 @@ TypePointers TypeChecker::checkSliceDecode(std::vectorlocation(), "Unsupported type for decoding."); + m_errorReporter.typeError(9365_error, typeArgument->location(), "Unsupported type for decoding."); } } else { - m_errorReporter.typeError(228_error, typeArgument->location(), "Argument has to be a type name."); + m_errorReporter.typeError(6318_error, typeArgument->location(), "Argument has to be a type name."); components.push_back(TypeProvider::emptyTuple()); } } @@ -487,15 +486,14 @@ TypePointers TypeChecker::checkSliceDecodeQ(std::vectorlocation(), "Unsupported type for decoding."); + m_errorReporter.typeError(7828_error, typeArgument->location(), "Unsupported type for decoding."); } } else { - m_errorReporter.typeError(228_error, typeArgument->location(), "Argument has to be a type name."); + m_errorReporter.typeError(7213_error, typeArgument->location(), "Argument has to be a type name."); components.push_back(TypeProvider::emptyTuple()); } } @@ -630,10 +628,10 @@ bool TypeChecker::isBadAbiType( auto printError = [&](const std::string& message) { if (doPrintErr) { if (origVarLoc == curVarLoc) { - m_errorReporter.typeError(228_error, origVarLoc, message); + m_errorReporter.typeError(2468_error, origVarLoc, message); } else { m_errorReporter.typeError( - 228_error, + 2321_error, origVarLoc, SecondarySourceLocation().append("Another declaration is here:", curVarLoc), message @@ -693,7 +691,7 @@ bool TypeChecker::isBadAbiType( if (usedStructs.count(&structDefinition)) { if (doPrintErr) { m_errorReporter.typeError( - 228_error, + 1107_error, origVarLoc, SecondarySourceLocation().append("Recursive struct:", structDefinition.location()), "ABI doesn't support recursive types." @@ -742,9 +740,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isResponsible()) { if (_function.returnParameters().empty()) - m_errorReporter.typeError(228_error, _function.location(), "Responsible function must return at least one value."); + m_errorReporter.typeError(4438_error, _function.location(), "Responsible function must return at least one value."); if (!_function.isPublic()) - m_errorReporter.typeError(228_error, _function.location(), "Responsible function must have public or external visibility."); + m_errorReporter.typeError(4403_error, _function.location(), "Responsible function must have public or external visibility."); } if (_function.markedVirtual()) @@ -768,13 +766,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isExternalMsg() || _function.isInternalMsg()) { if (_function.isExternalMsg() && _function.isInternalMsg()) { - m_errorReporter.typeError(228_error, _function.location(), R"("internalMsg" and "externalMsg" cannot be used together.)"); + m_errorReporter.typeError(6672_error, _function.location(), R"("internalMsg" and "externalMsg" cannot be used together.)"); } if (!_function.functionIsExternallyVisible()) { - m_errorReporter.typeError(228_error, _function.location(), R"(Private/internal function can't be marked as internalMsg/externalMsg.)"); + m_errorReporter.typeError(7446_error, _function.location(), R"(Private/internal function can't be marked as internalMsg/externalMsg.)"); } if (_function.isReceive() || _function.isFallback() || _function.isOnBounce() || _function.isOnTickTock()) { - m_errorReporter.typeError(228_error, _function.location(), R"(receiver, fallback, onBounce and onTickTock functions can't be marked as internalMsg/externalMsg.)"); + m_errorReporter.typeError(1399_error, _function.location(), R"(receiver, fallback, onBounce and onTickTock functions can't be marked as internalMsg/externalMsg.)"); } } @@ -975,7 +973,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) break; default: m_errorReporter.typeError( - 228_error, + 5241_error, _variable.location(), "Type " + mapType->keyType()->toString() + " can't be used as mapping key. " "Allowed types: address, bytes, string, bool, contract, enum, fixed bytes, fixed-point number, integer and struct."); @@ -989,7 +987,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (_variable.isStatic() && _variable.value() != nullptr) { m_errorReporter.syntaxError( - 228_error, + 3505_error, _variable.value()->location(), "Static variables can be initialized only during contract deployment."); } @@ -998,7 +996,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) auto ft = dynamic_cast(_variable.type()); if (ft->kind() == FunctionType::Kind::External) { m_errorReporter.fatalTypeError( - 228_error, + 7945_error, _variable.location(), "External functions are not supported yet."); } @@ -1162,7 +1160,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) auto printError = [&](SourceLocation const& loc){ m_errorReporter.typeError( - 228_error, + 8220_error, loc, "Expected `catch (variant value, uint16 number) { ... }`."); }; @@ -1215,7 +1213,7 @@ bool TypeChecker::visit(ForEachStatement const& _forStatement) auto vars = dynamic_cast(_forStatement.rangeDeclaration()); if (vars == nullptr) { m_errorReporter.typeError( - 228_error, + 9487_error, _forStatement.rangeDeclaration()->location(), "Expected variable declaration statement." ); @@ -1233,14 +1231,14 @@ bool TypeChecker::visit(ForEachStatement const& _forStatement) vd->type()->toString() + " is not implicitly convertible to expected type " + type->toString() + "."; - m_errorReporter.typeError(228_error, vd->location(), errorMsg); + m_errorReporter.typeError(5397_error, vd->location(), errorMsg); } }; if (mappingType) { if (vars->declarations().size() != 2) { m_errorReporter.typeError( - 228_error, + 6298_error, vars->location(), "Expected two variables of type " + mappingType->keyType()->toString() + @@ -1255,7 +1253,7 @@ bool TypeChecker::visit(ForEachStatement const& _forStatement) } else if (arrayType) { if (vars->declarations().size() != 1) { m_errorReporter.typeError( - 228_error, + 9207_error, vars->location(), "Too many declared variables. " "Expected one variable of type " + arrayType->baseType()->toString() + "." @@ -1265,7 +1263,7 @@ bool TypeChecker::visit(ForEachStatement const& _forStatement) } } else { m_errorReporter.typeError( - 228_error, + 4670_error, _forStatement.rangeExpression()->location(), "Invalid range expression of type " + _forStatement.rangeExpression()->annotation().type->toString() + ". " + @@ -1283,7 +1281,7 @@ void TypeChecker::endVisit(Return const& _return) if (!_return.names().empty() && (!m_currentFunction->isPublic() || !m_currentFunction->isResponsible())) { SourceLocation loc = getSmallestCovering(_return.options()); m_errorReporter.typeError( - 228_error, + 8755_error, loc, R"(Options in return statement can be used only in responsible public/external functions.)" ); @@ -1298,7 +1296,7 @@ void TypeChecker::endVisit(Return const& _return) }; if (nameToType.count(name) == 0) { m_errorReporter.typeError( - 228_error, + 3679_error, _return.options().at(i)->location(), "Unknown call option \"" + name + @@ -1382,7 +1380,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit) if (name == "dest") { expectType(*opt, *TypeProvider::address(), false); } else { - m_errorReporter.typeError(228_error, _emit.location(), "Unknown option " + name + ". Only option \"dest\" is supported."); + m_errorReporter.typeError(2900_error, _emit.location(), "Unknown option " + name + ". Only option \"dest\" is supported."); } } } @@ -2049,9 +2047,9 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function) if (_function.visibility() != Visibility::External) m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\"."); if (!_function.returnParameters().empty()) - m_errorReporter.typeError(228_error, _function.returnParameterList()->location(), "Fallback function cannot return parameters."); + m_errorReporter.typeError(1624_error, _function.returnParameterList()->location(), "Fallback function cannot return parameters."); if (!_function.parameters().empty()) - m_errorReporter.typeError(228_error, _function.parameterList().location(), "Fallback function cannot take parameters."); + m_errorReporter.typeError(1127_error, _function.parameterList().location(), "Fallback function cannot take parameters."); } void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) @@ -2099,27 +2097,27 @@ void TypeChecker::typeCheckOnBounce(const FunctionDefinition &_function) { solAssert(_function.isOnBounce(), ""); if (_function.libraryFunction()) - m_errorReporter.typeError(228_error, _function.location(), "Libraries cannot have onBounce functions."); + m_errorReporter.typeError(1510_error, _function.location(), "Libraries cannot have onBounce functions."); if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(228_error, _function.location(), "onBounce function must be defined as \"external\"."); + m_errorReporter.typeError(4869_error, _function.location(), "onBounce function must be defined as \"external\"."); if (!_function.returnParameters().empty()) - m_errorReporter.typeError(228_error, _function.returnParameterList()->location(), "onBounce function cannot return values."); + m_errorReporter.typeError(3628_error, _function.returnParameterList()->location(), "onBounce function cannot return values."); if (_function.parameters().size() != 1 || _function.parameters().at(0)->type()->category() != Type::Category::TvmSlice) - m_errorReporter.typeError(228_error, _function.parameterList().location(), "onBounce function should take one parameter (TvmSlice body)."); + m_errorReporter.typeError(5297_error, _function.parameterList().location(), "onBounce function should take one parameter (TvmSlice body)."); } void TypeChecker::typeCheckOnTickTock(const FunctionDefinition &_function) { if (_function.libraryFunction()) - m_errorReporter.typeError(228_error, _function.location(), "Libraries cannot have onTickTock functions."); + m_errorReporter.typeError(3729_error, _function.location(), "Libraries cannot have onTickTock functions."); if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(228_error, _function.location(), "onTickTock function must be defined as \"external\"."); + m_errorReporter.typeError(5243_error, _function.location(), "onTickTock function must be defined as \"external\"."); if (!_function.returnParameters().empty()) - m_errorReporter.typeError(228_error, _function.returnParameterList()->location(), "onTickTock function cannot return values."); + m_errorReporter.typeError(7745_error, _function.returnParameterList()->location(), "onTickTock function cannot return values."); if (_function.parameters().size() != 1 || _function.parameters().at(0)->type()->category() != Type::Category::Bool) - m_errorReporter.typeError(228_error, _function.parameterList().location(), "onTickTock function should take one parameter (bool isTock)."); + m_errorReporter.typeError(3050_error, _function.parameterList().location(), "onTickTock function should take one parameter (bool isTock)."); } void TypeChecker::checkNeedCallback(FunctionType const* callee, ASTNode const& node) { @@ -2127,7 +2125,7 @@ void TypeChecker::checkNeedCallback(FunctionType const* callee, ASTNode const& n auto funcDef = dynamic_cast(&callee->declaration()); if (funcDef && funcDef->isResponsible()) { m_errorReporter.typeError( - 228_error, + 9205_error, node.location(), SecondarySourceLocation().append("Declaration is here:", funcDef->location()), R"("callback" option must be set because callee function is marked as responsible.)" @@ -2140,7 +2138,7 @@ void TypeChecker::typeCheckTvmEncodeArg(Type const* type, Expression const& node switch (type->category()) { case Type::Category::RationalNumber: m_errorReporter.typeError( - 228_error, + 6332_error, node.location(), "Cannot perform encoding for a literal." " Please convert it to an explicit type first." @@ -2151,7 +2149,6 @@ void TypeChecker::typeCheckTvmEncodeArg(Type const* type, Expression const& node case Type::Category::Bool: case Type::Category::Contract: case Type::Category::Enum: - case Type::Category::ExtraCurrencyCollection: case Type::Category::FixedBytes: case Type::Category::Integer: case Type::Category::Mapping: @@ -2170,7 +2167,7 @@ void TypeChecker::typeCheckTvmEncodeArg(Type const* type, Expression const& node } default: m_errorReporter.typeError( - 228_error, + 9100_error, node.location(), "Encoding for a " + node.annotation().type->toString(true) + " isn't supported." ); @@ -2712,7 +2709,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( // It's checked in TypeChecker::visit(FunctionCall const& _functionCall) } else { m_errorReporter.typeError( - 228_error, + 8213_error, _functionCall.location(), "Named argument \"" + *argumentNames[i] + @@ -2760,7 +2757,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( solAssert(!!paramArgMap[i], "unmapped parameter"); vector const ¶meterNames = _functionType->parameterNames(); if (i >= parameterNames.size()) { - m_errorReporter.fatalTypeError(228_error, paramArgMap[i]->location(), "Too many arguments."); + m_errorReporter.fatalTypeError(3774_error, paramArgMap[i]->location(), "Too many arguments."); } if (!parameterNames.empty()) { string const &argName = parameterNames.at(i); @@ -2892,7 +2889,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( string const &name = *(names[i]); if (std::find(arr.begin(), arr.end(), name) == arr.end()) { m_errorReporter.typeError( - 228_error, + 4187_error, functionCallOpt->location(), "Unknown option \"" + name + "\". " + "Valid options are " + fold() + "." @@ -2915,7 +2912,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( auto callback = getId("callback"); if (callback != -1 && _functionCall.isAwait()) m_errorReporter.typeError( - 228_error, + 8276_error, _functionCall.location(), R"("callback" option can't be set for await call.)" ); @@ -2930,7 +2927,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( if (isExternalInboundMessage && (callbackId == -1 || onErrorId == -1)) { m_errorReporter.typeError( - 228_error, + 3365_error, functionCallOpt->location(), R"("callbackId" and "onErrorId" options must be set.)" ); @@ -2947,7 +2944,7 @@ TypeChecker::checkPubFunctionAndGetDefinition(Expression const& arg, bool printE if (funcDef) { if (!funcDef->isPublic()) { m_errorReporter.fatalTypeError( - 228_error, + 6273_error, arg.location(), SecondarySourceLocation().append("Declaration is here:", funcDef->location()), "Public/external function or contract type required, but \"" + @@ -2957,7 +2954,7 @@ TypeChecker::checkPubFunctionAndGetDefinition(Expression const& arg, bool printE } } else if (printError) { m_errorReporter.fatalTypeError( - 228_error, + 4076_error, arg.location(), "Expected function type, but got " + toString(arg.annotation().type->toString()) + @@ -2977,7 +2974,7 @@ TypeChecker::checkPubFunctionOrContractTypeAndGetDefinition(Expression const& ar const auto &[isContract, constructorDef] = getConstructorDefinition(&arg); if (!isContract) { m_errorReporter.fatalTypeError( - 228_error, + 4974_error, arg.location(), "Function or contract type required, but " + type(arg)->toString(true) + @@ -2998,7 +2995,7 @@ void TypeChecker::checkInitList(InitializerList const *list, ContractType const if (name == v->name()) { if (!v->isStatic()) { m_errorReporter.typeError( - 228_error, + 6626_error, list->options().at(i)->location(), SecondarySourceLocation() .append("Declaration is here:", v->location()), @@ -3006,7 +3003,7 @@ void TypeChecker::checkInitList(InitializerList const *list, ContractType const ); } else if (!exprType->isImplicitlyConvertibleTo(*v->type())) { m_errorReporter.typeError( - 228_error, + 2937_error, list->options().at(i)->location(), "Expected " + v->type()->toString() + " type" + " but got " + exprType->toString() + " type." @@ -3017,7 +3014,7 @@ void TypeChecker::checkInitList(InitializerList const *list, ContractType const } if (j == vars.size()) { m_errorReporter.typeError( - 228_error, + 6711_error, list->options().at(i)->location(), SecondarySourceLocation() .append("Contract is here:", ct->contractDefinition().location()), @@ -3034,7 +3031,7 @@ void TypeChecker::checkCallList( ) { if (arguments.empty()) { m_errorReporter.typeError( - 228_error, + 5070_error, _functionCall.location(), "At least one argument of function or contract type is expected." ); @@ -3050,7 +3047,7 @@ void TypeChecker::checkCallList( std::vector> const &calleeParams = functionDeclaration->parameters(); if (1 + shift + calleeParams.size() != arguments.size()) { m_errorReporter.typeError( - 228_error, + 5424_error, _functionCall.location(), SecondarySourceLocation() .append("Declaration is here:", functionDeclaration->location()), @@ -3077,7 +3074,7 @@ void TypeChecker::checkCallList( auto contractType = dynamic_cast(tt->actualType()); const auto &contractDefinition = contractType->contractDefinition(); m_errorReporter.fatalTypeError( - 228_error, + 2182_error, _functionCall.location(), SecondarySourceLocation().append("Declaration is here:", contractDefinition.location()), @@ -3105,7 +3102,7 @@ void TypeChecker::checkBuildExtMsg(FunctionCall const& _functionCall) { int index = findName(name); if (index == -1){ m_errorReporter.fatalTypeError( - 228_error, + 2651_error, _functionCall.location(), "Parameter \"" + name + "\" must be set." ); @@ -3119,7 +3116,7 @@ void TypeChecker::checkBuildExtMsg(FunctionCall const& _functionCall) { auto const& ann = arguments[SignIndex]->annotation(); if (ann.type->category() != Type::Category::Bool || !*ann.isPure) { m_errorReporter.typeError( - 228_error, + 9588_error, arguments[SignIndex]->location(), "\"sign\" parameter must have a constant boolean type." ); @@ -3160,7 +3157,7 @@ void TypeChecker::checkBuildExtMsg(FunctionCall const& _functionCall) { } if (!(callBackFunction || (intType && !intType->isSigned() && intType->numBits() <= 32))) { m_errorReporter.typeError( - 228_error, + 1693_error, arguments[nameIndex]->location(), std::string{} + "Invalid type for argument in function call. " + @@ -3188,7 +3185,7 @@ void TypeChecker::checkRemoteAndCallBackFunctions( ContractDefinition const *callbackContract = callbackFunc->annotation().contract; if (!m_currentContract->derivesFrom(*callbackContract)) { m_errorReporter.typeError( - 228_error, + 7940_error, _location, "Callback function should belong to this contract or any of base contracts." ); @@ -3199,7 +3196,7 @@ void TypeChecker::checkRemoteAndCallBackFunctions( const TypePointers& retTypes = calleeDefinition->functionType({})->returnParameterTypes(); // check function without return if (callbackParams.parameters().size() != retTypes.size()) { m_errorReporter.typeError( - 228_error, + 2691_error, _location, SecondarySourceLocation() .append("Declaration of the callee function:", calleeDefinition->location()) @@ -3212,7 +3209,7 @@ void TypeChecker::checkRemoteAndCallBackFunctions( for (std::size_t p = 0; p < callbackParams.parameters().size(); ++p) { if (*callbackParams.parameters().at(p)->type() != *retTypes.at(p)) { m_errorReporter.typeError( - 228_error, + 2572_error, _location, SecondarySourceLocation() .append("Parameter of the callee function:", calleeDefinition->returnParameters().at(p)->location()) @@ -3226,7 +3223,7 @@ void TypeChecker::checkRemoteAndCallBackFunctions( void TypeChecker::checkOnErrorId(FunctionDefinition const* errorFunction, langutil::SourceLocation const& _location) { auto badOnErrorFunction = [&]() { m_errorReporter.typeError( - 228_error, + 2992_error, _location, SecondarySourceLocation().append("Parameters of the error function:", errorFunction->location()), "Error function must take (uint32 sdkError, uint32 exitCode)." @@ -3358,7 +3355,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ){ if (!cmpOperator(arguments.size(), arguments_cnt)) { m_errorReporter.fatalTypeError( - 228_error, + 8309_error, _functionCall.location(), errorMsg ); @@ -3368,7 +3365,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) Type::Category cat = arg->annotation().type->mobileType()->category(); if (cat != Type::Category::Integer && cat != Type::Category::VarInteger) { m_errorReporter.fatalTypeError( - 228_error, + 4283_error, arg->location(), "Expected an integer or variable integer type." ); @@ -3384,7 +3381,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ){ if (!cmpOperator(arguments.size(), arguments_cnt)) { m_errorReporter.fatalTypeError( - 228_error, + 1040_error, _functionCall.location(), errorMsg ); @@ -3394,7 +3391,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) Type::Category cat = arg->annotation().type->mobileType()->category(); if (cat != Type::Category::Integer && cat != Type::Category::FixedPoint && cat != Type::Category::VarInteger) { m_errorReporter.fatalTypeError( - 228_error, + 6033_error, arg->location(), "Expected integer, variable integer or fixed point type." ); @@ -3413,7 +3410,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } if (areAllConstants && haveAnyFraction) { m_errorReporter.fatalTypeError( - 228_error, + 1164_error, loc, "Cannot perform operation for constant literals. Please convert at least one function argument to an explicit type." ); @@ -3427,7 +3424,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) result = Type::commonType(result, rightType); if (result == nullptr) { m_errorReporter.fatalTypeError( - 228_error, + 2304_error, arguments.at(i)->location(), "All arguments must have signed or unsigned integer type at the same time." ); @@ -3451,7 +3448,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto checkHaveNamedParams = [&]() { if (argumentNames.empty()) m_errorReporter.fatalTypeError( - 228_error, + 8461_error, _functionCall.location(), string("Function parameters should be specified with names.") ); @@ -3459,7 +3456,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto checkAtLeastOneArg = [&]() { if (arguments.empty()) { - m_errorReporter.fatalTypeError(228_error, _functionCall.location(), "Expected at least one argument."); + m_errorReporter.fatalTypeError(5648_error, _functionCall.location(), "Expected at least one argument."); } }; @@ -3480,7 +3477,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) const Type *givenType = arguments.at(i)->annotation().type; const Type *expType = paramTypes.at(i); if (!givenType->isImplicitlyConvertibleTo(*expType)) { - m_errorReporter.typeError(228_error, arguments.at(i)->location(), + m_errorReporter.typeError(1580_error, arguments.at(i)->location(), "Expected " + expType->canonicalName() + " type, but given " + givenType->canonicalName()); } } @@ -3491,7 +3488,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::ABIDecode: { if (arguments.size() != 2) { - m_errorReporter.fatalTypeError(228_error, _functionCall.location(), "Expected two arguments."); + m_errorReporter.fatalTypeError(1690_error, _functionCall.location(), "Expected two arguments."); } expectType(*arguments.at(0), *TypeProvider::tvmcell(), false); ASTPointer arg1 = arguments.at(1); @@ -3534,7 +3531,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) { if (arguments.size() != 1) { m_errorReporter.fatalTypeError( - 228_error, + 7016_error, _functionCall.location(), string("Expected one argument.") ); @@ -3585,7 +3582,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) { if (arguments.size() != 1) { m_errorReporter.fatalTypeError( - 228_error, + 5457_error, _functionCall.location(), string("Expected one argument.") ); @@ -3593,7 +3590,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ContractType const* ct = getContractType(arguments.front().get()); if (ct == nullptr) { m_errorReporter.fatalTypeError( - 228_error, + 7161_error, arguments.front()->location(), // TODO move "Expected contract type." ); @@ -3686,7 +3683,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) bool isConst = *arguments[1]->annotation().isPure; if (!isConst) { m_errorReporter.fatalTypeError( - 228_error, + 8650_error, arguments.at(1)->location(), "Expected a constant integer type but got " + arguments[1]->annotation().type->toString() + "." @@ -3710,19 +3707,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto memberAccess = dynamic_cast(&_functionCall.expression()); auto mapType = dynamic_cast(memberAccess->expression().annotation().type); - const Type * keyType; - const Type * valueType; - if (mapType == nullptr) { - auto eccType = dynamic_cast(memberAccess->expression().annotation().type); - keyType = eccType->keyType(); - valueType = eccType->valueType(); - } else { - keyType = mapType->realKeyType(); - valueType = mapType->valueType(); - } + Type const* keyType = mapType->realKeyType(); + Type const* valueType = mapType->valueType(); if (functionType->kind() == FunctionType::Kind::MappingGetNextKey) { if (arguments.size() != 1) { - m_errorReporter.typeError(228_error, _functionCall.location(), "Expected one argument."); + m_errorReporter.typeError(1385_error, _functionCall.location(), "Expected one argument."); } else { auto arg0Type = arguments[0]->annotation().type; if (keyType->category() == Type::Category::Integer) { @@ -3732,7 +3721,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) arg0Type->toString() + " is not implicitly convertible to expected type " + keyType->toString() + "."; - m_errorReporter.typeError(228_error, arguments[0]->location(), errorMsg); + m_errorReporter.typeError(3030_error, arguments[0]->location(), errorMsg); } } auto arrKey = dynamic_cast(keyType); @@ -3745,7 +3734,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } } else { if (!arguments.empty()) { - m_errorReporter.typeError(228_error, arguments[0]->location(), "Expected no arguments."); + m_errorReporter.typeError(5538_error, arguments[0]->location(), "Expected no arguments."); } } std::vector members = {keyType, valueType}; @@ -3756,7 +3745,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto cat = arguments[0]->annotation().type->category(); if (cat != Type::Category::StringLiteral) { m_errorReporter.fatalTypeError( - 228_error, + 1878_error, arguments[0]->location(), string("Expected string literal.") ); @@ -3764,7 +3753,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto lit = dynamic_cast(arguments[0].get()); if (lit == nullptr) { m_errorReporter.fatalTypeError( - 228_error, + 4336_error, arguments[0]->location(), string("Expected string literal.") ); @@ -3780,7 +3769,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } if (arguments.size() != placeholdersCnt + 1) { m_errorReporter.fatalTypeError( - 228_error, + 2689_error, _functionCall.location(), string("Number of arguments is not equal to the number of placeholders!") ); @@ -3796,7 +3785,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) int index = findName(name); if (index == -1) { m_errorReporter.typeError( - 228_error, + 3252_error, _functionCall.location(), string("Parameter \"" + name + "\" must be set.") ); @@ -3862,7 +3851,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } if (!hasValue) { m_errorReporter.fatalTypeError( - 228_error, + 3221_error, _functionCall.location(), string("Parameter \"value\" must be set.") ); @@ -3877,7 +3866,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::TVMFunctionId: { if (arguments.size() != 1) { m_errorReporter.fatalTypeError( - 228_error, + 7354_error, _functionCall.location(), "One argument of function type is expected." ); @@ -3902,7 +3891,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::LogTVM: { if (arguments.size() != 1) { m_errorReporter.typeError( - 228_error, + 8696_error, _functionCall.location(), "Expected one argument." ); @@ -3918,7 +3907,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) errMsg = "Expected a string literal, but got " + type->toString() + "."; } if (!errMsg.empty()) { - m_errorReporter.typeError(228_error, arguments.at(0)->location(), errMsg); + m_errorReporter.typeError(1045_error, arguments.at(0)->location(), errMsg); } else { typeCheckFunctionCall(_functionCall, functionType); } @@ -3928,13 +3917,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::SHA256: { if (arguments.size() != 1) { - m_errorReporter.fatalTypeError(228_error, _functionCall.location(), "Expected one argument."); + m_errorReporter.fatalTypeError(3449_error, _functionCall.location(), "Expected one argument."); } Type const* argType = arguments.at(0)->annotation().type->mobileType(); auto arrayType = dynamic_cast(argType); if (!((arrayType && arrayType->isByteArrayOrString()) || dynamic_cast(argType))) { - m_errorReporter.fatalTypeError(228_error, arguments.at(0)->location(), "Expected bytes, string or TvmSlice type."); + m_errorReporter.fatalTypeError(7972_error, arguments.at(0)->location(), "Expected bytes, string or TvmSlice type."); } paramTypes.push_back(argType); returnTypes.emplace_back(TypeProvider::uint256()); @@ -3944,13 +3933,18 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) paramTypes.push_back(TypeProvider::boolean()); checkAtLeastOneArg(); if (arguments.size() >= 2) { - paramTypes.push_back(TypeProvider::uint(16)); + Type const* type1 = arguments.at(1)->annotation().type; + auto arr = dynamic_cast(type1); + if (dynamic_cast(type1) || (arr && arr->isString())) + paramTypes.push_back(TypeProvider::stringStorage()); + else + paramTypes.push_back(TypeProvider::uint(16)); } if (arguments.size() >= 3) { paramTypes.push_back(arguments.at(2)->annotation().type->mobileType()); } if (arguments.size() >= 4) { - m_errorReporter.typeError(228_error, _functionCall.location(), "Expected at most 3 arguments."); + m_errorReporter.typeError(7843_error, _functionCall.location(), "Expected at most 3 arguments."); } checkArgConversion(); break; @@ -3963,20 +3957,20 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) paramTypes.push_back(arguments.at(1)->annotation().type->mobileType()); } if (arguments.size() >= 3) { - m_errorReporter.typeError(228_error, _functionCall.location(), "Expected at most 2 arguments."); + m_errorReporter.typeError(1683_error, _functionCall.location(), "Expected at most 2 arguments."); } checkArgConversion(); break; } case FunctionType::Kind::TVMDump: { if (arguments.size() != 1) { - m_errorReporter.typeError(228_error, _functionCall.location(), "Expected one argument."); + m_errorReporter.typeError(3797_error, _functionCall.location(), "Expected one argument."); } auto type = arguments[0]->annotation().type->mobileType(); auto cat = type->category(); if (cat != Type::Category::Integer && cat !=Type::Category::TvmCell) { m_errorReporter.fatalTypeError( - 228_error, + 8093_error, arguments[0]->location(), "Argument must have a TvmCell or integer type." ); @@ -3986,13 +3980,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::TVMHash: { if (arguments.size() != 1) { - m_errorReporter.typeError(228_error, _functionCall.location(), "Expected one argument."); + 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) { m_errorReporter.fatalTypeError( - 228_error, + 5802_error, arguments[0]->location(), "Expected string, bytes, TvmCell or TvmSlice types, but got " + type->toString() + " type." ); @@ -4040,7 +4034,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (_functionCall.isAwait()) { if (functionType == nullptr || functionType->kind() != FunctionType::Kind::External) { m_errorReporter.fatalTypeError( - 228_error, + 5451_error, _functionCall.location(), "\".await\" is supported only for external function calls." ); @@ -4049,7 +4043,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto fd = dynamic_cast(&functionType->declaration()); if (fd && !fd->isResponsible()) { m_errorReporter.fatalTypeError( - 228_error, + 9054_error, _functionCall.location(), "\".await\" is supported only for responsible functions." ); @@ -4162,7 +4156,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) if (std::find(arr.begin(), arr.end(), name) == arr.end()) { m_errorReporter.typeError( - 228_error, + 7867_error, _functionCallOptions.location(), "Unknown option \"" + name + "\". " + "Valid options are " + fold() + "." @@ -4173,7 +4167,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) setCheckOption(setSign, "sign", i); if (!*options[i]->annotation().isPure) m_errorReporter.typeError( - 228_error, + 5289_error, _functionCallOptions.location(), R"(Option "sign" can be specified only with constant bool value.)"); } else if (name == "pubkey") { @@ -4197,7 +4191,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) auto intType = dynamic_cast(type(*exp.get())->mobileType()); if (!(callBackFunction || (intType && !intType->isSigned() && intType->numBits() <= 32))) { m_errorReporter.typeError( - 228_error, + 3344_error, options.at(i)->location(), "Expected functionID of uint32 type or just function name (ContractName.functionName or functionName)." ); @@ -4214,7 +4208,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) auto intType = dynamic_cast(type(*exp.get())->mobileType()); if (!(errorFunDef || (intType && !intType->isSigned() && intType->numBits() <= 32))) { m_errorReporter.typeError( - 228_error, + 7946_error, options.at(i)->location(), "Expected functionID of uint32 type or just function name (ContractName.functionName or functionName)." ); @@ -4264,11 +4258,11 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) if (isNewExpression) { if (setStateInit == -1 && setCode == -1) { - m_errorReporter.typeError(228_error, _functionCallOptions.location(), R"(Either option "stateInit" or option "code" must be set.)"); + m_errorReporter.typeError(5391_error, _functionCallOptions.location(), R"(Either option "stateInit" or option "code" must be set.)"); } if (setStateInit != -1 && setPubkey != -1) { m_errorReporter.declarationError( - 228_error, + 4939_error, options.at(setPubkey)->location(), SecondarySourceLocation().append(R"(Option "stateInit" is set here: )", options.at(setStateInit)->location()), R"(Option "pubkey" is not compatible with option "stateInit". Only with option "code".)" @@ -4276,14 +4270,14 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) } if (setStateInit != -1 && setVarInit != -1) { m_errorReporter.declarationError( - 228_error, + 7888_error, options.at(setVarInit)->location(), SecondarySourceLocation().append(R"(Option "stateInit" is set here: )", options.at(setStateInit)->location()), R"(Option "varInit" is not compatible with option "stateInit". Only with option "code".)" ); } if (setValue == -1) { - m_errorReporter.typeError(228_error, _functionCallOptions.location(), R"(Option "value" must be set.)"); + m_errorReporter.typeError(4337_error, _functionCallOptions.location(), R"(Option "value" must be set.)"); } if (setVarInit != -1) { auto newExpr = to(&_functionCallOptions.expression()); @@ -4378,7 +4372,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (to(&_memberAccess.expression()) != nullptr) if (memberName == "value" || memberName == "flag") m_errorReporter.fatalTypeError( - 228_error, + 9836_error, _memberAccess.location(), string("\".value()\" and \".flag()\" functionality is ") + "deprecated, use call options in {} instead." @@ -4669,17 +4663,6 @@ bool TypeChecker::visit(IndexAccess const& _access) isLValue = true; break; } - case Type::Category::ExtraCurrencyCollection: - { - ExtraCurrencyCollectionType const& actualType = dynamic_cast(*baseType); - if (!index) - m_errorReporter.typeError(228_error, _access.location(), "Index expression cannot be omitted."); - else - expectType(*index, *actualType.keyType()); - resultType = actualType.valueType(); - isLValue = true; - break; - } case Type::Category::TypeType: { TypeType const& typeType = dynamic_cast(*baseType); @@ -4729,7 +4712,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { TvmVectorType const& actualType = dynamic_cast(*baseType); if (!index) - m_errorReporter.typeError(228_error, _access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(5357_error, _access.location(), "Index expression cannot be omitted."); else { expectType(*index, *TypeProvider::uint256()); @@ -5048,7 +5031,7 @@ bool TypeChecker::visit(Mapping const& _mapping) { if (contractType->contractDefinition().isLibrary()) m_errorReporter.typeError( - 228_error, + 3734_error, keyType->location(), "Library types cannot be used as mapping keys." ); @@ -5058,7 +5041,7 @@ bool TypeChecker::visit(Mapping const& _mapping) keyType->annotation().type->category() != Type::Category::Struct ) m_errorReporter.typeError( - 228_error, + 5411_error, keyType->location(), "Only elementary types, contract types, structures (fitted in one cell) or enums are allowed as mapping keys." ); diff --git a/compiler/libsolidity/analysis/ViewPureChecker.cpp b/compiler/libsolidity/analysis/ViewPureChecker.cpp index edf7f4ca..bdd40fcf 100644 --- a/compiler/libsolidity/analysis/ViewPureChecker.cpp +++ b/compiler/libsolidity/analysis/ViewPureChecker.cpp @@ -49,12 +49,12 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()}; if (ContractDefinition const* contr = _funDef.annotation().contract) { if (contr->isLibrary() && _funDef.stateMutability() != StateMutability::NonPayable) { - m_errorReporter.warning(228_error, _funDef.location(), + m_errorReporter.warning(3818_error, _funDef.location(), "Library functions must have default mutability. Delete keyword view or pure."); } } if (_funDef.isFree() && !_funDef.isInlineAssembly() && _funDef.stateMutability() != StateMutability::NonPayable) { - m_errorReporter.warning(228_error, _funDef.location(), + m_errorReporter.warning(4029_error, _funDef.location(), "Free functions must have default mutability. Delete keyword view or pure."); } return true; @@ -167,13 +167,13 @@ void ViewPureChecker::reportMutability( "environment or state and thus requires \"view\"."; if (funcDecl) { m_errorReporter.typeError( - 228_error, + 6669_error, _location, SecondarySourceLocation().append("Function declaration is here", *funcDecl), errText ); } else { - m_errorReporter.typeError(228_error, _location, errText); + m_errorReporter.typeError(7706_error, _location, errText); } m_errors = true; } @@ -185,13 +185,13 @@ void ViewPureChecker::reportMutability( "requires the default."; if (funcDecl) { m_errorReporter.typeError( - 228_error, + 5861_error, _location, SecondarySourceLocation().append("Function declaration is here", *funcDecl), errText ); } else { - m_errorReporter.typeError(228_error, _location, errText); + m_errorReporter.typeError(9527_error, _location, errText); } m_errors = true; } @@ -310,7 +310,6 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) if (isStateVar) mutability = StateMutability::View; break; - case Type::Category::ExtraCurrencyCollection: case Type::Category::Mapping: if (member == "delMin" || member == "delMax" || diff --git a/compiler/libsolidity/ast/TypeProvider.cpp b/compiler/libsolidity/ast/TypeProvider.cpp index 0c786275..b7c1ad06 100644 --- a/compiler/libsolidity/ast/TypeProvider.cpp +++ b/compiler/libsolidity/ast/TypeProvider.cpp @@ -721,8 +721,6 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& return tvmslice(); case Token::TvmBuilder: return tvmbuilder(); - case Token::ExtraCurrencyCollection: - return extraCurrencyCollection(); case Token::Bytes: return bytesStorage(); case Token::String: @@ -862,7 +860,7 @@ StringLiteralType const* TypeProvider::stringLiteral(string const& literal) return instance().m_stringLiteralTypes.emplace(literal, make_unique(literal)).first->second.get(); } -VarInteger const* TypeProvider::varInteger(unsigned m, IntegerType::Modifier _modifier) { +VarIntegerType const* TypeProvider::varInteger(unsigned m, IntegerType::Modifier _modifier) { auto& map = instance().m_varInterger; auto i = map.find(make_pair(m, _modifier)); if (i != map.end()) @@ -870,7 +868,7 @@ VarInteger const* TypeProvider::varInteger(unsigned m, IntegerType::Modifier _mo return map.emplace( make_pair(m, _modifier), - make_unique(m, _modifier) + make_unique(m, _modifier) ).first->second.get(); } @@ -1053,9 +1051,11 @@ MappingType const* TypeProvider::mapping(Type const* _keyType, Type const* _valu return createAndGet(_keyType, _valueType); } -ExtraCurrencyCollectionType const *TypeProvider::extraCurrencyCollection() +MappingType const *TypeProvider::extraCurrencyCollection() { - return createAndGet(); + auto keyType = TypeProvider::uint(32); + auto valueType = TypeProvider::varInteger(32, IntegerType::Modifier::Unsigned); + return createAndGet(keyType, valueType); } OptionalType const* TypeProvider::optional(Type const* _type) diff --git a/compiler/libsolidity/ast/TypeProvider.h b/compiler/libsolidity/ast/TypeProvider.h index e5060039..f39e73a5 100644 --- a/compiler/libsolidity/ast/TypeProvider.h +++ b/compiler/libsolidity/ast/TypeProvider.h @@ -109,7 +109,7 @@ class TypeProvider static IntegerType const* uint256() { return uint(256); } static IntegerType const* int256() { return integer(256, IntegerType::Modifier::Signed); } - static VarInteger 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); static StringLiteralType const* stringLiteral(std::string const& literal); @@ -199,7 +199,7 @@ class TypeProvider static MappingType const* mapping(Type const* _keyType, Type const* _valueType); - static ExtraCurrencyCollectionType const* extraCurrencyCollection(); + static MappingType const* extraCurrencyCollection(); static OptionalType const* optional(Type const* _type); @@ -245,7 +245,7 @@ class TypeProvider 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 d3a93e33..8fe60ed6 100644 --- a/compiler/libsolidity/ast/Types.cpp +++ b/compiler/libsolidity/ast/Types.cpp @@ -455,7 +455,8 @@ BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const return true; else if (dynamic_cast(&_convertTo)) return true; - return isImplicitlyConvertibleTo(_convertTo); + + return false; } string AddressType::toString(bool) const @@ -591,14 +592,16 @@ BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return true; } - if (_convertTo.category() == Category::VarInteger){ - IntegerType const& convertTo = dynamic_cast(_convertTo).asIntegerType(); - if (convertTo.m_bits < m_bits) + if (_convertTo.category() == Category::VarInteger) + { + IntegerType const& convertTo = dynamic_cast(_convertTo).asIntegerType(); + // disallowing unsigned to signed conversion of different bits + if (isSigned() != convertTo.isSigned()) + return false; + else if (convertTo.m_bits < m_bits) return false; - else if (isSigned()) - return convertTo.isSigned(); else - return !convertTo.isSigned() || convertTo.m_bits > m_bits; + return true; } else if (_convertTo.category() == category()) { @@ -630,13 +633,22 @@ BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || - _convertTo.category() == Category::VarInteger || - _convertTo.category() == Category::Address || - _convertTo.category() == Category::Contract || - _convertTo.category() == Category::Enum || - (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast(_convertTo).numBytes() * 8) || - _convertTo.category() == Category::FixedPoint; + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + else if (auto integerType = dynamic_cast(&_convertTo)) + return (numBits() == integerType->numBits()) || (isSigned() == integerType->isSigned()); + else if (auto varIntegerType = dynamic_cast(&_convertTo)) + return (numBits() == varIntegerType->asIntegerType().numBits()) || (isSigned() == varIntegerType->asIntegerType().isSigned()); + else if (dynamic_cast(&_convertTo) || dynamic_cast(&_convertTo)) + return !isSigned(); + else if (auto fixedBytesType = dynamic_cast(&_convertTo)) + return (!isSigned() && (numBits() == fixedBytesType->numBytes() * 8)); + else if (dynamic_cast(&_convertTo)) + return true; + else if (auto fixedPointType = dynamic_cast(&_convertTo)) + return (isSigned() == fixedPointType->isSigned()) && (numBits() == fixedPointType->numBits()); + + return false; } TypeResult IntegerType::unaryOperatorResult(Token _operator) const @@ -1046,7 +1058,7 @@ BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) { if (isFractional()) return false; - IntegerType const& targetType = dynamic_cast(_convertTo).asIntegerType(); + IntegerType const& targetType = dynamic_cast(_convertTo).asIntegerType(); return fitsIntegerType(m_value.numerator(), targetType); } case Category::Integer: @@ -1085,8 +1097,13 @@ BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) auto category = _convertTo.category(); if (category == Category::FixedBytes) return false; + else if (dynamic_cast(&_convertTo) || dynamic_cast(&_convertTo)) + return (m_value == 0) || + (!isNegative() && + !isFractional() && + integerType()); else if (category == Category::Integer) - return true; + return false; else if (auto enumType = dynamic_cast(&_convertTo)) if (isNegative() || isFractional() || m_value >= enumType->numberOfMembers()) return false; @@ -1425,13 +1442,19 @@ BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) con BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (auto arr = dynamic_cast(&_convertTo); - arr && arr->isByteArray() - ) { + arr && arr->isByteArray()) return true; - } - return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast(_convertTo).numBits()) || - _convertTo.category() == Category::FixedPoint || - _convertTo.category() == category(); + + if (_convertTo.category() == category()) + return true; + else if (auto integerType = dynamic_cast(&_convertTo)) + return (!integerType->isSigned() && integerType->numBits() == numBytes() * 8); + else if (dynamic_cast(&_convertTo)) + return numBytes() == 32; + else if (auto fixedPointType = dynamic_cast(&_convertTo)) + return fixedPointType->numBits() == numBytes() * 8; + + return false; } TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const @@ -5041,7 +5064,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const } else if (m_typeArgument->category() == Type::Category::VarInteger) { - VarInteger const* varIntTypePointer = dynamic_cast(m_typeArgument); + VarIntegerType const* varIntTypePointer = dynamic_cast(m_typeArgument); return MemberList::MemberMap({ {"min", varIntTypePointer}, {"max", varIntTypePointer}, @@ -5780,7 +5803,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { }, { "skip", TypeProvider::function( - strings{"uint10", "uint2"}, + strings{"uint10", "uint3"}, strings{}, FunctionType::Kind::TVMSliceSkip, StateMutability::Pure @@ -5898,45 +5921,85 @@ TypeResult TvmCellType::unaryOperatorResult(Token _operator) const { MemberList::MemberMap TvmCellType::nativeMembers(const ASTNode *) const { - MemberList::MemberMap members; - - members.emplace_back("depth", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMCellDepth, - StateMutability::Pure - )); - - members.emplace_back("toSlice", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmslice()}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMCellToSlice, - StateMutability::Pure - )); - - members.emplace_back("dataSize", TypeProvider::function( - {TypeProvider::uint256()}, - {TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}, - {{}}, - {{}, {}, {}}, - FunctionType::Kind::TVMDataSize, - StateMutability::Pure - )); - - members.emplace_back("dataSizeQ", TypeProvider::function( - {TypeProvider::uint256()}, - {TypeProvider::optional(TypeProvider::tuple({TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}))}, - {{}}, - {{}}, - FunctionType::Kind::TVMDataSize, - StateMutability::Pure - )); - - + MemberList::MemberMap members = { + { + "depth", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(16)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMCellDepth, + StateMutability::Pure + ) + }, + { + "toSlice", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMCellToSlice, + StateMutability::Pure + ) + }, + { + "exoticToSlice", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice(), TypeProvider::boolean()}, + strings{}, + strings{string(), string()}, + FunctionType::Kind::TVMCellToSlice, + StateMutability::Pure + ) + }, + { + "loadExoticCell", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmcell()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMCellToSlice, + StateMutability::Pure + ) + }, + { + "loadExoticCellQ", + TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmcell(), TypeProvider::boolean()}, + strings{}, + strings{string(), string()}, + FunctionType::Kind::TVMCellToSlice, + StateMutability::Pure + ) + }, + { + "dataSize", + TypeProvider::function( + {TypeProvider::uint256()}, + {TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}, + {{}}, + {{}, {}, {}}, + FunctionType::Kind::TVMDataSize, + StateMutability::Pure + ) + }, + { + "dataSizeQ", + TypeProvider::function( + {TypeProvider::uint256()}, + {TypeProvider::optional(TypeProvider::tuple({TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}))}, + {{}}, + {{}}, + FunctionType::Kind::TVMDataSize, + StateMutability::Pure + ) + } + }; return members; } @@ -6124,6 +6187,16 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const StateMutability::Pure ) }, + { + "toExoticCell", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmcell()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, { "toSlice", TypeProvider::function( TypePointers{}, @@ -6310,21 +6383,21 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const return members; } -BoolResult VarInteger::isImplicitlyConvertibleTo(Type const& _convertTo) const { +BoolResult VarIntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { return m_int.isImplicitlyConvertibleTo(_convertTo); } -BoolResult VarInteger::isExplicitlyConvertibleTo(Type const& _convertTo) const { +BoolResult VarIntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return m_int.isExplicitlyConvertibleTo(_convertTo); } -TypeResult VarInteger::unaryOperatorResult(Token _operator) const { +TypeResult VarIntegerType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) return TypeProvider::emptyTuple(); return nullptr; } -TypeResult VarInteger::binaryOperatorResult(Token _operator, Type const* _other) const { +TypeResult VarIntegerType::binaryOperatorResult(Token _operator, Type const* _other) const { Type const* resultType = m_int.binaryOperatorResult(_operator, _other); if (resultType == nullptr) return nullptr; @@ -6334,11 +6407,11 @@ TypeResult VarInteger::binaryOperatorResult(Token _operator, Type const* _other) return resultType; } -std::string VarInteger::toString(bool) const { +std::string VarIntegerType::toString(bool) const { return std::string{} + "var" + (m_int.isSigned()? "Int" : "Uint") + std::to_string(m_n); } -int VarInteger::maxBitSizeInCell() const { +int VarIntegerType::maxBitSizeInCell() const { if (m_n == 16) { return 4 + (15 * 8); } @@ -6347,33 +6420,3 @@ int VarInteger::maxBitSizeInCell() const { } solUnimplemented(""); } - -bigint VarInteger::minValue() const { - return m_int.minValue(); -} - -bigint VarInteger::maxValue() const { - return m_int.maxValue(); -} - -IntegerType const* ExtraCurrencyCollectionType::keyType() const { - return TypeProvider::uint(32); -} - -IntegerType const* ExtraCurrencyCollectionType::valueType() const { - return TypeProvider::uint(256); -} - -VarInteger const* ExtraCurrencyCollectionType::realValueType() const { - return TypeProvider::varInteger(32, IntegerType::Modifier::Unsigned); -} - -MemberList::MemberMap ExtraCurrencyCollectionType::nativeMembers(ASTNode const *) const { - MemberList::MemberMap members; - appendMapMethods(members, keyType(), valueType(), keyType()); - return members; -} - -TypeResult ExtraCurrencyCollectionType::unaryOperatorResult(Token _operator) const { - return _operator == Token::Delete ? TypeProvider::tuple(std::vector()) : nullptr; -} diff --git a/compiler/libsolidity/ast/Types.h b/compiler/libsolidity/ast/Types.h index c1763273..88ad151e 100644 --- a/compiler/libsolidity/ast/Types.h +++ b/compiler/libsolidity/ast/Types.h @@ -175,7 +175,7 @@ class Type Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice, FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple, Mapping, TypeType, Modifier, Magic, Module, - InaccessibleDynamic, TvmCell, TvmSlice, TvmBuilder, ExtraCurrencyCollection, TvmVector, Variant, + InaccessibleDynamic, TvmCell, TvmSlice, TvmBuilder, TvmVector, Variant, VarInteger, InitializerList, CallList, // <-- variables of that types can't be declared in solidity contract Optional, @@ -793,10 +793,10 @@ class TvmBuilderType: public Type /** * The VarInteger type. */ -class VarInteger: public Type +class VarIntegerType: public Type { public: - explicit VarInteger(uint32_t n, IntegerType::Modifier _modifier) : m_n(n), m_int{(n-1)*8, _modifier} {} + explicit VarIntegerType(uint32_t _n, IntegerType::Modifier _modifier) : m_n{_n}, m_int{(_n - 1) * 8, _modifier} {} BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; Category category() const override { return Category::VarInteger; } @@ -811,8 +811,6 @@ class VarInteger: public Type int maxBitSizeInCell() const; IntegerType const& asIntegerType() const { return m_int; } uint32_t n() const { return m_n; } - bigint minValue() const; - bigint maxValue() const; private: uint32_t m_n{}; IntegerType m_int; @@ -852,29 +850,6 @@ class CallListType: public Type TypeResult interfaceType(bool) const override { return this; } }; -/** - * The ExtraCurrencyCollection type. - */ -class ExtraCurrencyCollectionType: public Type -{ -public: - explicit ExtraCurrencyCollectionType() {} - Category category() const override { return Category::ExtraCurrencyCollection; } - bool isValueType() const override { return true; } - std::string richIdentifier() const override { return "t_extracurrencycollection"; } - TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } - std::string toString(bool) const override { return "ExtraCurrencyCollection"; } - - Type const* encodingType() const override { return this; } - TypeResult interfaceType(bool) const override { return this; } - IntegerType const* keyType() const; - IntegerType const* valueType() const; - VarInteger const* realValueType() const; - TypeResult unaryOperatorResult(Token _operator) const override; - - MemberList::MemberMap nativeMembers(ASTNode const*) const override; -}; - /** * Base class for types which can be thought of as several elements of other types put together. * For example a struct is composed of its members, an array is composed of multiple copies of its diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp index 0e38c0b5..301c3790 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp @@ -83,7 +83,6 @@ class PrivatePeepholeOptimizer { static std::optional> isBLKDROP2(Pointerconst& node); static std::optional isPUSH(Pointer const& node); - static bool isPUSHINT(Pointer const& node); static bigint pushintValue(Pointer const& node); static int fetchInt(Pointer const& node); static bool isNIP(Pointer const& node); @@ -528,6 +527,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer } std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer const& cmd1, Pointer const& cmd2) { + using namespace MathConsts; auto cmd1GenOp = to(cmd1.get()); auto cmd1Glob = to(cmd1.get()); @@ -542,11 +542,6 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer auto isBLKPUSH1 = isBLKPUSH(cmd1); auto isPUSH1 = isPUSH(cmd1); - std::map power2; - for (int p2 = 2, p = 1; p2 <= 256; p2 *= 2, ++p) { - power2[p2] = p; - } - if (isSWAP(cmd1)) { if (is(cmd2, "STU")) return Result{2, gen("STUR " + arg(cmd2))}; if (is(cmd2, "STSLICE")) return Result{2, gen("STSLICER")}; @@ -564,7 +559,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer return Result{2, gen(opcode + " " + arg(cmd2))}; } } - if (isPUSHINT(cmd1)) { + if (is(cmd1, "PUSHINT")) { if (arg(cmd1) == "1") { if (is(cmd2, "ADD")) return Result{2, gen("INC")}; if (is(cmd2, "SUB")) return Result{2, gen("DEC")}; @@ -846,27 +841,40 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer } } if ( - isPUSHINT(cmd1) && 1 <= pushintValue(cmd1) && pushintValue(cmd1) <= 256 && + is(cmd1, "PUSHINT") && 1 <= pushintValue(cmd1) && pushintValue(cmd1) <= 256 && (is(cmd2, "RSHIFT") || is(cmd2, "LSHIFT")) && arg(cmd2).empty() ) { return Result{2, gen(cmd2GenOpcode->opcode() + " " + arg(cmd1))}; } - if (isPUSHINT(cmd1) && + if (is(cmd1, "PUSHINT") && (is(cmd2, "DIV") || is(cmd2, "MUL"))) { bigint val = pushintValue(cmd1); - if (power2.count(val)) { + if (power2Exp().count(val)) { const std::string& newOp = is(cmd2, "DIV") ? "RSHIFT" : "LSHIFT"; - return Result{2, gen(newOp + " " + toString(power2.at(val)))}; + return Result{2, gen(newOp + " " + toString(power2Exp().at(val)))}; } } - if (isPUSHINT(cmd1) && - is(cmd2, "MOD")) { + // PUSHINT 2**N + // MOD + // => + // MODPOW2 N + if (is(cmd1, "PUSHINT") && is(cmd2, "MOD")) { bigint val = pushintValue(cmd1); - if (power2.count(val)) { - return Result{2, gen("MODPOW2 " + toString(power2.at(val)))}; + if (power2Exp().count(val)) { + return Result{2, gen("MODPOW2 " + toString(power2Exp().at(val)))}; } } - if (isPUSHINT(cmd1)) { + // PUSHINT (2**N)-1 + // AND + // => + // MODPOW2 N + if (is(cmd1, "PUSHINT") && is(cmd2, "AND")) { + bigint val = pushintValue(cmd1); + if (power2DecExp().count(val)) { + return Result{2, gen("MODPOW2 " + toString(power2DecExp().at(val)))}; + } + } + if (is(cmd1, "PUSHINT")) { bigint val = pushintValue(cmd1); if (-128 <= val && val < 128) { if (is(cmd2, "NEQ")) @@ -953,7 +961,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer return Result{2, gen("STONE")}; } if ( - isPUSHINT(cmd1) && pushintValue(cmd1) == 0 && + is(cmd1, "PUSHINT") && pushintValue(cmd1) == 0 && is(cmd2, "STUR") ) { return Result{2, @@ -968,7 +976,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer } if ( - isPUSHINT(cmd1) && pushintValue(cmd1) == 1 && + is(cmd1, "PUSHINT") && pushintValue(cmd1) == 1 && is(cmd2, "STZEROES") ) { return Result{2, gen("STZERO")}; @@ -1082,6 +1090,10 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer return Result{2, makeTHROW("THROW " + cmd2Exc->arg())}; } + // pure gen(1, 1) + // DROP N + // => + // DROP N if (cmd1GenOp && cmd1GenOp->isPure() && std::make_pair(cmd1GenOp->take(), cmd1GenOp->ret()) == std::make_pair(1, 1) && isDrop(cmd2) @@ -1089,6 +1101,26 @@ std::optional PrivatePeepholeOptimizer::optimizeAt2(Pointer return Result{2, cmd2}; } + // ABS + // MODPOW2 256 + // => + // ABS + if (is(cmd1, "ABS") && + cmd2GenOpcode->opcode() == "MODPOW2" && cmd2GenOpcode->arg() == "256" + ) { + return Result{2, gen("ABS")}; + } + + // MODPOW2 x + // MODPOW2 y + // => + // MODPOW2 min(x, y) + if (is(cmd1, "MODPOW2") && is(cmd2, "MODPOW2")) { + int x = fetchInt(cmd1); + int y = fetchInt(cmd2); + return Result{2, gen("MODPOW2 " + toString(std::min(x, y)))}; + } + // BLKPUSH N, 0 / DUP // BLKPUSH Q, 0 / DUP // => @@ -1175,7 +1207,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer // => // PUSH S(i-1) or gen(0,1) // CMP2 - if (isPUSHINT(cmd1) && ((isPUSH2 && *isPUSH2 != 0) || isPureGen01(*cmd2))) { + if (is(cmd1, "PUSHINT") && ((isPUSH2 && *isPUSH2 != 0) || isPureGen01(*cmd2))) { auto newCmd2 = isPUSH2 ? makePUSH(*isPUSH2 - 1) : cmd2; bigint val = pushintValue(cmd1); if (-128 <= val && val < 128) { @@ -1193,8 +1225,8 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer if (-128 <= val - 1 && val - 1 < 128 && is(cmd3, "LEQ")) return Result{3, newCmd2, gen("GTINT " + toString(val - 1))}; } - if (isPUSHINT(cmd1) && - isPUSHINT(cmd2) && + if (is(cmd1, "PUSHINT") && + is(cmd2, "PUSHINT") && cmd3GenOpcode && cmd3GenOpcode->opcode() == "MUL" ) { bigint a = pushintValue(cmd1); @@ -1203,8 +1235,8 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer return Result{3, gen("PUSHINT " + toString(c))}; } - if (isPUSHINT(cmd1) && - isPUSHINT(cmd2) && + if (is(cmd1, "PUSHINT") && + is(cmd2, "PUSHINT") && cmd3GenOpcode && cmd3GenOpcode->opcode() == "DIV" ) { bigint a = pushintValue(cmd1); @@ -1284,7 +1316,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer if (cmd3SubProgram) { std::vector> const& instructions = cmd3SubProgram->block()->instructions(); if (instructions.size() == 1 && - *instructions.at(0) == *createNode(".inline __concatenateStrings_macro", 2, 1)) { + *instructions.at(0) == *createNode(".inline concatenateStrings", 2, 1)) { string hexStr = cmd1PushCellOrSlice->chainBlob() + cmd2PushCellOrSlice->chainBlob(); return Result{3, makePushCellOrSlice(hexStr, false)}; } @@ -1304,7 +1336,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer std::vector> const &cmds = ifRef->trueBody()->instructions(); if (cmds.size() == 1) { if (auto gen = to(cmds.at(0).get())) { - if (isIn(gen->fullOpcode(), ".inline __c7_to_c4", ".inline __upd_only_time_in_c4")) { + if (isIn(gen->fullOpcode(), ".inline c7_to_c4", ".inline upd_only_time_in_c4")) { return Result{2, cmd2}; } } @@ -1319,7 +1351,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer Pointer const& cmd3, Pointer const& cmd4) { auto cmd3Exc = to(cmd3.get()); - if (isPUSHINT(cmd1) && isPUSHINT(cmd3)) { + if (is(cmd1, "PUSHINT") && is(cmd3, "PUSHINT")) { if (isAddOrSub(cmd2) && isAddOrSub(cmd4)) { bigint sum = 0; sum += (is(cmd2, "ADD") ? +1 : -1) * pushintValue(cmd1); @@ -1357,7 +1389,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer } } } - if (isPUSHINT(cmd1) && + if (is(cmd1, "PUSHINT") && is(cmd2, "NEWC") && is(cmd3, "STSLICECONST") && is(cmd4, "STU")) { @@ -1379,9 +1411,9 @@ std::optional PrivatePeepholeOptimizer::optimizeAt4(Pointer ) { return Result{4, makePUSHREF(isPlainPushSlice(cmd1)->blob())}; } - if (isPUSHINT(cmd1) && pushintValue(cmd1) == 0 && + if (is(cmd1, "PUSHINT") && pushintValue(cmd1) == 0 && is(cmd2, "STUR") && - isPUSHINT(cmd3) && pushintValue(cmd3) == 0 && + is(cmd3, "PUSHINT") && pushintValue(cmd3) == 0 && is(cmd4, "STUR") ) { int bitSize = fetchInt(cmd2) + fetchInt(cmd4); @@ -1458,7 +1490,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt5(Pointer // NEWC // STSLICE ? // STU ? - if (isPUSHINT(cmd1) && + if (is(cmd1, "PUSHINT") && isPlainPushSlice(cmd2) && is(cmd3, "NEWC") && is(cmd4, "STSLICE") && @@ -1561,7 +1593,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { // NEWC // STU y else if ( - isPUSHINT(cmd1) && + is(cmd1, "PUSHINT") && is(cmd2, "NEWC") && is(cmd3, "STU") ) { @@ -1594,13 +1626,13 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { bitString += StrUtils::toBitString(arg(c1)); ++opcodeQty; i = j; - } else if (c2 && isPUSHINT(c1) && is(c2, "STUR")) { + } else if (c2 && is(c1, "PUSHINT") && is(c2, "STUR")) { bigint num = pushintValue(c1); int len = fetchInt(c2); bitString += StrUtils::toBitString(num, len); opcodeQty += 2; i = nextCommandLine(j); - } else if (c2 && isPUSHINT(c1) && is(c2, "STIR")) { + } else if (c2 && is(c1, "PUSHINT") && is(c2, "STIR")) { bigint num = pushintValue(c1); int len = fetchInt(c2); bitString += StrUtils::toBitString(num, len); @@ -1616,7 +1648,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { bitString += StrUtils::toBitString(hexSlice); opcodeQty += 2; i = nextCommandLine(j); - } else if (c2 && isPUSHINT(c1) && is(c2, "STGRAMS")) { + } else if (c2 && is(c1, "PUSHINT") && is(c2, "STGRAMS")) { bigint arg = pushintValue(c1); bitString += StrUtils::tonsToBinaryString(arg); opcodeQty += 2; @@ -2182,7 +2214,7 @@ std::optional PrivatePeepholeOptimizer::isPUSH(Pointer const& n } bigint PrivatePeepholeOptimizer::pushintValue(Pointer const& node) { - solAssert(isPUSHINT(node), ""); + solAssert(is(node, "PUSHINT"), ""); auto g = dynamic_pointer_cast(node); return bigint{g->arg()}; } @@ -2199,20 +2231,6 @@ bool PrivatePeepholeOptimizer::isNIP(Pointer const& node) { (isBLKDROP2(node) && isBLKDROP2(node).value() == std::make_pair(1, 1)); } -bool PrivatePeepholeOptimizer::isPUSHINT(Pointer const& node) { - auto g = dynamic_pointer_cast(node); - if (!g || g->opcode() != "PUSHINT") - return false; - int i = 0; - for (char ch : g->arg()) { - if (!(isdigit(ch) || (i == 0 && ch=='-'))) { - return false; // e.g. PUSHINT $func_name$ - } - ++i; - } - return true; -} - std::string PrivatePeepholeOptimizer::arg(Pointer const& node) { auto g = dynamic_pointer_cast(node); solAssert(g, ""); diff --git a/compiler/libsolidity/codegen/StackOptimizer.cpp b/compiler/libsolidity/codegen/StackOptimizer.cpp index 686248ae..a2f13468 100644 --- a/compiler/libsolidity/codegen/StackOptimizer.cpp +++ b/compiler/libsolidity/codegen/StackOptimizer.cpp @@ -269,9 +269,8 @@ bool StackOptimizer::visit(While &_node) { bool StackOptimizer::visit(Function &f) { switch (f.type()) { - case Function::FunctionType::PrivateFunction: case Function::FunctionType::PrivateFunctionWithObj: - case Function::FunctionType::Macro: + case Function::FunctionType::Fragment: case Function::FunctionType::OnCodeUpgrade: case Function::FunctionType::OnTickTock: { if (f.name() != "c7_to_c4_for_await") { @@ -287,7 +286,7 @@ bool StackOptimizer::visit(Function &f) { break; } - case Function::FunctionType::MacroGetter: + case Function::FunctionType::PublicStateVariableGetter: case Function::FunctionType::MainInternal: case Function::FunctionType::MainExternal: break; @@ -296,7 +295,7 @@ bool StackOptimizer::visit(Function &f) { } bool StackOptimizer::visit(Contract &_node) { - for (Pointer &f: _node.functions()) { + for (Pointer const& f: _node.functions()) { f->accept(*this); } return false; diff --git a/compiler/libsolidity/codegen/TVM.cpp b/compiler/libsolidity/codegen/TVM.cpp index 7b98b19a..d87475d4 100644 --- a/compiler/libsolidity/codegen/TVM.cpp +++ b/compiler/libsolidity/codegen/TVM.cpp @@ -82,7 +82,7 @@ void TVMCompilerProceedContract( TVMContractCompiler::generateCodeAndSaveToFile(pathToFiles + ".code", _contract, _sourceUnits, pragmaHelper); } if (generateAbi) { - TVMContractCompiler::generateABI(pathToFiles + ".abi.json", &_contract, *pragmaDirectives); + TVMContractCompiler::generateABI(pathToFiles + ".abi.json", &_contract, _sourceUnits, *pragmaDirectives); } } diff --git a/compiler/libsolidity/codegen/TVMABI.cpp b/compiler/libsolidity/codegen/TVMABI.cpp index 095e07d6..0b16b212 100644 --- a/compiler/libsolidity/codegen/TVMABI.cpp +++ b/compiler/libsolidity/codegen/TVMABI.cpp @@ -74,19 +74,19 @@ Json::Value TVMABI::generateFunctionIdsJson( Json::Value TVMABI::generatePrivateFunctionIdsJson( - const ContractDefinition &contract, + ContractDefinition const& contract, std::vector> _sourceUnits, - const PragmaDirectiveHelper &pragmaHelper + PragmaDirectiveHelper const& pragmaHelper ) { Json::Value ids{Json::arrayValue}; Pointer codeContract = TVMContractCompiler::generateContractCode(&contract, _sourceUnits, pragmaHelper); for (Pointer const& fun : codeContract->functions()) { - if (fun->type() == Function::FunctionType::PrivateFunction) { + if (fun->type() == Function::FunctionType::Fragment && fun->functionId()) { Json::Value func{Json::objectValue}; FunctionDefinition const* def = fun->functionDefinition(); func["scope"] = def->annotation().contract->name(); func["sign"] = def->externalSignature(); - func["id"] = ChainDataEncoder::toHash256(fun->name()); + func["id"] = fun->functionId().value(); ids.append(func); } } @@ -95,6 +95,7 @@ TVMABI::generatePrivateFunctionIdsJson( Json::Value TVMABI::generateABIJson( ContractDefinition const *contract, + std::vector> const& _sourceUnits, std::vector const &pragmaDirectives ) { PragmaDirectiveHelper pdh{pragmaDirectives}; @@ -105,6 +106,12 @@ Json::Value TVMABI::generateABIJson( for (const auto &_event : contract->definedInterfaceEvents()) events.push_back(_event); + for (std::shared_ptr const& source: _sourceUnits) + for (ASTPointer const &node: source->nodes()) + if (auto lib = dynamic_cast(node.get())) + if (lib->isLibrary()) + for (const auto &event : lib->definedInterfaceEvents()) + events.push_back(event); std::set used; @@ -163,12 +170,13 @@ Json::Value TVMABI::generateABIJson( for (const auto &e: events) { const auto &ename = e->name(); solAssert(!ename.empty(), "Empty event name!"); - if (usedEvents.count(ename)) { + string name = eventName(e); + if (usedEvents.count(name)) { solUnimplemented("Event name duplication!"); } - usedEvents.insert(ename); + usedEvents.insert(name); Json::Value cur; - cur["name"] = ename; + cur["name"] = name; cur["inputs"] = encodeParams(convertArray(e->parameters())); eventAbi.append(cur); } @@ -217,10 +225,11 @@ Json::Value TVMABI::generateABIJson( void TVMABI::generateABI( ContractDefinition const *contract, + std::vector> const& _sourceUnits, std::vector const &pragmaDirectives, ostream *out ) { - Json::Value root = generateABIJson(contract, pragmaDirectives); + Json::Value root = generateABIJson(contract, _sourceUnits, pragmaDirectives); // Json::StreamWriterBuilder builder; // const std::string json_file = Json::writeString(builder, root); @@ -439,7 +448,7 @@ 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); + auto varInt = to(type); typeName = varInt->toString(false); boost::algorithm::to_lower(typeName); } else if (ti.isNumeric) { @@ -690,10 +699,10 @@ void ChainDataDecoder::decodeParametersQ( pusher->blockSwap(n, 1); if (n == 1) { if (optValueAsTuple(types.at(0))) { - pusher->tuple(1); + pusher->makeTuple(1); } } else { - pusher->tuple(n); + pusher->makeTuple(n); } pusher->blockSwap(1, 1); } @@ -723,7 +732,7 @@ void ChainDataDecoder::decodeParameter(Type const* type, DecodePosition* positio // members... slice const int memberQty = members.size(); pusher->blockSwap(memberQty, 1); // slice members... - pusher->tuple(memberQty); // slice struct + pusher->makeTuple(memberQty); // slice struct pusher->exchange(1); // ... struct slice } else if (isIntegralType(type)) { TypeInfo ti{type}; @@ -742,7 +751,7 @@ void ChainDataDecoder::decodeParameter(Type const* type, DecodePosition* positio to(type) || to(type) || to(type) || - to(type) || + to(type) || (category == Type::Category::Address || category == Type::Category::Contract) ) { loadNextSliceIfNeed(position->loadNextCell(type)); @@ -852,16 +861,12 @@ std::pair ChainDataEncoder::calculateFunctionID(const CallableDe } uint32_t ChainDataEncoder::toHash256(std::string const& str) { - bytes hash = picosha2::hash256(bytes( - str.begin(), - str.end() - )); + bytes hash = picosha2::hash256(bytes(str.begin(), str.end())); uint32_t funcID = 0; for (size_t i = 0; i < 4; i++) { funcID <<= 8u; funcID += hash[i]; } - return funcID; } diff --git a/compiler/libsolidity/codegen/TVMABI.hpp b/compiler/libsolidity/codegen/TVMABI.hpp index 5e3513ae..55ff0d93 100644 --- a/compiler/libsolidity/codegen/TVMABI.hpp +++ b/compiler/libsolidity/codegen/TVMABI.hpp @@ -36,8 +36,10 @@ class TVMABI { PragmaDirectiveHelper const& pragmaHelper ); static void generateABI(ContractDefinition const* contract, + std::vector> const& _sourceUnits, std::vector const& pragmaDirectives, std::ostream* out = &std::cout); static Json::Value generateABIJson(ContractDefinition const* contract, + std::vector> const& _sourceUnits, std::vector const& pragmaDirectives); private: static std::vector publicFunctions(ContractDefinition const& contract); diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.cpp b/compiler/libsolidity/codegen/TVMAnalyzer.cpp index f61dbcde..61cfbd60 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.cpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.cpp @@ -39,7 +39,7 @@ bool TVMAnalyzer::visit(MemberAccess const& _node) { if (funType->bound()) { auto printError = [&]{ m_errorReporter.fatalTypeError( - 228_error, + 5939_error, _node.location(), "Function references are not supported if the function is called as arg1.fun(arg2, ..., argn)." ); @@ -69,7 +69,7 @@ bool TVMAnalyzer::visit(ContractDefinition const& contract) { for (EventDefinition const* event : contract.definedInterfaceEvents()) { if (used.count(event->name())) { m_errorReporter.declarationError( - 228_error, + 2018_error, event->location(), SecondarySourceLocation().append("Another declaration is here:", used.at(event->name())->location()), "Event overriding is not supported." @@ -101,7 +101,7 @@ bool TVMAnalyzer::visit(Return const& _return) { }; if (!hasName("value") || !hasName("bounce") || !hasName("flag")) { m_errorReporter.fatalDeclarationError( - 228_error, + 5133_error, _return.location(), std::string{} + "`value`, `bounce` and `flag` options must be explicitly defined for `responsible` functions.\n" + @@ -133,18 +133,18 @@ void TVMAnalyzer::endVisit(PragmaDirective const& _pragma) { auto checkConstInteger = [&](Expression const& _e, SourceLocation const& loc, const std::string& msg, const bigint& max_val){ if (_e.annotation().type->category() != Type::Category::RationalNumber) { - m_errorReporter.syntaxError(228_error, loc, msg); + m_errorReporter.syntaxError(5514_error, loc, msg); return; } auto number = dynamic_cast(_e.annotation().type); solAssert(number, ""); if (number->isFractional()) { - m_errorReporter.syntaxError(228_error, loc, msg); + m_errorReporter.syntaxError(8784_error, loc, msg); return; } bigint val = number->value2(); if (val < 0 || val >= max_val) { - m_errorReporter.syntaxError(228_error, loc, msg); + m_errorReporter.syntaxError(5971_error, loc, msg); return; } }; diff --git a/compiler/libsolidity/codegen/TVMCommons.cpp b/compiler/libsolidity/codegen/TVMCommons.cpp index 165fb2d3..1f7f0d3d 100644 --- a/compiler/libsolidity/codegen/TVMCommons.cpp +++ b/compiler/libsolidity/codegen/TVMCommons.cpp @@ -45,13 +45,20 @@ std::string functionName(FunctionDefinition const *_function) { return _function->name(); } +std::string eventName(EventDefinition const* _event) { + ContractDefinition const* contract = _event->annotation().contract; + if (contract->isLibrary()) + return contract->name() + "#" + _event->name(); + return _event->name(); +} + void cast_error(const ASTNode &node, const string &error_message) { - GlobalParams::g_errorReporter->fatalParserError(228_error, node.location(), error_message); + GlobalParams::g_errorReporter->fatalParserError(9768_error, node.location(), error_message); BOOST_THROW_EXCEPTION(FatalError()); // never throw, just for [[noreturn]] } void fatal_error(const string &error_message) { - GlobalParams::g_errorReporter->error(228_error, Error::Type::TypeError, SourceLocation(), error_message); + GlobalParams::g_errorReporter->error(5711_error, Error::Type::TypeError, SourceLocation(), error_message); BOOST_THROW_EXCEPTION(FatalError()); // never throw, just for [[noreturn]] } @@ -188,18 +195,9 @@ IntegerType const& getArrayKeyType() { std::tuple dictKeyValue(Type const* type) { - Type const* keyType{}; - Type const* valueType{}; - if (auto mapType = to(type)) { - keyType = mapType->keyType(); - valueType = mapType->valueType(); - } else if (auto ccType = to(type)) { - keyType = ccType->keyType(); - valueType = ccType->realValueType(); - } else { - solUnimplemented(""); - } - return {keyType, valueType}; + auto mapType = to(type); + solAssert(mapType, ""); + return {mapType->keyType(), mapType->valueType()}; } std::tuple @@ -209,9 +207,6 @@ realDictKeyValue(Type const* type) { if (auto mapType = to(type)) { keyType = mapType->realKeyType(); valueType = mapType->valueType(); - } else if (auto ccType = to(type)) { - keyType = ccType->keyType(); - valueType = ccType->realValueType(); } else { solUnimplemented(""); } @@ -255,10 +250,6 @@ bool isSuper(Expression const *expr) { return false; } -bool isMacro(const std::string &functionName) { - return boost::ends_with(functionName, "_macro"); -} - bool isAddressThis(const FunctionCall *funCall) { if (!funCall) return false; @@ -315,7 +306,7 @@ bool isEmptyFunction(FunctionDefinition const* f) { std::vector convertArray(std::vector> const& arr) { - std::vector ret; + std::vector ret; for (const auto& v : arr) ret.emplace_back(v.get()); return ret; @@ -358,8 +349,6 @@ DictValueType toDictValueType(const Type::Category& category) { return DictValueType::Contract; case Type::Category::Enum: return DictValueType::Enum; - case Type::Category::ExtraCurrencyCollection: - return DictValueType::ExtraCurrencyCollection; case Type::Category::FixedBytes: return DictValueType::FixedBytes; case Type::Category::Integer: @@ -385,18 +374,6 @@ DictValueType toDictValueType(const Type::Category& category) { } } -// e.g.: hello -> 68656c6c6f -std::string stringToHex(const std::string& str) { - std::string slice; - for (char index : str) { - std::stringstream ss; - ss << std::hex << std::setfill('0') << std::setw(2) - << (static_cast(index) & 0xFFu); - slice += ss.str(); - } - return slice; -} - std::set getAllBaseFunctions(CallableDeclaration const* f) { std::set res; for (CallableDeclaration const* base : f->annotation().baseFunctions) { @@ -421,7 +398,7 @@ void ABITypeSize::init(Type const* type) { solAssert(ti.isNumeric, ""); maxBits = ti.numBits; maxRefs = 0; - } else if (auto varInt = to(type)) { + } else if (auto varInt = to(type)) { maxBits = varInt->maxBitSizeInCell(); maxRefs = 0; } else if (auto arrayType = to(type)) { @@ -460,7 +437,7 @@ void ABITypeSize::init(Type const* type) { maxBits += size.maxBits; maxRefs += size.maxRefs; } - } else if (to(type) || to(type)) { + } else if (to(type)) { maxBits = 1; maxRefs = 1; } else if (to(type)) { @@ -630,9 +607,7 @@ std::string StrUtils::boolToBinaryString(bool value) { return value ? "1" : "0"; } -std::string StrUtils::literalToSliceAddress(Literal const* literal) { - Type const *type = literal->annotation().type; - u256 value = type->literalValue(literal); +std::string StrUtils::literalToSliceAddress(bigint const& value) { // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; std::string s; s += "10"; @@ -663,6 +638,19 @@ std::string StrUtils::toBinString(bigint num) { return res; } +// e.g.: hello -> 68656c6c6f +std::string StrUtils::stringToHex(const std::string& str) { + std::string slice; + for (char index : str) { + std::stringstream ss; + ss << std::hex << std::setfill('0') << std::setw(2) + << (static_cast(index) & 0xFFu); + slice += ss.str(); + } + return slice; +} + + std::optional ExprUtils::constValue(const Expression &_e) { if (*_e.annotation().isPure) { if (auto memberAccess = to(&_e)) { @@ -698,4 +686,52 @@ std::optional ExprUtils::constBool(Expression const& _e) { return {}; } +std::map const& MathConsts::power2Exp() { + static std::map power2Exp; + if (power2Exp.empty()) { + bigint p2 = 1; + for (int p = 0; p <= 256; ++p) { + power2Exp[p2] = p; + p2 *= 2; + } + } + return power2Exp; +} + +std::map const& MathConsts::power2DecExp() { + static std::map power2DecExp; + if (power2DecExp.empty()) { + bigint p2 = 1; + for (int p = 0; p <= 256; ++p) { + power2DecExp[p2 - 1] = p; + p2 *= 2; + } + } + return power2DecExp; +} + +std::map const& MathConsts::power2NegExp() { + static std::map power2NegExp; + if (power2NegExp.empty()) { + bigint p2 = 1; + for (int p = 0; p <= 256; ++p) { + power2NegExp[-p2] = p; + p2 *= 2; + } + } + return power2NegExp; +} + +std::map const& MathConsts::power10() { + static std::map power10; + if (power10.empty()) { + bigint p10 = 1; + for (int i = 0; i <= 80; ++i) { + power10[i] = p10; + p10 *= 10; + } + } + return power10; +} + } // end namespace solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMCommons.hpp b/compiler/libsolidity/codegen/TVMCommons.hpp index c8ebca45..8029df55 100644 --- a/compiler/libsolidity/codegen/TVMCommons.hpp +++ b/compiler/libsolidity/codegen/TVMCommons.hpp @@ -50,6 +50,7 @@ constexpr uint64_t str2int(const char* str, int i = 0) { } std::string functionName(FunctionDefinition const* _function); +std::string eventName(EventDefinition const* _event); bool isAddressOrContractType(const Type* type); bool isUsualArray(const Type* type); @@ -155,9 +156,6 @@ std::vector> getContractFunctionPairs(ContractDefinition const* contract); bool isSuper(Expression const* expr); - -bool isMacro(const std::string& functionName); - bool isAddressThis(const FunctionCall* funCall); // List of all function but constructors with a given name @@ -333,7 +331,6 @@ enum class DictValueType { Bool, Contract, Enum, - ExtraCurrencyCollection, FixedBytes, FixedPoint, Function, @@ -365,7 +362,6 @@ struct LValueInfo { }; DictValueType toDictValueType(const Type::Category& category); -std::string stringToHex(const std::string& str); std::set getAllBaseFunctions(CallableDeclaration const* f); bool isLoc(Pointer const& node); std::vector split(const std::string &s, char sep = '\n'); @@ -383,9 +379,10 @@ namespace StrUtils { std::string tonsToBinaryString(const u256& value); std::string tonsToBinaryString(bigint value); std::string boolToBinaryString(bool value); - std::string literalToSliceAddress(Literal const* literal); + std::string literalToSliceAddress(bigint const& value); bigint toBigint(const std::string& binStr); std::string toBinString(bigint num); + std::string stringToHex(const std::string& str); } namespace ExprUtils { @@ -393,4 +390,11 @@ namespace ExprUtils { std::optional constBool(Expression const &_e); } +namespace MathConsts { + std::map const& power2Exp(); + std::map const& power2DecExp(); + std::map const& power2NegExp(); + std::map const& power10(); +} + } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.cpp b/compiler/libsolidity/codegen/TVMContractCompiler.cpp index 98bff562..5d312114 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.cpp @@ -70,14 +70,15 @@ void TVMConstructorCompiler::dfs(ContractDefinition const *c) { } Pointer TVMConstructorCompiler::generateConstructors() { + FunctionDefinition const* constructor = m_pusher.ctx().getContract()->constructor(); + m_pusher.ctx().setCurrentFunction(constructor, "constructor"); + { ChainDataEncoder encode{&m_pusher}; - uint32_t functionId{}; - if (FunctionDefinition const* c = m_pusher.ctx().getContract()->constructor()) { - functionId = encode.calculateFunctionIDWithReason(c, ReasonOfOutboundMessage::RemoteCallInternal); - } else { - functionId = encode.calculateConstructorFunctionID(); - } + uint32_t functionId = + constructor != nullptr ? + encode.calculateFunctionIDWithReason(constructor, ReasonOfOutboundMessage::RemoteCallInternal) : + encode.calculateConstructorFunctionID(); m_pusher.ctx().addPublicFunction(functionId, "constructor"); } @@ -88,12 +89,10 @@ Pointer TVMConstructorCompiler::generateConstructors() { c4ToC7WithMemoryInitAndConstructorProtection(); std::vector linearizedBaseContracts = - m_pusher.ctx().getContract()->annotation().linearizedBaseContracts; // from derived to base - for (ContractDefinition const* c : linearizedBaseContracts) { + m_pusher.ctx().getContract()->annotation().linearizedBaseContracts; // from derived to base + for (ContractDefinition const* c : linearizedBaseContracts) dfs(c); - } - FunctionDefinition const* constructor = linearizedBaseContracts[0]->constructor(); int take{}; if (constructor == nullptr) { m_pusher << "ENDS"; @@ -102,38 +101,30 @@ Pointer TVMConstructorCompiler::generateConstructors() { vector types = getParams(constructor->parameters()).first; ChainDataDecoder{&m_pusher}.decodeFunctionParameters(types, false); m_pusher.getStack().change(-static_cast(constructor->parameters().size())); - for (const ASTPointer& variable: constructor->parameters()) { + for (const ASTPointer& variable: constructor->parameters()) m_pusher.getStack().add(variable.get(), true); - } } solAssert(m_pusher.stackSize() == take, ""); std::set areParamsOnStack; - areParamsOnStack.insert(linearizedBaseContracts[0]); - for (ContractDefinition const* c : linearizedBaseContracts | boost::adaptors::reversed) { - if (c->constructor() == nullptr || c->constructor()->parameters().empty()) { + areParamsOnStack.insert(linearizedBaseContracts.at(0)); + for (ContractDefinition const* c : linearizedBaseContracts | boost::adaptors::reversed) + if (c->constructor() == nullptr || c->constructor()->parameters().empty()) areParamsOnStack.insert(c); - } - } bool haveConstructor = false; for (ContractDefinition const* c : linearizedBaseContracts | boost::adaptors::reversed) { - if (c->constructor() == nullptr) { + if (c->constructor() == nullptr) continue; - } haveConstructor = true; for (ContractDefinition const* parent : path[c]) { if (areParamsOnStack.count(parent) == 0) { areParamsOnStack.insert(parent); - for (int i = 0; i < static_cast(parent->constructor()->parameters().size()); ++i) { + for (size_t i = 0; i < parent->constructor()->parameters().size(); ++i) { TVMExpressionCompiler(m_pusher).acceptExpr((*m_args[parent])[i].get(), true); m_pusher.getStack().add(parent->constructor()->parameters()[i].get(), false); } } } - - - m_pusher.ctx().setCurrentFunction(c->constructor()); - int take2 = c->constructor()->parameters().size(); StackPusher pusher = m_pusher; pusher.clear(); @@ -143,19 +134,17 @@ Pointer TVMConstructorCompiler::generateConstructors() { m_pusher.add(pusher); } - if (!haveConstructor) { + if (!haveConstructor) m_pusher << "ACCEPT"; - } // solAssert(m_pusher.stackSize() == 0, ""); - - m_pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); - + m_pusher.pushFragmentInCallRef(0, 0, "c7_to_c4"); m_pusher._throw("THROW 0"); + m_pusher.ctx().resetCurrentFunction(); Pointer block = m_pusher.getBlock(); // take slice (contains params) and functionID - Pointer f = createNode(2, 0, "constructor", Function::FunctionType::Macro, block); + Pointer f = createNode(2, 0, "constructor", nullopt, Function::FunctionType::Fragment, block); return f; } @@ -165,7 +154,7 @@ void TVMConstructorCompiler::c4ToC7WithMemoryInitAndConstructorProtection() { m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "c4_to_c7_with_init_storage"); + m_pusher.pushFragment(0, 0, "c4_to_c7_with_init_storage"); m_pusher.endContinuationFromRef(); m_pusher._if(); @@ -194,6 +183,7 @@ void TVMContractCompiler::printPrivateFunctionIds( void TVMContractCompiler::generateABI( const std::string& fileName, ContractDefinition const *contract, + std::vector> const& _sourceUnits, std::vector const &pragmaDirectives ) { if (!fileName.empty()) { @@ -201,11 +191,11 @@ void TVMContractCompiler::generateABI( ofile.open(fileName); if (!ofile) fatal_error("Failed to open the output file: " + fileName); - TVMABI::generateABI(contract, pragmaDirectives, &ofile); + TVMABI::generateABI(contract, _sourceUnits, pragmaDirectives, &ofile); ofile.close(); cout << "ABI was generated and saved to file " << fileName << endl; } else { - TVMABI::generateABI(contract, pragmaDirectives); + TVMABI::generateABI(contract, _sourceUnits, pragmaDirectives); } } @@ -234,30 +224,18 @@ TVMContractCompiler::generateContractCode( std::vector> _sourceUnits, PragmaDirectiveHelper const &pragmaHelper ) { - std::vector pragmas; std::vector> functions; TVMCompilerContext ctx{contract, pragmaHelper}; - if (!ctx.isStdlib()) { - pragmas.emplace_back(std::string{} + ".version sol " + solidity::frontend::VersionNumber); - } - - if (pragmaHelper.hasUpgradeFunc()) { - pragmas.emplace_back(".pragma selector-func-solidity"); - } - if (pragmaHelper.hasUpgradeOldSol()) { - pragmas.emplace_back(".pragma selector-old-sol"); - } - fillInlineFunctions(ctx, contract); - // generate global constructor which inlines all contract constructors + // generate global constructor which inlines all contract's constructors if (!ctx.isStdlib()) { StackPusher pusher{&ctx}; TVMConstructorCompiler compiler(pusher); Pointer f = compiler.generateConstructors(); - functions.push_back(f); + functions.emplace_back(f); } for (ContractDefinition const* c : contract->annotation().linearizedBaseContracts) { @@ -268,37 +246,32 @@ TVMContractCompiler::generateContractCode( continue; } - ctx.setCurrentFunction(_function); - if (_function->isOnBounce()) { if (!ctx.isOnBounceGenerated()) { ctx.setIsOnBounce(); - functions.push_back(TVMFunctionCompiler::generateOnBounce(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generateOnBounce(ctx, _function)); } } else if (_function->isReceive()) { if (!ctx.isReceiveGenerated()) { ctx.setIsReceiveGenerated(); - functions.push_back(TVMFunctionCompiler::generateReceive(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generateReceive(ctx, _function)); } } else if (_function->isFallback()) { if (!ctx.isFallBackGenerated()) { ctx.setIsFallBackGenerated(); - functions.push_back(TVMFunctionCompiler::generateFallback(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generateFallback(ctx, _function)); } } else if (_function->isOnTickTock()) { - functions.push_back(TVMFunctionCompiler::generateOnTickTock(ctx, _function)); - } else if (isMacro(_function->name())) { - functions.push_back(TVMFunctionCompiler::generateMacro(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generateOnTickTock(ctx, _function)); } else if (_function->name() == "onCodeUpgrade") { if (!ctx.isBaseFunction(_function)) - functions.push_back(TVMFunctionCompiler::generateOnCodeUpgrade(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generateOnCodeUpgrade(ctx, _function)); } else { - std::string functionName = ctx.getFunctionInternalName(_function); if (!ctx.isStdlib()) { if (_function->isPublic()) { bool isBaseMethod = _function != getContractFunctions(contract, _function->name()).back(); if (!isBaseMethod) { - functions.push_back(TVMFunctionCompiler::generatePublicFunction(ctx, _function)); + functions.emplace_back(TVMFunctionCompiler::generatePublicFunction(ctx, _function)); StackPusher pusher{&ctx}; ChainDataEncoder encoder{&pusher}; // TODO delete pusher @@ -307,15 +280,10 @@ TVMContractCompiler::generateContractCode( ctx.addPublicFunction(functionId, _function->name()); } } - if (_function->visibility() <= Visibility::Public) { - functions.push_back(TVMFunctionCompiler::generatePrivateFunction(ctx, functionName, _function)); - } } - const std::string macroName = functionName + "_macro"; - functions.push_back(TVMFunctionCompiler::generateMacro(ctx, _function, macroName)); + std::string const functionName = ctx.getFunctionInternalName(_function); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, _function, functionName)); } - - ctx.setCurrentFunction(nullptr); } } @@ -324,12 +292,12 @@ TVMContractCompiler::generateContractCode( functions.emplace_back(TVMFunctionCompiler::generateC4ToC7WithInitMemory(ctx)); { StackPusher pusher{&ctx}; - Pointer f = pusher.generateC7ToT4Macro(false); + Pointer f = pusher.generateC7ToC4(false); functions.emplace_back(f); } if (ctx.usage().hasAwaitCall()) { StackPusher pusher{&ctx}; - Pointer f = pusher.generateC7ToT4Macro(true); + Pointer f = pusher.generateC7ToC4(true); functions.emplace_back(f); } functions.emplace_back(TVMFunctionCompiler::updateOnlyTime(ctx)); @@ -362,56 +330,37 @@ TVMContractCompiler::generateContractCode( } } - for (std::shared_ptr source: _sourceUnits) { + // generate library functions + for (std::shared_ptr const& source: _sourceUnits) { for (ASTPointer const &node: source->nodes()) { if (auto lib = dynamic_cast(node.get())) { if (lib->isLibrary()) { for (FunctionDefinition const *function : lib->definedFunctions()) { - ctx.setCurrentFunction(function); - if (!function->modifiers().empty()) { cast_error(*function->modifiers().at(0).get(), "Modifiers for library functions are not supported yet."); } - - if (!function->parameters().empty()) { - { - const std::string name = TVMCompilerContext::getLibFunctionName(function, true); - functions.emplace_back(TVMFunctionCompiler::generateLibraryFunction(ctx, function, name)); - } - { - const std::string name = TVMCompilerContext::getLibFunctionName(function, true) + "_macro"; - functions.emplace_back(TVMFunctionCompiler::generateLibraryFunctionMacro(ctx, function, name)); - } - } + if (!function->parameters().empty()) + functions.emplace_back(TVMFunctionCompiler::generateLibFunctionWithObject(ctx, function)); const std::string name = TVMCompilerContext::getLibFunctionName(function, false); - functions.emplace_back(TVMFunctionCompiler::generatePrivateFunction(ctx, name, function)); - functions.emplace_back(TVMFunctionCompiler::generateMacro(ctx, function, name + "_macro")); - ctx.setCurrentFunction(nullptr); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); } } } } } - for (std::shared_ptr source: _sourceUnits) { + // generate free functions + for (std::shared_ptr const& source: _sourceUnits) { for (ASTPointer const &node: source->nodes()) { if (auto function = dynamic_cast(node.get())) { if (function->isFree() && !function->isInlineAssembly()) { - ctx.setCurrentFunction(function); - if (!function->modifiers().empty()) { cast_error(*function->modifiers().at(0).get(), "Modifiers for free functions are not supported yet."); } - - std::string functionName = ctx.getFunctionInternalName(function); - functions.push_back(TVMFunctionCompiler::generatePrivateFunction(ctx, functionName, function)); - - const std::string macroName = functionName + "_macro"; - functions.push_back(TVMFunctionCompiler::generateMacro(ctx, function, macroName)); - - ctx.setCurrentFunction(nullptr); + const std::string name = ctx.getFunctionInternalName(function); + functions.emplace_back(TVMFunctionCompiler::generateFunction(ctx, function, name)); } } } @@ -430,23 +379,41 @@ TVMContractCompiler::generateContractCode( it = ctx.constArrays().begin(); } - for (const auto&[name, arr] : ctx.newArrays()) { + for (const auto&[name, arr] : ctx.newArrays()) functions.emplace_back(TVMFunctionCompiler::generateNewArrays(ctx, name, arr)); - } - for (const auto&[name, types] : ctx.buildTuple()) { + for (const auto&[name, types] : ctx.buildTuple()) functions.emplace_back(TVMFunctionCompiler::generateBuildTuple(ctx, name, types)); - } - if (!ctx.isStdlib()) { + if (!ctx.isStdlib()) functions.emplace_back(TVMFunctionCompiler::generatePublicFunctionSelector(ctx, contract)); - } - if (ctx.getPragmaSaveAllFunctions()) { - pragmas.emplace_back(".pragma save-all-private-functions"); + + std::map> functionsInMap; + for (const auto& func : functions) { + solAssert(functionsInMap.count(func->name()) == 0, ""); + functionsInMap[func->name()] = func; + } + std::vector> functionOrder; + for (std::string const& funcDef : ctx.callGraph().DAG()) { + if (functionsInMap.count(funcDef) == 0) { + // TODO check stdlib function or inline function + continue; + } + Pointer f = functionsInMap.at(funcDef); + functionOrder.emplace_back(f); + functionsInMap.erase(functionsInMap.find(funcDef)); + } + for (const auto& func : functions) { + if (functionsInMap.count(func->name()) != 0) + functionOrder.emplace_back(func); } - Pointer c = createNode(pragmas, functions); + Pointer c = createNode( + ctx.isStdlib(), ctx.getPragmaSaveAllFunctions(), pragmaHelper.hasUpgradeFunc(), pragmaHelper.hasUpgradeOldSol(), + std::string{"sol "} + solidity::frontend::VersionNumber, + functionOrder, ctx.callGraph().privateFunctions() + ); DeleterAfterRet d; c->accept(d); @@ -508,13 +475,13 @@ void TVMContractCompiler::fillInlineFunctions(TVMCompilerContext &ctx, ContractD std::vector order = inlineFunctionChecker.functionOrder(); for (FunctionDefinition const * function : order) { - ctx.setCurrentFunction(function); + const std::string name = functionName(function); + ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; TVMFunctionCompiler::generateFunctionWithModifiers(pusher, function, true); - const std::string name = functionName(function); - Pointer body = pusher.getBlock(); ctx.addInlineFunction(name, body); + ctx.resetCurrentFunction(); } } diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.hpp b/compiler/libsolidity/codegen/TVMContractCompiler.hpp index 85bd81c1..37d07006 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.hpp @@ -50,6 +50,7 @@ class TVMContractCompiler: private boost::noncopyable { static void generateABI( const std::string& fileName, ContractDefinition const* contract, + std::vector> const& _sourceUnits, std::vector const& pragmaDirectives ); static void generateCodeAndSaveToFile( diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp index f1f21880..1245f0fa 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp @@ -170,7 +170,7 @@ void TVMExpressionCompiler::visitHonest(TupleExpression const& _tupleExpression, for (ASTPointer const& expr : components | boost::adaptors::reversed) { compileNewExpr(expr.get()); - m_pusher.hardConvert(arrayBaseType, expr.get()->annotation().type); + m_pusher.convert(arrayBaseType, expr.get()->annotation().type); } // values... m_pusher.pushInt(0); @@ -215,6 +215,7 @@ bool TVMExpressionCompiler::tryPushConstant(Declaration const* declaration) { return false; } compileNewExpr(variableDeclaration->value().get()); + m_pusher.convert(declaration->type(), variableDeclaration->value()->annotation().type); return true; } @@ -245,7 +246,7 @@ void TVMExpressionCompiler::visit2(Identifier const &_identifier) { // calling this.function() creates an internal message that should be sent to the address of current contract m_pusher << "MYADDR"; } else if (auto funDecl = to(_identifier.annotation().referencedDeclaration)) { - m_pusher.pushPrivateFunctionId(*funDecl); + m_pusher.pushPrivateFunctionId(*funDecl, false); } else { cast_error(_identifier, "Unsupported identifier: " + name); } @@ -282,9 +283,10 @@ void TVMExpressionCompiler::compileUnaryOperation( m_pusher << tvmUnaryOperation; } - if (!isCheckFitUseless(resType, _node.getOperator()) && !m_pusher.ctx().ignoreIntegerOverflow()) { + if (!isCheckFitUseless(resType, _node.getOperator()) && + !m_pusher.ctx().ignoreIntegerOverflow() + ) m_pusher.checkFit(resType); - } collectLValue(lValueInfo, true, false); } @@ -334,8 +336,9 @@ void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { } else if (op == Token::Sub) { compileNewExpr(&_node.subExpression()); m_pusher << "NEGATE"; + if (!m_pusher.ctx().ignoreIntegerOverflow()) + m_pusher.checkFit(getType(&_node.subExpression())); } else if (op == Token::BitNot) { - compileNewExpr(&_node.subExpression()); int numBits = 0; bool isSigned {}; auto type = getType(&_node.subExpression()); @@ -346,11 +349,13 @@ void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { } else { cast_error(_node, "~ operation is supported only for numbers."); } - if (isSigned) + if (isSigned) { + compileNewExpr(&_node.subExpression()); m_pusher << "BITNOT"; - else { - m_pusher << "PUSHPOW2DEC " + to_string(numBits); - m_pusher << "SUBR"; + } else { + m_pusher.pushInt((bigint(1) << numBits) - 1); + compileNewExpr(&_node.subExpression()); + m_pusher << "SUB"; } } else if (op == Token::Delete) { compileUnaryDelete(_node); @@ -390,7 +395,7 @@ void TVMExpressionCompiler::compareSlices(Token op) { } void TVMExpressionCompiler::compareStrings(Token op) { - m_pusher.pushMacroCallInCallRef(2, 1, "compareLongStrings_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "compareLongStrings"); switch(op) { case Token::GreaterThan: m_pusher << "ISPOS"; @@ -443,7 +448,7 @@ void TVMExpressionCompiler::visitBinaryOperationForString( if (op == Token::Add) { pushLeft(); pushRight(); - m_pusher.pushMacroCallInCallRef(2, 1, "concatenateStrings_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "concatenateStrings"); } else if (op == Token::Equal || op == Token::NotEqual) { visitBinaryOperationForTvmCell(pushLeft, pushRight, op); } else if (TokenTraits::isCompareOp(op)){ @@ -500,7 +505,8 @@ void TVMExpressionCompiler::visit2(BinaryOperation const &_binaryOperation) { Type const *rt = getType(&rexp); Type const* &commonType = _binaryOperation.annotation().commonType; Type const* leftTargetType = commonType; - Type const* rightTargetType = TokenTraits::isShiftOp(op) ? rexp.annotation().type->mobileType() : commonType; + Type const* rightTargetType = TokenTraits::isShiftOp(op) || op == Token::Exp ? + rexp.annotation().type->mobileType() : commonType; if (lt->category() == Type::Category::Function || rt->category() == Type::Category::Function) { solUnimplemented("Unsupported binary operation"); @@ -508,12 +514,12 @@ void TVMExpressionCompiler::visit2(BinaryOperation const &_binaryOperation) { auto acceptLeft = [&]() { compileNewExpr(&lexp); - m_pusher.hardConvert(leftTargetType, lt); + m_pusher.convert(leftTargetType, lt); }; auto acceptRight = [&]() { compileNewExpr(&rexp); - m_pusher.hardConvert(rightTargetType, rt); + m_pusher.convert(rightTargetType, rt); }; if (isString(commonType)) { @@ -593,7 +599,7 @@ void TVMExpressionCompiler::visitMathBinaryOperation( m_pusher.dup2(); m_pusher << "OR"; m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::Exponent00)); - m_pusher.pushMacroCallInCallRef(2, 1, "__exp_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__exp"); } checkOverflow = true; } else { @@ -606,7 +612,7 @@ void TVMExpressionCompiler::visitMathBinaryOperation( } else if (op == Token::Mul) { if (commonType->category() == Type::Category::FixedPoint) { int power = to(commonType)->fractionalDigits(); - m_pusher.pushInt(StackPusher::pow10(power)); + m_pusher.pushInt(MathConsts::power10().at(power)); m_pusher << "MULDIV"; } else { m_pusher << "MUL"; @@ -620,7 +626,7 @@ void TVMExpressionCompiler::visitMathBinaryOperation( else if (op == Token::Div) { if (commonType->category() == Type::Category::FixedPoint) { int power = to(commonType)->fractionalDigits(); - m_pusher.pushInt(StackPusher::pow10(power)); // res 10^n + m_pusher.pushInt(MathConsts::power10().at(power)); // res 10^n m_pusher.exchange(1); m_pusher << "MULDIV"; } else { @@ -646,11 +652,11 @@ void TVMExpressionCompiler::visitMathBinaryOperation( } } - if (checkOverflow && !m_pusher.ctx().ignoreIntegerOverflow()) { - if (!isCheckFitUseless(commonType, op)) { - m_pusher.checkFit(commonType); - } - } + if (checkOverflow && + !isCheckFitUseless(commonType, op) && + !m_pusher.ctx().ignoreIntegerOverflow() + ) + m_pusher.checkFit(commonType); } void TVMExpressionCompiler::visitMsgMagic(MemberAccess const &_node) { @@ -680,9 +686,9 @@ void TVMExpressionCompiler::visitMsgMagic(MemberAccess const &_node) { "PICK", }, 0, 1, true)); } else if (_node.memberName() == "forwardFee") { // msg.forwardFee - m_pusher.pushMacro(0, 1, "__forwardFee_macro"); + m_pusher.pushFragment(0, 1, "__forwardFee"); } else if (_node.memberName() == "importFee") { // msg.importFee - m_pusher.pushMacro(0, 1, "__importFee_macro"); + m_pusher.pushFragment(0, 1, "__importFee"); } else if (isIn(_node.memberName(), "isInternal", "isExternal", "isTickTock")) { m_pusher.push(createNode(std::vector{ "DEPTH", @@ -840,8 +846,8 @@ void TVMExpressionCompiler::visitMagic(MemberAccess const &_memberAccess) { const Type *argType = arg->typeArgument(); if (auto const* integerType = dynamic_cast(argType)) opcode += toString(member == "min" ? integerType->minValue() : integerType->maxValue()); - else if (auto const* varInt = dynamic_cast(argType)) - opcode += toString(member == "min" ? varInt->minValue() : varInt->maxValue()); + else if (auto const* varInt = dynamic_cast(argType)) + opcode += toString(member == "min" ? varInt->asIntegerType().minValue() : varInt->asIntegerType().maxValue()); else if (auto const* enumType = dynamic_cast(argType)) opcode += toString(member == "min" ? enumType->minValue() : enumType->maxValue()); else @@ -897,7 +903,7 @@ void TVMExpressionCompiler::visit2(MemberAccess const &_node) { auto funType = to(_node.annotation().type); if (funType) { auto funDef = to(&funType->declaration()); - m_pusher.pushPrivateFunctionId(*funDef); + m_pusher.pushPrivateFunctionId(*funDef, false); return ; } } @@ -974,7 +980,7 @@ void TVMExpressionCompiler::visitMemberAccessFixedBytes(MemberAccess const &_nod 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::ExtraCurrencyCollection, Type::Category::TvmVector) && !isUsualArray(baseExprType)) { + 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"); } } @@ -999,7 +1005,7 @@ void TVMExpressionCompiler::visit2(IndexRangeAccess const &indexRangeAccess) { m_pusher << "TRUE"; } - m_pusher.pushMacroCallInCallRef(4, 1, "bytes_substr_macro"); + m_pusher.pushFragmentInCallRef(4, 1, "bytes_substr"); return; } } @@ -1072,9 +1078,8 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { } m_pusher.getDict(*StackPusher::parseIndexType(baseType), - *StackPusher::parseValueType(indexAccess), - baseType->category() == Type::Category::Mapping || - baseType->category() == Type::Category::ExtraCurrencyCollection ? + *indexAccess.annotation().type, + baseType->category() == Type::Category::Mapping ? GetDictOperation::GetFromMapping : GetDictOperation::GetFromArray); } @@ -1164,7 +1169,7 @@ TVMExpressionCompiler::expandLValue( m_pusher.getGlob(vd); } } else if (auto index = to(lValueInfo.expressions[i])) { - if (isIn(index->baseExpression().annotation().type->category(), Type::Category::Mapping, Type::Category::ExtraCurrencyCollection)) { + if (index->baseExpression().annotation().type->category() == Type::Category::Mapping) { // dict1 pushIndexAndConvert(*index); // dict1 index m_pusher.prepareKeyForDictOperations(index->indexExpression()->annotation().type, false); @@ -1176,7 +1181,7 @@ TVMExpressionCompiler::expandLValue( // index dict1 index dict1 m_pusher.getDict(*StackPusher::parseIndexType(index->baseExpression().annotation().type), - *StackPusher::parseValueType(*index), + *index->annotation().type, GetDictOperation::GetFromMapping); // index dict1 dict2 } else if (index->baseExpression().annotation().type->category() == Type::Category::Array) { @@ -1245,15 +1250,14 @@ TVMExpressionCompiler::collectLValue( m_pusher.setGlob(vd); } } else if (auto indexAccess = to(lValueInfo.expressions[i])) { - if (isIn(indexAccess->baseExpression().annotation().type->category(), Type::Category::Mapping, Type::Category::ExtraCurrencyCollection)) { - // pushLog("colMapIndex"); + if (indexAccess->baseExpression().annotation().type->category() == Type::Category::Mapping) { if (isLast && !haveValueOnStackTop) { // index dict m_pusher.dropUnder(1, 1); // dict } else { // index dict value Type const* keyType = StackPusher::parseIndexType(indexAccess->baseExpression().annotation().type); - Type const* valueDictType = StackPusher::parseValueType(*indexAccess); + Type const* valueDictType = indexAccess->annotation().type; const DataType& dataType = m_pusher.prepareValueForDictOperations(keyType, valueDictType); m_pusher.rotRev(); // value index dict m_pusher.setDict(*keyType, *valueDictType, dataType); // dict' @@ -1300,7 +1304,7 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { if (op == Token::Assign) { auto push_rhs = [&] () { compileNewExpr(&rhs); - m_pusher.hardConvert(getType(&lhs), getType(&rhs)); + m_pusher.convert(getType(&lhs), getType(&rhs)); return false; }; const int saveStackSize0 = m_pusher.stackSize(); @@ -1317,15 +1321,15 @@ bool TVMExpressionCompiler::tryAssignLValue(Assignment const &_assignment) { } else { Type const*& commonType = lhs.annotation().type; compileNewExpr(&rhs); // r - m_pusher.hardConvert(commonType, rhs.annotation().type); + m_pusher.convert(commonType, rhs.annotation().type); const int saveStackSize = m_pusher.stackSize(); const LValueInfo lValueInfo = expandLValue(&lhs, true); // r expanded... l - m_pusher.hardConvert(commonType, lhs.annotation().type); + m_pusher.convert(commonType, lhs.annotation().type); const int expandedLValueSize = m_pusher.stackSize() - saveStackSize - 1; m_pusher.blockSwap(1, expandedLValueSize + 1); // expanded... l r if (isString(commonType)) { - m_pusher.pushMacroCallInCallRef(2, 1, "concatenateStrings_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "concatenateStrings"); } else { visitMathBinaryOperation(binOp, commonType, nullptr, nullopt); } @@ -1359,10 +1363,10 @@ bool TVMExpressionCompiler::tryAssignTuple(Assignment const &_assignment) { m_pusher.drop(); } else { if (auto rightType = to(rhs.annotation().type)) { - m_pusher.hardConvert(leftComp->annotation().type, rightType->components().at(i)); + m_pusher.convert(leftComp->annotation().type, rightType->components().at(i)); } else { solAssert(lhs->components().size() == 1, ""); - m_pusher.hardConvert(leftComp->annotation().type, rhs.annotation().type); + m_pusher.convert(leftComp->annotation().type, rhs.annotation().type); } const int stackSizeForValue = m_pusher.stackSize(); const LValueInfo lValueInfo = expandLValue(leftComp.get(), false); @@ -1407,7 +1411,7 @@ void TVMExpressionCompiler::pushIndexAndConvert(IndexAccess const& indexAccess) compileNewExpr(arg); // index const auto&[keyT, valT] = dictKeyValue(indexAccess.baseExpression().annotation().type); boost::ignore_unused(valT); - m_pusher.hardConvert(keyT, arg->annotation().type); + m_pusher.convert(keyT, arg->annotation().type); } diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.cpp b/compiler/libsolidity/codegen/TVMFunctionCall.cpp index 2f6779fe..ad26d41e 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.cpp @@ -90,7 +90,7 @@ void FunctionCallCompiler::compile() { } else if (*m_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { structConstructorCall(); } else if (*m_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { - if (m_arguments.empty()) { + if (m_arguments.empty()) { // TODO separate to another kind FunctionCallKind::some-type createObject(); } else { typeConversion(); @@ -147,7 +147,7 @@ void FunctionCallCompiler::compile() { superFunctionCall(*_memberAccess); } else if (category == Type::Category::TypeType) { Type const* actualType = to(_memberAccess->expression().annotation().type)->actualType(); - if (checkBaseContractCall(*_memberAccess, category)) { + if (checkBaseContractCall(*_memberAccess)) { // nothing } else if (to(actualType)) { userDefinedValueMethods(*_memberAccess); @@ -179,7 +179,7 @@ void FunctionCallCompiler::arrayPush(StackPusher& pusher, Type const* arrayBaseT bool FunctionCallCompiler::checkForMappingOrCurrenciesMethods() { auto expr = &m_functionCall.expression(); auto ma = to(expr); - if (ma == nullptr || (!to(ma->expression().annotation().type) && !to(ma->expression().annotation().type))) + if (ma == nullptr || !to(ma->expression().annotation().type)) return false; const ASTString &memberName = ma->memberName(); @@ -398,13 +398,13 @@ void FunctionCallCompiler::superFunctionCall(MemberAccess const &_node) { pushArgs(); auto someFunDecl = to(_node.annotation().referencedDeclaration); FunctionDefinition const* superFunc = getSuperFunction( - m_pusher.ctx().getCurrentFunction()->annotation().contract, + m_pusher.ctx().currentFunction()->annotation().contract, m_pusher.ctx().getContract(), someFunDecl->externalIdentifierHex() ); solAssert(superFunc, ""); std::string functionName = m_pusher.ctx().getFunctionInternalName(superFunc, true); - m_pusher.pushCallOrCallRef(functionName, m_funcType); + m_pusher.pushCallOrCallRef(superFunc, std::nullopt, true); } void FunctionCallCompiler::userDefinedValueMethods(MemberAccess const &_memberAccess) { @@ -467,11 +467,14 @@ bool FunctionCallCompiler::libraryCall(MemberAccess const& ma) { auto t = getType(&ma.expression()); const int argQty = static_cast(m_arguments.size()); const int retQty = static_cast(libFunction->returnParameters().size()); - FunctionType const* funType = libFunction->functionType(!libFunction->isPublic()); if (t->category() == Type::Category::TypeType) { // uint z = MyLib.sum(a, b); pushArgs(); - m_pusher.pushCallOrCallRef(TVMCompilerContext::getLibFunctionName(libFunction, false), funType); + m_pusher.pushCallOrCallRef( + libFunction, + std::nullopt, + false + ); } else { // using MathLib for uint; // a.add(b); @@ -482,9 +485,9 @@ bool FunctionCallCompiler::libraryCall(MemberAccess const& ma) { pushArgs(); m_pusher.pushCallOrCallRef( - TVMCompilerContext::getLibFunctionName(libFunction, true), - funType, - std::make_pair(argQty + 1, retQty + 1) + libFunction, + std::make_pair(argQty + 1, retQty + 1), + true ); m_pusher.blockSwap(lValueQty, retQty); @@ -687,7 +690,7 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa // stack: remote addr m_pusher.startOpaque(); m_pusher.startContinuation(); - m_pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4_for_await"); + m_pusher.pushFragmentInCallRef(0, 0, "c7_to_c4_for_await"); m_pusher.push(createNode(std::vector{ "DEPTH", "ADDCONST -5; sys vars", @@ -1051,7 +1054,7 @@ void FunctionCallCompiler::tvmBuildIntMsg() { for (int idx = static_cast(args.size()) - 1; shift <= idx; --idx) { ASTPointer const &arg = args.at(idx); acceptExpr(arg.get()); - m_pusher.hardConvert(functionDefinition->parameters().at(idx - shift)->type(), getType(arg.get())); + m_pusher.convert(functionDefinition->parameters().at(idx - shift)->type(), getType(arg.get())); } std::set isParamOnStack; @@ -1335,13 +1338,13 @@ bool FunctionCallCompiler::checkForTvmDeployMethods(MemberAccess const &_node, T if (_node.memberName() == "insertPubkey") { pushArgs(); - m_pusher.pushMacroCallInCallRef(2, 1, "insert_pubkey_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "insert_pubkey"); return true; } if (_node.memberName() == "stateInitHash") { pushArgs(); - m_pusher.pushMacroCallInCallRef(4, 1, "stateInitHash_macro"); + m_pusher.pushFragmentInCallRef(4, 1, "stateInitHash"); return true; } @@ -1778,7 +1781,7 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // if last tuple is full (his length is equal to 255) push it back to vector and create new tuple m_pusher.startContinuation(); m_pusher << "TPUSH"; - m_pusher.tuple(0); + m_pusher.makeTuple(0); m_pusher.endContinuation(); m_pusher.ifNot(); // new_el vector' tuple @@ -1786,7 +1789,7 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // if vector is empty create new tuple m_pusher.startContinuation(); - m_pusher.tuple(0); + m_pusher.makeTuple(0); m_pusher.endContinuation(); m_pusher.ifElse(); @@ -2044,6 +2047,10 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { } else if (memberName == "toCell") { acceptExpr(&_node.expression()); m_pusher << "ENDC"; + } else if (memberName == "toExoticCell") { + acceptExpr(&_node.expression()); + m_pusher << "TRUE"; + m_pusher << "ENDXC"; } else if (memberName == "toSlice") { acceptExpr(&_node.expression()); m_pusher << "ENDC"; @@ -2071,28 +2078,29 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { acceptExpr(&_node.expression()); pushArgs(); if (m_arguments.size() == 1) { - m_pusher.pushInt(-1); + m_pusher.pushFragmentInCallRef(2, 1, "__substr_from"); + } else { + m_pusher.pushFragmentInCallRef(3, 1, "__substr_from_count"); } - m_pusher.pushMacroCallInCallRef(3, 1, "__substr_macro"); } else if (_node.memberName() == "find") { acceptExpr(&_node.expression()); pushArgs(); Type::Category cat = m_arguments.at(0)->annotation().type->category(); if (cat == Type::Category::FixedBytes) { - m_pusher.pushMacroCallInCallRef(2, 1, "__strchr_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__strchr"); } else { - m_pusher.pushMacroCallInCallRef(2, 1, "__strstr_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__strstr"); } } else if (_node.memberName() == "findLast") { acceptExpr(&_node.expression()); pushArgs(); - m_pusher.pushMacroCallInCallRef(2, 1, "__strrchr_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__strrchr"); } else if (_node.memberName() == "toLowerCase") { acceptExpr(&_node.expression()); - m_pusher.pushMacroCallInCallRef(1, 1, "__toLowerCase_macro"); + m_pusher.pushFragmentInCallRef(1, 1, "__toLowerCase"); } else if (_node.memberName() == "toUpperCase") { acceptExpr(&_node.expression()); - m_pusher.pushMacroCallInCallRef(1, 1, "__toUpperCase_macro"); + m_pusher.pushFragmentInCallRef(1, 1, "__toUpperCase"); } else if (_node.memberName() == "byteLength") { acceptExpr(&_node.expression()); m_pusher.byteLengthOfCell(); @@ -2139,7 +2147,7 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { } else if (_node.memberName() == "append") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); pushArgAndConvert(0); - m_pusher.pushMacroCallInCallRef(2, 1, "concatenateStrings_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "concatenateStrings"); m_exprCompiler.collectLValue(lValueInfo, true, false); } else { solUnimplemented(""); @@ -2212,7 +2220,7 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { } const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), false); pushArgs(false, false); - m_pusher.hardConvert(getType(&_node.expression()), rightType); + m_pusher.convert(getType(&_node.expression()), rightType); m_exprCompiler.collectLValue(lValueInfo, true, false); } else if (memberName == "reset") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), false); @@ -2225,23 +2233,26 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { } void FunctionCallCompiler::cellMethods(MemberAccess const &_node) { - if (_node.memberName() == "toSlice") { - acceptExpr(&_node.expression()); + ASTString const& memberName = _node.memberName(); + acceptExpr(&_node.expression()); + if (memberName == "toSlice") m_pusher << "CTOS"; - } else if (_node.memberName() == "depth") { - acceptExpr(&_node.expression()); + else if (memberName == "exoticToSlice") + m_pusher << "XCTOS"; + else if (memberName == "loadExoticCell") + m_pusher << "XLOAD"; + else if (memberName == "loadExoticCellQ") + m_pusher << "XLOADQ"; + else if (memberName == "depth") m_pusher << "CDEPTH"; - } else if (_node.memberName() == "dataSize") { - acceptExpr(&_node.expression()); + else if (memberName == "dataSize") { pushArgAndConvert(0); m_pusher << "CDATASIZE"; - } else if (_node.memberName() == "dataSizeQ") { - acceptExpr(&_node.expression()); + } else if (memberName == "dataSizeQ") { pushArgAndConvert(0); cellBitRefQty(); - } else { + } else solUnimplemented(""); - } } void FunctionCallCompiler::variantMethods(MemberAccess const& _node) { @@ -2774,7 +2785,7 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { } else if (_node.memberName() == "rawCommit") { // tvm.rawCommit m_pusher << "COMMIT"; } else if (_node.memberName() == "commit") { // tvm.commit - m_pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); + m_pusher.pushFragmentInCallRef(0, 0, "c7_to_c4"); m_pusher << "COMMIT"; } else if (_node.memberName() == "log") { // tvm.log compileLog(); @@ -2833,7 +2844,7 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "c7_to_c4"); + m_pusher.pushFragment(0, 0, "c7_to_c4"); m_pusher.endContinuationFromRef(); m_pusher.ifNot(); @@ -3061,7 +3072,7 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { pushArgs(); if (m_retType->category() == Type::Category::FixedPoint) { int power = to(m_retType)->fractionalDigits(); - m_pusher.pushInt(StackPusher::pow10(power)); // res 10^n + m_pusher.pushInt(MathConsts::power10().at(power)); // res 10^n m_pusher.exchange(1); m_pusher << "MUL" + boost::to_upper_copy(_node.memberName()); } else { @@ -3110,15 +3121,12 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { } } -bool FunctionCallCompiler::checkBaseContractCall(MemberAccess const &_node, Type::Category category) { - if (category != Type::Category::TypeType) - return false; - if (to(&_node.expression()) && m_funcType->hasDeclaration()) { +bool FunctionCallCompiler::checkBaseContractCall(MemberAccess const &_node) { + auto funDef = to(_node.annotation().referencedDeclaration); + if (funDef) { // calling base contract method pushArgs(); - auto fd = to(&m_funcType->declaration()); - const std::string functionName = m_pusher.ctx().getFunctionInternalName(fd); - m_pusher.pushCallOrCallRef(functionName, m_funcType); + m_pusher.pushCallOrCallRef(funDef, std::nullopt, true); return true; } return false; @@ -3146,76 +3154,31 @@ void FunctionCallCompiler::createObject() { } void FunctionCallCompiler::typeConversion() { - Type::Category argCategory = m_arguments[0]->annotation().type->category(); + solAssert(m_arguments.size() == 1, ""); Type const* argType = m_arguments[0]->annotation().type; Type const* resultType = m_functionCall.annotation().type; - solAssert(m_arguments.size() == 1, ""); - auto conversionToAddress = [&](){ - switch (argCategory) { - case Type::Category::Contract: - case Type::Category::Address: - acceptExpr(m_arguments.at(0).get()); // it's correct - break; - case Type::Category::RationalNumber: - case Type::Category::Integer: { - auto literal = to(m_arguments[0].get()); - if (literal) { - m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(StrUtils::literalToSliceAddress(literal))); - } else { - acceptExpr(m_arguments.at(0).get()); // it's correct - m_pusher << "NEWC"; - m_pusher << "STSLICECONST x801_"; // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 // 10 0 00000000 1 = 801 - m_pusher << "STU 256"; // address:bits256 - m_pusher << "ENDC"; - m_pusher << "CTOS"; + if (auto funCall = to(m_arguments[0].get())) { + if (*funCall->annotation().kind == FunctionCallKind::TypeConversion && funCall->arguments().size() == 1) { + // c(b(a)), e.g. uint8 x; int(uint(x)); + auto a = to(funCall->arguments().at(0)->annotation().type); + auto b = to(funCall->annotation().type); + auto c = to(resultType); + if (a && b && c) { + if (!a->isSigned() && !b->isSigned() && c->isSigned() && + a->numBits() < b->numBits() && b->numBits() == c->numBits() + ) { + acceptExpr(funCall->arguments().at(0).get()); + // no conversion + return; } - break; - } - default: - solUnimplemented(""); - } - }; - - if (auto etn = to(&m_functionCall.expression())) { - switch (etn->type().typeName().token()) { - case Token::Address: { - conversionToAddress(); - return; } - default: - break; - } - } else if (auto identifier = to(&m_functionCall.expression())) { - if (to(identifier->annotation().referencedDeclaration)) { - conversionToAddress(); - return; - } else if (auto enumDef = to(identifier->annotation().referencedDeclaration)) { - const auto& value = ExprUtils::constValue(*m_arguments[0]); - if (value.has_value()) { - m_pusher << "PUSHINT " + value->str(); - return; - } - - acceptExpr(m_arguments[0].get()); // it's correct - m_pusher.pushS(0); - m_pusher.pushInt(enumDef->members().size()); - m_pusher << "GEQ"; - m_pusher._throw("THROWIF " + toString(TvmConst::RuntimeException::WrongValueOfEnum)); - - auto type = m_arguments[0].get()->annotation().type; - TypeInfo ti(type); - if (!ti.isNumeric || ti.isSigned) { - m_pusher << "UFITS 256"; // checks whether value >= 0 - } - - return; } } solAssert(m_arguments.size() == 1, ""); - acceptExpr(m_arguments.at(0).get()); // it's correct - m_pusher.hardConvert(resultType, argType); + acceptExpr(m_arguments.at(0).get()); + m_pusher.convert(resultType, argType); } bool FunctionCallCompiler::checkLocalFunctionOrLibCall(const Identifier *identifier) { @@ -3225,15 +3188,13 @@ bool FunctionCallCompiler::checkLocalFunctionOrLibCall(const Identifier *identif return false; pushArgs(); if (functionDefinition->isInline()) { - Pointer body = m_pusher.ctx().getInlinedFunction(functionName); int take = m_funcType->parameterTypes().size(); int ret = m_funcType->returnParameterTypes().size(); - m_pusher.pushInlineFunction(body, take, ret); + m_pusher.pushInlineFunction(functionName, take, ret); } else { ContractDefinition const* contractDecl = functionDefinition->annotation().contract; if (contractDecl && contractDecl->isLibrary()) { - std::string name = TVMCompilerContext::getLibFunctionName(functionDefinition, false); - m_pusher.pushCallOrCallRef(name, m_funcType); + m_pusher.pushCallOrCallRef(functionDefinition, std::nullopt, false); } else if (functionDefinition->isInlineAssembly()) { int take = functionDefinition->parameters().size(); int ret = functionDefinition->returnParameters().size(); @@ -3247,8 +3208,7 @@ bool FunctionCallCompiler::checkLocalFunctionOrLibCall(const Identifier *identif } m_pusher.push(createNode(lines, take, ret, false)); } else { - std::string name = m_pusher.ctx().getFunctionInternalName(functionDefinition, false); - m_pusher.pushCallOrCallRef(name, m_funcType); + m_pusher.pushCallOrCallRef(functionDefinition, std::nullopt, false); } } return true; @@ -3265,7 +3225,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { if (m_arguments.size() == 1) { m_pusher << "GASTOGRAM"; } else { - m_pusher.pushMacroCallInCallRef(2, 1, "__gasToTon_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__gasToTon"); } return true; } @@ -3274,7 +3234,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { if (m_arguments.size() == 1) { m_pusher << "GRAMTOGAS"; } else { - m_pusher.pushMacroCallInCallRef(2, 1, "__tonToGas_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "__tonToGas"); } return true; } @@ -3320,29 +3280,37 @@ bool FunctionCallCompiler::checkSolidityUnits() { pushArgAndConvert(0); m_pusher._throw("THROWIFNOT 100"); } else if (m_arguments.size() == 2 || m_arguments.size() == 3) { - if (m_arguments.size() == 3) - pushArgAndConvert(2); - const auto &exceptionCode = ExprUtils::constValue(*m_arguments[1].get()); - if (exceptionCode.has_value() && exceptionCode.value() <= 1) { - cast_error(*m_arguments[1].get(), "Error code must be at least two"); - } - if (exceptionCode.has_value() && exceptionCode.value() < 2048) { - pushArgAndConvert(0); - if (m_arguments.size() == 3) - m_pusher._throw("THROWARGIFNOT " + toString(exceptionCode.value())); - else - m_pusher._throw("THROWIFNOT " + toString(exceptionCode.value())); - } else { + Type const* type1 = m_arguments.at(1)->annotation().type; + auto arr = dynamic_cast(type1); + if (dynamic_cast(type1) || (arr && arr->isString())){ pushArgAndConvert(1); - if (!exceptionCode.has_value()) { - m_pusher.pushInt(2); - m_pusher << "MAX"; - } pushArgAndConvert(0); + m_pusher._throw("THROWARGIFNOT 100"); + } else { if (m_arguments.size() == 3) - m_pusher._throw("THROWARGANYIFNOT"); - else - m_pusher._throw("THROWANYIFNOT"); + pushArgAndConvert(2); + const auto &exceptionCode = ExprUtils::constValue(*m_arguments[1].get()); + if (exceptionCode.has_value() && exceptionCode.value() <= 1) { + cast_error(*m_arguments[1].get(), "Error code must be at least two"); + } + if (exceptionCode.has_value() && exceptionCode.value() < 2048) { + pushArgAndConvert(0); + if (m_arguments.size() == 3) + m_pusher._throw("THROWARGIFNOT " + toString(exceptionCode.value())); + else + m_pusher._throw("THROWIFNOT " + toString(exceptionCode.value())); + } else { + pushArgAndConvert(1); + if (!exceptionCode.has_value()) { + m_pusher.pushInt(2); + m_pusher << "MAX"; + } + pushArgAndConvert(0); + if (m_arguments.size() == 3) + m_pusher._throw("THROWARGANYIFNOT"); + else + m_pusher._throw("THROWANYIFNOT"); + } } } else { cast_error(m_functionCall, R"("require" takes from one to three m_arguments.)"); @@ -3414,7 +3382,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { for(size_t i = 0; i < constStr.length(); i += maxSlice) { m_pusher.pushString(constStr.substr(i, min(maxSlice, constStr.length() - i)), true); // stack: BldrList builder Slice - m_pusher.pushMacroCallInCallRef(3, 2, "storeStringInBuilders_macro"); + m_pusher.pushFragmentInCallRef(3, 2, "storeStringInBuilders"); } // stack: BldrList builder } @@ -3462,21 +3430,21 @@ bool FunctionCallCompiler::checkSolidityUnits() { } // stack: vector(TvmBuilder) builder abs(number) width leadingZeroes addMinus if (isHex) { - m_pusher.pushMacroCallInCallRef(7, 2, "convertIntToHexStr_macro"); + m_pusher.pushFragmentInCallRef(7, 2, "convertIntToHexStr"); } else { - m_pusher.pushMacroCallInCallRef(6, 2, "convertIntToDecStr_macro"); + m_pusher.pushFragmentInCallRef(6, 2, "convertIntToDecStr"); } // stack: vector(TvmBuilder) builder } else { acceptExpr(m_arguments[it + 1].get()); m_pusher.pushInt(9); - m_pusher.pushMacroCallInCallRef(4, 2, "convertFixedPointToString_macro"); + m_pusher.pushFragmentInCallRef(4, 2, "convertFixedPointToString"); } } else if (cat == Type::Category::Address) { // stack: vector(TvmBuilder) builder acceptExpr(m_arguments[it + 1].get()); // stack: vector(TvmBuilder) builder address - m_pusher.pushMacroCallInCallRef(3, 2, "convertAddressToHexString_macro"); + m_pusher.pushFragmentInCallRef(3, 2, "convertAddressToHexString"); // stack: vector(TvmBuilder) builder } else if (isStringOrStringLiteralOrBytes(argType)) { // stack: vector(TvmBuilder) builder @@ -3484,25 +3452,25 @@ bool FunctionCallCompiler::checkSolidityUnits() { // stack: vector(TvmBuilder) builder string(cell) m_pusher << "CTOS"; // stack: vector(TvmBuilder) builder string(slice) - m_pusher.pushMacroCallInCallRef(3, 2, "storeStringInBuilders_macro"); + m_pusher.pushFragmentInCallRef(3, 2, "storeStringInBuilders"); // stack: vector(TvmBuilder) builder } else if (cat == Type::Category::FixedPoint) { int power = to(argType)->fractionalDigits(); acceptExpr(m_arguments[it + 1].get()); m_pusher.pushInt(power); - m_pusher.pushMacroCallInCallRef(4, 2, "convertFixedPointToString_macro"); + m_pusher.pushFragmentInCallRef(4, 2, "convertFixedPointToString"); } else { cast_error(*m_arguments[it + 1].get(), "Unsupported argument type"); } } pushConstStr(formatStr); - m_pusher.pushMacroCallInCallRef(2, 1, "assembleList_macro"); + m_pusher.pushFragmentInCallRef(2, 1, "assembleList"); return true; } case FunctionType::Kind::Stoi: { pushArgAndConvert(0); - m_pusher.pushMacroCallInCallRef(1, 1, "__stoi_macro"); + m_pusher.pushFragmentInCallRef(1, 1, "__stoi"); return true; } default: @@ -3919,7 +3887,6 @@ void FunctionCallCompiler::buildStateInit(std::map &e, int i) { acceptExpr(e.get()); @@ -3930,7 +3897,7 @@ void FunctionCallCompiler::pushArgs(bool reversed, bool doConvert) { } else { targetType = m_funcType->parameterTypes().at(i); } - m_pusher.hardConvert(targetType, e->annotation().type); + m_pusher.convert(targetType, e->annotation().type); } }; @@ -3969,12 +3936,12 @@ void FunctionCallCompiler::pushArgAndConvert(int index, const std::string& name) targetType = m_functionCall.annotation().arguments->targetTypes.at(index); } - m_pusher.hardConvert(targetType, arg->annotation().type); + m_pusher.convert(targetType, arg->annotation().type); } void FunctionCallCompiler::pushExprAndConvert(const Expression *expr, Type const* targetType) { acceptExpr(expr); - m_pusher.hardConvert(targetType, expr->annotation().type); + m_pusher.convert(targetType, expr->annotation().type); } void FunctionCallCompiler::acceptExpr(const Expression *expr) { @@ -3986,7 +3953,7 @@ void FunctionCallCompiler::compileLog() auto logstr = m_arguments[0].get(); auto literal = to(logstr); if (literal && literal->value().size() < 16) { - std::string hexStr = stringToHex(literal->value()); + std::string hexStr = StrUtils::stringToHex(literal->value()); m_pusher << "PRINTSTR x" + hexStr; } else { pushArgs(); diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.hpp b/compiler/libsolidity/codegen/TVMFunctionCall.hpp index e8373776..258789ff 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.hpp @@ -67,7 +67,7 @@ class FunctionCallCompiler { bool checkForTvmFunction(MemberAccess const& _node); void abiFunction(); void mathFunction(MemberAccess const& _node); - bool checkBaseContractCall(MemberAccess const& _node, Type::Category category); + bool checkBaseContractCall(MemberAccess const& _node); bool checkAddressThis(); void createObject(); void typeConversion(); diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp index 2883f3b5..dc8fd07f 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp @@ -139,7 +139,7 @@ Pointer TVMFunctionCompiler::updateOnlyTime(TVMCompilerContext& ctx) { pusher.popRoot(); Pointer block = pusher.getBlock(); - auto f = createNode(0, 0, "upd_only_time_in_c4", Function::FunctionType::Macro, block); + auto f = createNode(0, 0, "upd_only_time_in_c4", nullopt, Function::FunctionType::Fragment, block); return f; } @@ -174,7 +174,7 @@ TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { pusher.getGlob(i); } pusher.blockSwap(varQty, TvmConst::C7::FirstIndexForVariables); - pusher.tuple(varQty + TvmConst::C7::FirstIndexForVariables); + pusher.makeTuple(varQty + TvmConst::C7::FirstIndexForVariables); pusher.popC7(); } else { for (int i = varQty - 1; i >= 0; --i) { @@ -194,12 +194,14 @@ TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { pusher.setGlob(TvmConst::C7::TvmPubkey); Pointer block = pusher.getBlock(); - auto f = createNode(0, 0, "c4_to_c7", Function::FunctionType::Macro, block); + auto f = createNode(0, 0, "c4_to_c7", nullopt, Function::FunctionType::Fragment, block); return f; } Pointer TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { + std::string const name = "c4_to_c7_with_init_storage"; + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, pusher.ctx().getContract()}; @@ -209,7 +211,7 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { pusher << "GTINT 1"; pusher.startContinuation(); - pusher.pushMacro(0, 0, "c4_to_c7"); + pusher.pushFragment(0, 0, "c4_to_c7"); pusher.endContinuationFromRef(); pusher.startContinuation(); @@ -238,7 +240,7 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { ++varQty; } if (tooMuchStateVars) { - pusher.tuple(varQty); + pusher.makeTuple(varQty); pusher.popC7(); } else { auto x = pusher.ctx().notConstantStateVariables(); // move @@ -267,8 +269,8 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { pusher.endContinuation(); pusher.ifElse(); - auto f = createNode(0, 0, "c4_to_c7_with_init_storage", Function::FunctionType::Macro, pusher.getBlock()); - return f; + ctx.resetCurrentFunction(); + return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } Pointer @@ -279,62 +281,72 @@ TVMFunctionCompiler::generateBuildTuple(TVMCompilerContext& ctx, std::string con for (Type const* t : types) { pusher.pushDefaultValue(t); } - pusher.tuple(n); + pusher.makeTuple(n); StructCompiler sc{&pusher, types, names}; sc.tupleToBuilder(); pusher << "ENDC"; - return createNode(0, 0, name, Function::FunctionType::Macro, pusher.getBlock()); + return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } Pointer TVMFunctionCompiler::generateNewArrays(TVMCompilerContext& ctx, std::string const& name, FunctionCall const* arr) { + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; FunctionCallCompiler{pusher, *arr, true}.honestArrayCreation(true); - return createNode(0, 0, name, Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } Pointer TVMFunctionCompiler::generateConstArrays(TVMCompilerContext& ctx, std::string const& name, TupleExpression const* arr) { + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; solAssert(arr->isInlineArray(), ""); TVMExpressionCompiler{pusher}.visitHonest(*arr, true); - return createNode(0, 0, name, Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } Pointer -TVMFunctionCompiler::generateMacro( +TVMFunctionCompiler::generateFunction( TVMCompilerContext& ctx, FunctionDefinition const* function, - const std::optional& forceName + std::string const& name ) { + ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; - std::string name = forceName.has_value() ? forceName.value() : function->name(); TVMFunctionCompiler funCompiler{pusher, 0, function, false, true, 0}; funCompiler.pushLocation(*function); funCompiler.visitFunctionWithModifiers(); funCompiler.pushLocation(*function, true); int take = function->parameters().size(); int ret = function->returnParameters().size(); - return createNode(take, ret, name, Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + uint32_t id = ChainDataEncoder::toHash256(name); + return createNode(take, ret, name, id, Function::FunctionType::Fragment, pusher.getBlock(), function); } Pointer TVMFunctionCompiler::generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefinition const* function) { + const std::string name = ctx.getFunctionInternalName(function, false); + ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, 0, function, false, true, 0}; funCompiler.visitFunctionWithModifiers(); - pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); + pusher.pushFragmentInCallRef(0, 0, "c7_to_c4"); pusher << "COMMIT"; pusher._throw("THROW 0"); int take = function->parameters().size(); - const std::string name = ctx.getFunctionInternalName(function, false); - return createNode(take, 0, name, Function::FunctionType::OnCodeUpgrade, + ctx.resetCurrentFunction(); + uint32_t id = function->functionID() ? function->functionID().value() : ChainDataEncoder::toHash256(name); + return createNode(take, 0, name, id, Function::FunctionType::OnCodeUpgrade, pusher.getBlock(), function); } Pointer TVMFunctionCompiler::generateOnTickTock(TVMCompilerContext& ctx, FunctionDefinition const* function) { + ctx.setCurrentFunction(function, "onTickTock"); StackPusher pusher{&ctx}; pusher.startOpaque(); pusher.pushInt(-2); @@ -347,7 +359,7 @@ TVMFunctionCompiler::generateOnTickTock(TVMCompilerContext& ctx, FunctionDefinit bool isPure = function->stateMutability() == StateMutability::Pure; if (!isPure) { - pusher.pushMacroCallInCallRef(0, 0, "c4_to_c7"); + pusher.pushFragmentInCallRef(0, 0, "c4_to_c7"); } TVMFunctionCompiler funCompiler{pusher, 0, function, false, false, 0}; @@ -357,9 +369,10 @@ TVMFunctionCompiler::generateOnTickTock(TVMCompilerContext& ctx, FunctionDefinit if (!isPure) { - pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); + pusher.pushFragmentInCallRef(0, 0, "c7_to_c4"); } - return createNode(0, 0, "onTickTock", Function::FunctionType::OnTickTock, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(0, 0, "onTickTock", nullopt, Function::FunctionType::OnTickTock, pusher.getBlock()); } void TVMFunctionCompiler::decodeFunctionParamsAndInitVars(bool isResponsible) { @@ -382,10 +395,11 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef * function result * [send int/ext msg] */ + std::string name = function->name(); + ctx.setCurrentFunction(function, name); StackPusher pusher{&ctx}; - std::string name = function->name(); - Function::FunctionType type = Function::FunctionType::Macro; + Function::FunctionType type = Function::FunctionType::Fragment; Pointer block; TVMFunctionCompiler funCompiler{pusher, 0, function, false, false, 0}; @@ -413,7 +427,7 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef int retQty = function->returnParameters().size(); // stack: selector, arg0, arg1, arg2 ... // +1 because function may use selector - pusher.pushMacroCallInCallRef(paramQty + 1, retQty + 1, pusher.ctx().getFunctionInternalName(function) + "_macro"); + pusher.pushFragmentInCallRef(paramQty + 1, retQty + 1, pusher.ctx().getFunctionInternalName(function)); solAssert(pusher.stackSize() == retQty, ""); // emit @@ -426,9 +440,10 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef pusher._throw("THROW 0"); block = pusher.getBlock(); + ctx.resetCurrentFunction(); // takes selector, sliceWithBody, functionId // returns nothing - return createNode(3, 0, name, type, block); + return createNode(3, 0, name, nullopt, type, block); } void TVMFunctionCompiler::generateFunctionWithModifiers( @@ -447,11 +462,12 @@ void TVMFunctionCompiler::generateFunctionWithModifiers( Pointer TVMFunctionCompiler::generateGetter(StackPusher &pusher, VariableDeclaration const* vd) { + pusher.ctx().setCurrentFunction(nullptr, vd->name()); TVMFunctionCompiler funCompiler{pusher, nullptr}; pusher.fixStack(+2); // stack: functionId msgBody pusher.drop(); // drop function id pusher << "ENDS"; - pusher.pushMacroCallInCallRef(0, 0, "c4_to_c7"); + pusher.pushFragmentInCallRef(0, 0, "c4_to_c7"); pusher.getGlob(vd); // check ext msg @@ -475,63 +491,60 @@ TVMFunctionCompiler::generateGetter(StackPusher &pusher, VariableDeclaration con pusher._if(); pusher._throw("THROW 0"); - - return createNode(2, 1, vd->name(), Function::FunctionType::MacroGetter, pusher.getBlock()); + pusher.ctx().resetCurrentFunction(); + return createNode(2, 1, vd->name(), nullopt, Function::FunctionType::PublicStateVariableGetter, pusher.getBlock()); } Pointer TVMFunctionCompiler::generatePublicFunctionSelector(TVMCompilerContext& ctx, ContractDefinition const *contract) { + std::string name = "public_function_selector"; + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; const std::vector>& functions = pusher.ctx().getPublicFunctions(); TVMFunctionCompiler compiler{pusher, contract}; compiler.buildPublicFunctionSelector(functions, 0, functions.size()); - return createNode(1, 1, "public_function_selector", Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(1, 1, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } -Pointer -TVMFunctionCompiler::generatePrivateFunction(TVMCompilerContext& ctx, const std::string& name, FunctionDefinition const* funDef) { - StackPusher pusher{&ctx}; - const std::string macroName = name + "_macro"; - pusher.pushMacro(0, 0, macroName); - return createNode(0, 0, name, Function::FunctionType::PrivateFunction, pusher.getBlock(), funDef); -} - -Pointer TVMFunctionCompiler::generateLibraryFunction( +Pointer TVMFunctionCompiler::generateLibFunctionWithObject( TVMCompilerContext& ctx, - FunctionDefinition const* function, - const std::string& name -) { - StackPusher pusher{&ctx}; - TVMFunctionCompiler funCompiler{pusher, 0, function, true, true, 0}; - const std::string macroName = name + "_macro"; - pusher.pushMacro(0, 0, macroName); - return createNode(0, 0, name, Function::FunctionType::PrivateFunctionWithObj, pusher.getBlock()); -} - -Pointer TVMFunctionCompiler::generateLibraryFunctionMacro( - TVMCompilerContext& ctx, - FunctionDefinition const* function, - const std::string& name + FunctionDefinition const* function ) { + 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}; funCompiler.visitFunctionWithModifiers(); int take = function->parameters().size(); int ret = function->returnParameters().size(); - return createNode(take, ret + 1, name, Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(take, ret + 1, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } Pointer TVMFunctionCompiler::generateReceive(TVMCompilerContext& ctx, FunctionDefinition const* function) { - return generateReceiveOrFallbackOrOnBounce(ctx, function, "receive_macro", 0); + std::string const name = "receive"; + ctx.setCurrentFunction(function, name); + auto f = generateReceiveOrFallbackOrOnBounce(ctx, function, name, 0); + ctx.resetCurrentFunction(); + return f; } Pointer TVMFunctionCompiler::generateFallback(TVMCompilerContext& ctx, FunctionDefinition const* function) { - return generateReceiveOrFallbackOrOnBounce(ctx, function, "fallback_macro", 0); + std::string const name = "fallback"; + ctx.setCurrentFunction(function, name); + auto f = generateReceiveOrFallbackOrOnBounce(ctx, function, name, 0); + ctx.resetCurrentFunction(); + return f; } Pointer TVMFunctionCompiler::generateOnBounce(TVMCompilerContext& ctx, const FunctionDefinition *function) { - return generateReceiveOrFallbackOrOnBounce(ctx, function, "on_bounce_macro", 1); + ctx.setCurrentFunction(function, "on_bounce"); + Pointer f = generateReceiveOrFallbackOrOnBounce(ctx, function, "on_bounce", 1); + ctx.resetCurrentFunction(); + return f; } Pointer TVMFunctionCompiler::generateReceiveOrFallbackOrOnBounce( @@ -546,7 +559,7 @@ Pointer TVMFunctionCompiler::generateReceiveOrFallbackOrOnBounce( funCompiler.pushC4ToC7IfNeed(); funCompiler.visitFunctionWithModifiers(); funCompiler.updC4IfItNeeds(); - return createNode(take, 0, name, Function::FunctionType::Macro, pusher.getBlock()); + return createNode(take, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } // pop params.size() elements from stack top @@ -567,7 +580,7 @@ void TVMFunctionCompiler::emitOnPublicFunctionReturn() { m_pusher.pushS(m_pusher.stackSize()); m_pusher.fixStack(-1); // fix stack - bool isResponsible = m_pusher.ctx().getCurrentFunction()->isResponsible(); + bool isResponsible = m_pusher.ctx().currentFunction()->isResponsible(); // emit for ext m_pusher.startContinuation(); @@ -813,7 +826,7 @@ bool TVMFunctionCompiler::visit(VariableDeclarationStatement const &_variableDec for (std::size_t i = 0; i < tuple.size(); ++i) { acceptExpr(tuple[i].get()); if (variables.at(i) != nullptr) { - m_pusher.hardConvert(variables.at(i)->type(), tuple.at(i)->annotation().type); + m_pusher.convert(variables.at(i)->type(), tuple.at(i)->annotation().type); } else { ++bad; m_pusher.drop(); @@ -822,13 +835,13 @@ bool TVMFunctionCompiler::visit(VariableDeclarationStatement const &_variableDec } else { acceptExpr(init); if (varQty == 1) { - m_pusher.hardConvert(variables.at(0)->type(), init->annotation().type); + m_pusher.convert(variables.at(0)->type(), init->annotation().type); } else { auto tuple = to(init->annotation().type); std::vector hasName(varQty); for (int i = varQty - 1; i >= 0; --i) { if (variables.at(i) != nullptr) { - m_pusher.hardConvert(variables.at(i)->type(), tuple->components().at(i)); + m_pusher.convert(variables.at(i)->type(), tuple->components().at(i)); hasName[i] = true; } else { ++bad; @@ -894,7 +907,9 @@ void TVMFunctionCompiler::acceptBody(Block const& _block, std::optional= 0; --i) { Type const* leftType = m_function->returnParameters().at(i)->type(); Type const* rightType = givenTypes.at(i); - m_pusher.hardConvert(leftType, rightType); + m_pusher.convert(leftType, rightType); if (retQty >= 2) { m_pusher.blockSwap(retQty - 1, 1); } @@ -1548,18 +1563,23 @@ bool TVMFunctionCompiler::visit(Continue const&) { bool TVMFunctionCompiler::visit(EmitStatement const &_emit) { auto eventCall = to(&_emit.eventCall()); solAssert(eventCall, ""); - CallableDeclaration const * eventDef = getFunctionDeclarationOrConstructor(&eventCall->expression()); - solAssert(eventDef, "Event Declaration was not found"); + CallableDeclaration const * def = getFunctionDeclarationOrConstructor(&eventCall->expression()); + solAssert(def, "Event Declaration was not found"); + auto eventDef = to(def); std::vector> args = eventCall->arguments(); for (ASTPointer const& arg : args | boost::adaptors::reversed) { TVMExpressionCompiler{m_pusher}.compileNewExpr(arg.get()); } + string name = eventName(eventDef); + auto appendBody = [&](int builderSize) { ChainDataEncoder{&m_pusher}.createMsgBodyAndAppendToBuilder( convertArray(eventDef->parameters()), - ChainDataEncoder{&m_pusher}.calculateFunctionIDWithReason(eventDef, ReasonOfOutboundMessage::EmitEventExternal), + ChainDataEncoder{&m_pusher}.calculateFunctionIDWithReason( + name, getTypesFromVarDecls(eventDef->parameters()), nullptr, + ReasonOfOutboundMessage::EmitEventExternal, std::nullopt, false), {}, builderSize, true @@ -1616,6 +1636,8 @@ Pointer TVMFunctionCompiler::generateMainExternal( // msg_body_slice // transaction_id = -1 + std::string name = "main_external"; + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; TVMFunctionCompiler f{pusher, contract}; @@ -1624,14 +1646,13 @@ Pointer TVMFunctionCompiler::generateMainExternal( f.setGlobSenderAddressIfNeed(); pusher.pushS(1); - pusher.pushMacroCallInCallRef(0, 0, "c4_to_c7_with_init_storage"); + pusher.pushFragmentInCallRef(0, 0, "c4_to_c7_with_init_storage"); f.checkSignatureAndReadPublicKey(); if (pusher.ctx().afterSignatureCheck()) { // ... msg_cell msg_body_slice -1 rest_msg_body_slice pusher.pushS(3); - Pointer block = pusher.ctx().getInlinedFunction("afterSignatureCheck"); - pusher.pushInlineFunction(block, 2, 1); + pusher.pushInlineFunction("afterSignatureCheck", 2, 1); } else { if (pusher.ctx().pragmaHelper().hasTime()) f.defaultReplayProtection(); if (pusher.ctx().pragmaHelper().hasExpire()) f.expire(); @@ -1642,8 +1663,9 @@ Pointer TVMFunctionCompiler::generateMainExternal( pusher.exchange(1); f.callPublicFunctionOrFallback(); + ctx.resetCurrentFunction(); return createNode( - 0, 0, "main_external", + 0, 0, name, nullopt, Function::FunctionType::MainExternal, pusher.getBlock() ); @@ -1734,7 +1756,7 @@ void TVMFunctionCompiler::defaultReplayProtection() { // msgSlice m_pusher << "LDU 64 ; timestamp msgSlice"; m_pusher.exchange(1); - m_pusher.pushMacro(1, 0, "replay_protection_macro"); + m_pusher.pushFragment(1, 0, "replay_protection"); } void TVMFunctionCompiler::expire() { @@ -1746,12 +1768,12 @@ void TVMFunctionCompiler::expire() { } void TVMFunctionCompiler::callPublicFunctionOrFallback() { - m_pusher.pushMacroCallInCallRef(0, 0, "public_function_selector"); + m_pusher.pushFragmentInCallRef(0, 0, "public_function_selector"); if (m_pusher.ctx().isFallBackGenerated()) { m_pusher.drop(2); m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "fallback_macro"); + m_pusher.pushFragment(0, 0, "fallback"); m_pusher.pushRefContAndCallX(0, 0, false); } else { m_pusher._throw("THROW " + toString(TvmConst::RuntimeException::NoFallback)); @@ -1766,6 +1788,8 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin // created_lt:uint64 created_at:uint32 // = CommonMsgInfoRelaxed; + std::string name = "main_internal"; + ctx.setCurrentFunction(nullptr, name); StackPusher pusher{&ctx}; TVMFunctionCompiler funCompiler{pusher, contract}; @@ -1782,7 +1806,7 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin pusher << "LDMSGADDR ; bounced src tail"; pusher.drop(); if (sc.hasAwaitCall()) { - pusher.pushMacroCallInCallRef(0, 0, "check_resume"); + pusher.pushFragmentInCallRef(0, 0, "check_resume"); } pusher.setGlob(TvmConst::C7::SenderAddress); pusher << "MODPOW2 1"; @@ -1812,7 +1836,7 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin pusher.pushS(1); pusher << "LDSLICE 32"; pusher.dropUnder(1, 1); - pusher.pushMacro(0, 0, "on_bounce_macro"); + pusher.pushFragment(0, 0, "on_bounce"); pusher.endContinuationFromRef(); pusher.ifJmp(); } else { @@ -1823,11 +1847,13 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin pusher.exchange(1); funCompiler.callPublicFunctionOrFallback(); - - return createNode(0, 0, "main_internal", Function::FunctionType::MainInternal, pusher.getBlock()); + ctx.resetCurrentFunction(); + 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 @@ -1853,7 +1879,7 @@ PUSHCONT { DROP NIP CALLREF { - CALL $c4_to_c7$ + .inline c4_to_c7 } CALLX } @@ -1862,11 +1888,13 @@ PUSHCONT { } 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)); - return createNode(0, 0, "check_resume", Function::FunctionType::Macro, pusher.getBlock()); + ctx.resetCurrentFunction(); + return createNode(0, 0, name, nullopt, Function::FunctionType::Fragment, pusher.getBlock()); } bool TVMFunctionCompiler::visit(PlaceholderStatement const &) { @@ -1881,7 +1909,7 @@ void TVMFunctionCompiler::pushC4ToC7IfNeed() { m_pusher.was_c4_to_c7_called(); m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "c4_to_c7"); + m_pusher.pushFragment(0, 0, "c4_to_c7"); m_pusher.endContinuationFromRef(); m_pusher._if(); } @@ -1891,7 +1919,7 @@ void TVMFunctionCompiler::updC4IfItNeeds() { // c7_to_c4 if need // solAssert(m_pusher.stackSize() == 0, ""); if (m_function->stateMutability() == StateMutability::NonPayable) { - m_pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); + m_pusher.pushFragmentInCallRef(0, 0, "c7_to_c4"); } else { // if it's external message than save values for replay protection if (m_pusher.ctx().afterSignatureCheck() == nullptr && @@ -1900,13 +1928,13 @@ void TVMFunctionCompiler::updC4IfItNeeds() { ) { m_pusher.pushS(0); m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "upd_only_time_in_c4"); + m_pusher.pushFragment(0, 0, "upd_only_time_in_c4"); m_pusher.endContinuationFromRef(); m_pusher._if(); } else { m_pusher.pushS(0); m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "c7_to_c4"); + m_pusher.pushFragment(0, 0, "c7_to_c4"); m_pusher.endContinuationFromRef(); m_pusher._if(); } @@ -1920,7 +1948,7 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { if (m_contract->fallbackFunction()) { m_pusher.startContinuation(); m_pusher.drop(); - m_pusher.pushMacroCallInCallRef(0, 0, "fallback_macro"); + m_pusher.pushFragmentInCallRef(0, 0, "fallback"); m_pusher._throw("THROW 0"); m_pusher.endContinuation(); m_pusher.ifNot(); @@ -1956,7 +1984,7 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { m_pusher.endContinuation(); m_pusher.ifNot(); m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, "receive_macro"); + m_pusher.pushFragment(0, 0, "receive"); m_pusher.endContinuationFromRef(); m_pusher.ifJmp(); } else { @@ -1995,7 +2023,7 @@ void TVMFunctionCompiler::buildPublicFunctionSelector( m_pusher << "EQUAL"; m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); - m_pusher.pushMacro(0, 0, name); + m_pusher.pushFragment(0, 0, name); m_pusher.endContinuationFromRef(); m_pusher.ifJmp(); }; diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp index c8b81fd5..353cfddc 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp @@ -43,15 +43,13 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable static Pointer generateBuildTuple(TVMCompilerContext& ctx, std::string const& name, const std::vector& types); static Pointer generateNewArrays(TVMCompilerContext& ctx, std::string const& name, FunctionCall const* arr); static Pointer generateConstArrays(TVMCompilerContext& ctx, std::string const& name, TupleExpression const* arr); - static Pointer generateMacro(TVMCompilerContext& ctx, FunctionDefinition const* function, const std::optional& forceName = std::nullopt); + 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 generatePrivateFunction(TVMCompilerContext& ctx, const std::string& name, FunctionDefinition const* funDef); - static Pointer generateLibraryFunction(TVMCompilerContext& ctx, FunctionDefinition const* function, const std::string& name); - static Pointer generateLibraryFunctionMacro(TVMCompilerContext& ctx, FunctionDefinition const* function, const std::string& name); + static Pointer generateLibFunctionWithObject(TVMCompilerContext& ctx, FunctionDefinition const* function); 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/TVMPusher.cpp b/compiler/libsolidity/codegen/TVMPusher.cpp index ae49aef5..f3704078 100644 --- a/compiler/libsolidity/codegen/TVMPusher.cpp +++ b/compiler/libsolidity/codegen/TVMPusher.cpp @@ -41,7 +41,7 @@ void StackPusher::pushLoc(const std::string& file, int line) { } void StackPusher::pushString(const std::string& _str, bool toSlice) { - std::string hexStr = stringToHex(_str); // 2 * len(_str) == len(hexStr). One symbol to 2 hex digits + std::string hexStr = StrUtils::stringToHex(_str); // 2 * len(_str) == len(hexStr). One symbol to 2 hex digits solAssert(hexStr.length() % 2 == 0, ""); if (4 * hexStr.length() <= TvmConst::MaxPushSliceBitLength && toSlice) { pushSlice("x" + hexStr); @@ -62,7 +62,7 @@ void StackPusher::pushLog() { } // TODO move to function compiler -Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { +Pointer StackPusher::generateC7ToC4(bool forAwait) { const std::vector& memberTypes = m_ctx->notConstantStateVariableTypes(); const int stateVarQty = memberTypes.size(); if (ctx().tooMuchStateVariables()) { @@ -162,7 +162,7 @@ Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { popRoot(); } Pointer block = getBlock(); - auto f = createNode(0, 0, (forAwait ? "c7_to_c4_for_await" : "c7_to_c4"), Function::FunctionType::Macro, + auto f = createNode(0, 0, (forAwait ? "c7_to_c4_for_await" : "c7_to_c4"), nullopt, Function::FunctionType::Fragment, block); return f; } @@ -206,7 +206,6 @@ StackPusher::prepareValueForDictOperations(Type const *keyType, Type const *valu case DictValueType::Bool: case DictValueType::Enum: - case DictValueType::ExtraCurrencyCollection: case DictValueType::FixedBytes: case DictValueType::FixedPoint: case DictValueType::Integer: @@ -271,7 +270,6 @@ DataType StackPusher::pushDefaultValueForDict(Type const* keyType, Type const* v break; } - case DictValueType::ExtraCurrencyCollection: case DictValueType::Mapping: { pushSlice("x4_"); value = DataType::Slice; @@ -338,7 +336,6 @@ bool StackPusher::doesDictStoreValueInRef(Type const* keyType, Type const* value case DictValueType::Bool: case DictValueType::Contract: case DictValueType::Enum: - case DictValueType::ExtraCurrencyCollection: case DictValueType::FixedBytes: case DictValueType::FixedPoint: case DictValueType::Integer: @@ -412,7 +409,6 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( [[fallthrough]]; case DictValueType::Bool: case DictValueType::Enum: - case DictValueType::ExtraCurrencyCollection: case DictValueType::FixedBytes: case DictValueType::FixedPoint: case DictValueType::Integer: @@ -453,7 +449,7 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( auto checkOnMappingOrOptional = [&]() { if (optValueAsTuple(valueType)) { - tuple(1); + makeTuple(1); } }; @@ -492,7 +488,7 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( preloadValue(); if (haveKey) { if (!saveOrigKeyAndNoTuple) { - tuple(2); + makeTuple(2); } } else { checkOnMappingOrOptional(); @@ -533,11 +529,12 @@ void StackPusher::setDict(Type const &keyType, Type const &valueType, const Data d.dictSet(); } -void StackPusher::pushInlineFunction(const Pointer& block, int take, int ret) { +void StackPusher::pushInlineFunction(std::string const& name, int take, int ret) { + solAssert(!ctx().callGraph().tryToAddEdge(ctx().currentFunctionName(), name), ""); + auto block = ctx().getInlinedFunction(name); solAssert(block->type() == CodeBlock::Type::None, ""); - for (Pointer const& i : block->instructions()) { + for (Pointer const& i : block->instructions()) m_instructions.back().emplace_back(i); - } change(take, ret); } @@ -728,9 +725,11 @@ void StackPusher::pushSlice(std::string const& data) { change(0, 1); } -void StackPusher::pushPrivateFunctionId(FunctionDefinition const& funDef) { - std::string funName = ctx().getFunctionInternalName(&funDef, false); - *this << "PUSHINT $" + funName + "$"; +void StackPusher::pushPrivateFunctionId(FunctionDefinition const& funDef, bool isCalledByPoint) { + std::string funName = ctx().getFunctionInternalName(&funDef, isCalledByPoint); + uint32_t id = funDef.functionID().has_value() ? funDef.functionID().value() : ChainDataEncoder::toHash256(funName); + pushInt(id); + ctx().callGraph().addPrivateFunction(id, funName); } void StackPusher::startContinuation() { @@ -970,7 +969,7 @@ void StackPusher::setIndexQ(int index) { } } -void StackPusher::tuple(int qty) { +void StackPusher::makeTuple(int qty) { solAssert(0 <= qty, ""); if (qty <= 15) { *this << "TUPLE " + toString(qty); @@ -993,7 +992,7 @@ void StackPusher::resetAllStateVars() { for (VariableDeclaration const *variable: stateVariables) pushDefaultValue(variable->type()); const int stateVarQty = stateVariables.size(); - tuple(TvmConst::C7::FirstIndexForVariables + stateVarQty); + makeTuple(TvmConst::C7::FirstIndexForVariables + stateVarQty); popC7(); } else { for (VariableDeclaration const *variable: stateVariables) @@ -1137,7 +1136,7 @@ bool StackPusher::fastLoad(const Type* type) { startContinuation(); if (optValueAsTuple(opt->valueType())) { f(true); - tuple(1); + makeTuple(1); exchange(1); } else { f(false); @@ -1172,7 +1171,7 @@ bool StackPusher::fastLoad(const Type* type) { load(t, false); } blockSwap(tup->components().size(), 1); - tuple(tup->components().size()); + makeTuple(tup->components().size()); return false; } case Type::Category::TvmCell: @@ -1185,7 +1184,7 @@ bool StackPusher::fastLoad(const Type* type) { load(t->type(), false); } blockSwap(members.size(), 1); - tuple(members.size()); + makeTuple(members.size()); exchange(1); return true; } @@ -1225,7 +1224,7 @@ bool StackPusher::fastLoad(const Type* type) { *this << "LDDICT"; return true; case Type::Category::VarInteger: { - auto varInt = to(type); + auto varInt = to(type); std::string cmd = "LDVAR"; if (!varInt->asIntegerType().isSigned()) cmd += "U"; cmd += "INT" + std::to_string(varInt->n()); @@ -1300,7 +1299,6 @@ void StackPusher::preload(const Type *type) { break; } case Type::Category::Mapping: - case Type::Category::ExtraCurrencyCollection: *this << "PLDDICT"; break; case Type::Category::VarInteger: @@ -1386,7 +1384,7 @@ void StackPusher::loadQ(const Type *type) { { startContinuation(); // u32 dict s rotRev(); - this->tuple(2); + this->makeTuple(2); blockSwap(1, 1); *this << "TRUE"; endContinuation(); @@ -1408,7 +1406,6 @@ void StackPusher::loadQ(const Type *type) { break; } case Type::Category::Mapping: - case Type::Category::ExtraCurrencyCollection: pushAsym("LDDICTQ"); break; case Type::Category::VarInteger: { @@ -1532,7 +1529,6 @@ void StackPusher::store( break; } case Type::Category::Mapping: - case Type::Category::ExtraCurrencyCollection: if (reverse) { exchange(1); // builder dict } @@ -1571,7 +1567,7 @@ 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()); @@ -1590,436 +1586,39 @@ void StackPusher::pushZeroAddress() { pushSlice("x8000000000000000000000000000000000000000000000000000000000000000001_"); } -bigint StackPusher::pow10(int power) { - bigint r = 1; - for (int i = 1; i <= power; ++i) { - r *= 10; - } - return r; -} - -void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { - // opt(opt(opt(opt(T)))) = T; - // opt(opt(opt(opt(T0, T1, T2)))) = (T0, T1, T2); - int lQty = optTypeQty(leftType); - int rQty = optTypeQty(rightType); - solAssert(lQty >= rQty, ""); - if (lQty > rQty) { - auto l = to(leftType); - hardConvert(l->valueType(), rightType); - // optional(uint, uint) q = (1, 2); - if (l->valueType()->category() == Type::Category::Tuple){ - if (rightType->category() != Type::Category::Null) { - auto tt = to(l->valueType()); - tuple(tt->components().size()); - } - // optional([mapping|optional]) q = ... - } else if (optValueAsTuple(l->valueType())) { - tuple(1); - } - return; - } - - - - bool impl = rightType->isImplicitlyConvertibleTo(*leftType); - - auto fixedPointFromFixedPoint = [this, impl](FixedPointType const* l, FixedPointType const* r) { - int powerDiff = l->fractionalDigits() - r->fractionalDigits(); - if (powerDiff != 0) { - if (powerDiff > 0) { - pushInt(pow10(powerDiff)); - *this << "MUL"; - } else { - pushInt(pow10(-powerDiff)); - *this << "DIV"; - } - } - if (!impl) - checkFit(l); - }; - - auto integerFromFixedPoint = [this, impl](IntegerType const* l, FixedPointType const* r) { - int powerDiff = r->fractionalDigits(); - if (powerDiff > 0) { - pushInt(pow10(powerDiff)); - *this << "DIV"; - } - if (!impl) - checkFit(l); - }; - - auto integerFromInteger = [this, impl](IntegerType const* l, IntegerType const* r) { - if (!impl) { - bool canConvert = l->numBits() > r->numBits() && l->isSigned() && !r->isSigned(); - if (!canConvert) { - checkFit(l); - } - } - }; - - auto fixedPointFromInteger = [this, impl](FixedPointType const* l, IntegerType const* /*r*/) { - int powerDiff = l->fractionalDigits(); - if (powerDiff > 0) { - pushInt(pow10(powerDiff)); - *this << "MUL"; - } - if (!impl) - checkFit(l); - }; - - auto fixedBytesFromFixedBytes = [this](FixedBytesType const* l, FixedBytesType const* r) { - int diff = 8 * (l->numBytes() - r->numBytes()); - if (diff > 0) { - *this << "LSHIFT " + std::to_string(diff); - } else if (diff < 0) { - *this << "RSHIFT " + std::to_string(-diff); - } - }; - - auto fixedBytesFromBytes = [this](FixedBytesType const* r) { - size_t bits = r->numBytes() * 8; - startContinuation(); - startOpaque(); - *this << "CTOS"; // slice - pushAsym("LDUQ " + std::to_string(bits)); - // data slice flag - - // if load succeeded drop slice - startContinuation(); - drop(1); - endContinuation(); - - // if load failed load all available data - startContinuation(); - // slice - pushS(0); - *this << "SBITS"; - // slice slice_bits - pushS(0); - // slice slice_bits slice_bits - rotRev(); - // slice_bits slice slice_bits - *this << "PLDUX"; - // slice_bits number - blockSwap(1, 1); - // number slice_bits - *this << "NEGATE"; - pushInt(bits); - *this << "ADD"; - *this << "LSHIFT"; - // number with trailing zeros - endContinuation(); - ifElse(); - endOpaque(1, 1); - pushRefContAndCallX(1, 1, false); - }; - - auto fixedBytesFromStringLiteral = [this](FixedBytesType const* l, StringLiteralType const* r) { - size_t bytes = 0; - u256 value = 0; - for (char c : r->value()) { - value = value * 256 + c; - ++bytes; - } - while (bytes < l->numBytes()) { - value *= 256; - ++bytes; - } - drop(1); // delete old value - *this << "PUSHINT " + toString(value); - }; - - auto fromFixedPoint = [&](FixedPointType const* r) { - switch (leftType->category()) { - case Type::Category::FixedPoint: - fixedPointFromFixedPoint(to(leftType), r); - break; - case Type::Category::Integer: - integerFromFixedPoint(to(leftType), r); - break; - case Type::Category::VarInteger: - integerFromFixedPoint(&to(leftType)->asIntegerType(), r); - break; - default: - solUnimplemented(leftType->toString()); - break; - } - }; - - auto fromInteger = [&](IntegerType const* r) { - switch (leftType->category()) { - case Type::Category::FixedPoint: - fixedPointFromInteger(to(leftType), r); - break; - case Type::Category::Integer: - integerFromInteger(to(leftType), r); - break; - case Type::Category::VarInteger: - integerFromInteger(&to(leftType)->asIntegerType(), r); - break; - case Type::Category::Function: { - m_ctx->setPragmaSaveAllFunctions(); - break; - } - case Type::Category::FixedBytes: - // do nothing here - break; - case Type::Category::Address: - solUnimplemented("See FunctionCallCompiler::typeConversion"); - break; - default: - solUnimplemented(leftType->toString()); - break; - } - }; - - auto tupleFromTuple = [&](TupleType const* leftType, TupleType const* rightType) { - std::vector const& lc = leftType->components(); - std::vector const& rc = rightType->components(); - solAssert(lc.size() == rc.size(), ""); - int n = lc.size(); - for (int i = n - 1; 0 <= i; --i) { - hardConvert(lc.at(i), rc.at(i)); - if (n >= 2) { - blockSwap(n - 1, 1); - } - } - }; - - - - switch (rightType->category()) { - - case Type::Category::RationalNumber: { - Type const* mt = rightType->mobileType(); - if (mt->category() == Type::Category::Integer) { - fromInteger(to(mt)); - } else if (mt->category() == Type::Category::FixedPoint) { - fromFixedPoint(to(mt)); - } else { - solUnimplemented(""); - } - break; - } - - case Type::Category::FixedPoint: { - fromFixedPoint(to(rightType)); - break; - } - - case Type::Category::VarInteger: { - fromInteger(&to(rightType)->asIntegerType()); - break; - } - - case Type::Category::Integer: { - fromInteger(to(rightType)); - break; - } - - case Type::Category::FixedBytes: { - auto r = to(rightType); - switch (leftType->category()) { - case Type::Category::FixedBytes: { - fixedBytesFromFixedBytes(to(leftType), r); - break; - } - case Type::Category::Integer: { - auto intType = to(leftType); - if (intType && !intType->isSigned() && - (intType->numBits() >= r->numBytes() * 8)) - break; - solUnimplemented(""); - break; - } - case Type::Category::FixedPoint: { - auto fixType = to(leftType); - if (fixType && fixType->isSigned() && - (fixType->numBits() >= 8 * r->numBytes())) { - fromInteger(to(rightType)); - break; - } - solUnimplemented(""); - break; - } - case Type::Category::Array: { - auto stringType = to(leftType); - solAssert(stringType->isByteArrayOrString(), ""); - *this << "NEWC"; - *this << "STU " + toString(8 * r->numBytes()); - *this << "ENDC"; - break; - } - default: - solUnimplemented(""); - break; - } - break; - } - - case Type::Category::Array: { - auto r = to(rightType); - if (!r->isByteArrayOrString()) { - break; - } - // bytes or string - switch (leftType->category()) { - case Type::Category::FixedBytes: - fixedBytesFromBytes(to(leftType)); - break; - case Type::Category::Array: - break; - case Type::Category::TvmSlice: - *this << "CTOS"; - break; - default: - solUnimplemented(""); - break; - } - break; - } - - case Type::Category::Optional: { - auto r = to(rightType); - switch (leftType->category()) { - case Type::Category::Optional: { - auto l = to(leftType); - startOpaque(); - - pushS(0); - *this << "ISNULL"; - fixStack(-1); // fix stack - - startContinuation(); - if (optValueAsTuple(l->valueType())) { - untuple(1); - } else if (auto tt = to(l->valueType())) { - untuple(tt->components().size()); - } - hardConvert(l->valueType(), r->valueType()); - if (optValueAsTuple(l->valueType())) { - tuple(1); - } else if (auto tt = to(l->valueType())) { - tuple(tt->components().size()); - } - endContinuation(); - ifNot(); - - endOpaque(1, 1, true); - break; - } - default: - break; - } - break; - } - - case Type::Category::TvmSlice: { - switch (leftType->category()) { - case Type::Category::TvmSlice: - break; - case Type::Category::Array: { - auto arrType = to(leftType); - solAssert(arrType->isByteArrayOrString(), ""); - *this << "NEWC" // s b - << "STSLICE" // b' - << "ENDC"; // cell - break; - } - default: - solUnimplemented(""); - } - break; - } - - case Type::Category::Address: - case Type::Category::Bool: - case Type::Category::Contract: - case Type::Category::Enum: - case Type::Category::ExtraCurrencyCollection: - case Type::Category::Function: - case Type::Category::Mapping: - case Type::Category::TvmVector: - case Type::Category::Struct: - case Type::Category::TvmBuilder: - case Type::Category::TvmCell: - case Type::Category::Null: - case Type::Category::EmpyMap: - break; - case Type::Category::Tuple: { - auto r = to(rightType); - switch (leftType->category()) { - case Type::Category::Tuple: - tupleFromTuple(to(leftType), r); - break; - default: - solUnimplemented(leftType->toString()); - break; - } - break; - } - - case Type::Category::StringLiteral: { - auto r = to(rightType); - switch (leftType->category()) { - case Type::Category::FixedBytes: - fixedBytesFromStringLiteral(to(leftType), r); - break; - case Type::Category::Array: - break; - default: - solUnimplemented(leftType->toString()); - break; - } - break; - } - - case Type::Category::UserDefinedValueType: { - break; - } - - default: - solUnimplemented(rightType->toString()); - break; - } +void StackPusher::convert(Type const *leftType, Type const *rightType) { + TypeConversion{*this}.convert(leftType, rightType); } void StackPusher::checkFit(Type const *type) { switch (type->category()) { - - case Type::Category::Integer: { - auto it = to(type); - if (it->isSigned()) { - *this << "FITS " + toString(it->numBits()); - } else { - *this << "UFITS " + toString(it->numBits()); - } - break; - } - - case Type::Category::FixedPoint: { - auto fp = to(type); - if (fp->isSigned()) { - *this << "FITS " + toString(fp->numBits()); - } else { - *this << "UFITS " + toString(fp->numBits()); - } - break; - } - - case Type::Category::VarInteger: { - auto varInt = to(type); - checkFit(&varInt->asIntegerType()); - break; - } - - default: - solUnimplemented(""); - break; + case Type::Category::Integer: { + auto it = to(type); + if (it->isSigned()) + *this << "FITS " + toString(it->numBits()); + else + *this << "UFITS " + toString(it->numBits()); + break; + } + case Type::Category::FixedPoint: { + auto fp = to(type); + if (fp->isSigned()) + *this << "FITS " + toString(fp->numBits()); + else + *this << "UFITS " + toString(fp->numBits()); + break; } + case Type::Category::VarInteger: { + auto varInt = to(type); + checkFit(&varInt->asIntegerType()); + break; + } + default: + solUnimplemented(""); + break; + } } void StackPusher::pushParameter(std::vector> const& params) { @@ -2028,56 +1627,48 @@ void StackPusher::pushParameter(std::vector> con } } -void StackPusher::pushMacroCallInCallRef(int take, int ret, const string& functionName) { +void StackPusher::pushFragmentInCallRef(int take, int ret, const std::string &functionName) { startContinuation(); - pushMacro(take, ret, functionName); + pushFragment(take, ret, functionName); pushRefContAndCallX(take, ret, false); } void StackPusher::pushCallOrCallRef( - const string &functionName, - const FunctionType *ft, - const std::optional>& deltaStack + FunctionDefinition const* _functionDef, + const std::optional>& deltaStack, + bool isCalledByPoint ) { - int take{}; - int ret{}; - if (deltaStack.has_value()) { - std::tie(take, ret) = deltaStack.value(); - } else { - take = ft->parameterTypes().size(); - ret = ft->returnParameterTypes().size(); - } - - if (boost::ends_with(functionName, "_macro")) { - pushMacroCallInCallRef(take, ret, functionName); - return; - } - - auto _to = to(&ft->declaration()); - FunctionDefinition const* v = m_ctx->getCurrentFunction(); - bool hasLoop = m_ctx->addAndDoesHaveLoop(v, _to); - auto fd = to(&ft->declaration()); - const bool isOnCodeUpgrade = fd->name() == "onCodeUpgrade"; - if (hasLoop || isOnCodeUpgrade) { - pushPrivateFunctionId(*_to); + auto [take, ret] = deltaStack.has_value() ? + deltaStack.value() : + std::make_pair(_functionDef->parameters().size(), _functionDef->returnParameters().size()); + + std::string curFunctionName = ctx().currentFunctionName(); + std::string functionName = ctx().getFunctionInternalName(_functionDef, isCalledByPoint); + if (_functionDef->name() == "onCodeUpgrade" || + m_ctx->callGraph().tryToAddEdge(curFunctionName, functionName) // Does it have a loop? + ) { + pushPrivateFunctionId(*_functionDef, isCalledByPoint); pushC3(); execute(take + 2, ret); } else { - pushMacroCallInCallRef(take, ret, functionName + "_macro"); + pushFragmentInCallRef(take, ret, functionName); } } -void StackPusher::pushMacro(int take, int ret, const std::string& functionName) { +void StackPusher::pushFragment(int take, int ret, const std::string& functionName) { + solAssert(!ctx().callGraph().tryToAddEdge(ctx().currentFunctionName(), functionName), ""); change(take, ret); - auto opcode = createNode(".inline __" + functionName, take, ret); + auto opcode = createNode(".inline " + functionName, take, ret); m_instructions.back().push_back(opcode); } void StackPusher::computeConstCell(std::string const& expName) { + solAssert(!ctx().callGraph().tryToAddEdge(ctx().currentFunctionName(), expName), ""); pushCellOrSlice(createNode(PushCellOrSlice::Type::PUSHREF_COMPUTE, expName, nullptr)); } void StackPusher::computeConstSlice(std::string const& expName) { + solAssert(!ctx().callGraph().tryToAddEdge(ctx().currentFunctionName(), expName), ""); pushCellOrSlice(createNode(PushCellOrSlice::Type::PUSHREFSLICE_COMPUTE, expName, nullptr)); } @@ -2141,19 +1732,9 @@ Type const* StackPusher::parseIndexType(Type const *type) { if (auto mappingType = to(type)) { return mappingType->keyType(); } - if (auto currencyType = to(type)) { - return currencyType->keyType(); - } solUnimplemented(""); } -Type const* StackPusher::parseValueType(IndexAccess const &indexAccess) { - if (auto currencyType = to(indexAccess.baseExpression().annotation().type)) { - return currencyType->realValueType(); - } - return indexAccess.annotation().type; -} - void StackPusher::assignStackVariable(Declaration const *name) { auto& stack = getStack(); int idx = stack.getOffset(name); @@ -2505,6 +2086,48 @@ bool InherHelper::isBaseFunction(CallableDeclaration const* d) const { return m_baseFunctions.count(d) != 0; } +bool FunctionCallGraph::tryToAddEdge(std::string const& _v, std::string const& _to) { + m_graph[_v].insert(_to); + m_graph[_to]; // creates default value if there is no such key + for (const auto& k : m_graph | boost::adaptors::map_keys) + m_color[k] = Color::White; + m_order = {}; + bool hasLoop{}; + for (const auto& k : m_graph | boost::adaptors::map_keys) + if (dfs(k)) { + hasLoop = true; + m_graph[_v].erase(_to); + break; + } + return hasLoop; +} + +std::vector FunctionCallGraph::DAG() { + for (const auto& k : m_graph | boost::adaptors::map_keys) + m_color[k] = Color::White; + m_order = {}; + for (const auto& k : m_graph | boost::adaptors::map_keys) + dfs(k); + return m_order; +} + +bool FunctionCallGraph::dfs(std::string const& v) { + if (m_color.at(v) == Color::Black) + return false; + if (m_color.at(v) == Color::Red) + return true; + + // It's white + m_color.at(v) = Color::Red; + for (std::string const& _to : m_graph.at(v)) + if (dfs(_to)) + return true; + m_order.emplace_back(v); + + m_color.at(v) = Color::Black; + return false; +} + void TVMCompilerContext::initMembers(ContractDefinition const *contract) { solAssert(!m_contract, ""); m_contract = contract; @@ -2520,6 +2143,7 @@ TVMCompilerContext::TVMCompilerContext(ContractDefinition const *contract, Pragm m_usage{*contract}, m_inherHelper{contract} { + m_isUncheckedBlock.push(false); initMembers(contract); } @@ -2552,25 +2176,20 @@ bool TVMCompilerContext::isStdlib() const { } string TVMCompilerContext::getFunctionInternalName(FunctionDefinition const* _function, bool calledByPoint) const { - if (isStdlib()) { + if (isStdlib()) return _function->name(); - } - if (_function->isFallback()) { - return "fallback"; - } std::string functionName; const std::string hexName = _function->externalIdentifierHex(); ContractDefinition const* contract = _function->annotation().contract; - if (contract && contract->isLibrary()) { + if (contract && contract->isLibrary()) functionName = getLibFunctionName(_function, calledByPoint); - } else if (calledByPoint && isBaseFunction(_function)) { + else if (calledByPoint && isBaseFunction(_function)) functionName = _function->annotation().contract->name() + "_" + _function->name() + "_" + hexName; - } else if (_function->isFree()) { + else if (_function->isFree()) functionName = _function->name() + "_" + hexName + "_free_internal"; - } else { + else functionName = _function->name() + "_" + hexName + "_internal"; - } return functionName; } @@ -2598,7 +2217,8 @@ const ContractDefinition *TVMCompilerContext::getContract() const { } bool TVMCompilerContext::ignoreIntegerOverflow() const { - return ignoreIntOverflow; + solAssert(!m_isUncheckedBlock.empty(), ""); + return ignoreIntOverflow || m_isUncheckedBlock.top(); } FunctionDefinition const *TVMCompilerContext::afterSignatureCheck() const { @@ -2655,45 +2275,10 @@ const std::vector>& TVMCompilerContext::getPubl return m_publicFunctions; } -bool TVMCompilerContext::addAndDoesHaveLoop(FunctionDefinition const* _v, FunctionDefinition const* _to) { - graph[_v].insert(_to); - graph[_to]; // creates default value if there is no such key - for (const auto& k : graph | boost::adaptors::map_keys) { - color[k] = Color::White; - } - bool hasLoop{}; - for (const auto& k : graph | boost::adaptors::map_keys) { - if (dfs(k)) { - hasLoop = true; - graph[_v].erase(_to); - break; - } - } - return hasLoop; -} - bool TVMCompilerContext::isBaseFunction(CallableDeclaration const* d) const { return m_inherHelper.isBaseFunction(d); } -bool TVMCompilerContext::dfs(FunctionDefinition const* v) { - if (color.at(v) == Color::Black) { - return false; - } - if (color.at(v) == Color::Red) { - return true; - } - // It's white - color.at(v) = Color::Red; - for (FunctionDefinition const* _to : graph.at(v)) { - if (dfs(_to)) { - return true; - } - } - color.at(v) = Color::Black; - return false; -} - void StackPusher::pushEmptyArray() { pushInt(0); *this << "NEWDICT"; @@ -2733,7 +2318,6 @@ void StackPusher::pushDefaultValue(Type const* _type) { pushEmptyArray(); break; case Type::Category::Mapping: - case Type::Category::ExtraCurrencyCollection: *this << "NEWDICT"; break; case Type::Category::Struct: { @@ -2757,7 +2341,7 @@ void StackPusher::pushDefaultValue(Type const* _type) { pushNull(); break; case Type::Category::TvmVector: - tuple(0); + makeTuple(0); break; case Type::Category::UserDefinedValueType: { auto userDefValue = to(_type); @@ -2840,3 +2424,448 @@ void StackPusher::clear() { void StackPusher::takeLast(int n) { m_stack.takeLast(n); } + +void TypeConversion::convert(Type const* leftType, Type const* rightType) { + // opt(opt(opt(opt(T)))) = T; + // opt(opt(opt(opt(T0, T1, T2)))) = (T0, T1, T2); + int lQty = optTypeQty(leftType); + int rQty = optTypeQty(rightType); + solAssert(lQty >= rQty, ""); + if (lQty > rQty) { + auto l = to(leftType); + convert(l->valueType(), rightType); + // optional(uint, uint) q = (1, 2); + if (l->valueType()->category() == Type::Category::Tuple){ + if (rightType->category() != Type::Category::Null) { + auto tt = to(l->valueType()); + m_pusher.makeTuple(tt->components().size()); + } + // optional([mapping|optional]) q = ... + } else if (optValueAsTuple(l->valueType())) { + m_pusher.makeTuple(1); + } + return; + } + + switch (rightType->category()) { + case Type::Category::RationalNumber: + fromRational(leftType, to(rightType)); + break; + case Type::Category::FixedPoint: + fromFixedPoint(leftType, to(rightType)); + break; + case Type::Category::VarInteger: + fromInteger(leftType, &to(rightType)->asIntegerType()); + break; + case Type::Category::Integer: + fromInteger(leftType, to(rightType)); + break; + case Type::Category::FixedBytes: + fromFixedBytesType(leftType, to(rightType)); + break; + case Type::Category::Array: + fromArray(leftType, to(rightType)); + break; + case Type::Category::Optional: + fromOptional(leftType, to(rightType)); + break; + case Type::Category::TvmSlice: + fromSlice(leftType); + break; + case Type::Category::Tuple: + fromTuple(leftType, to(rightType)); + break ; + case Type::Category::StringLiteral: + fromStringLiteral(leftType, to(rightType)); + break; + case Type::Category::Address: + case Type::Category::Bool: + case Type::Category::Contract: + case Type::Category::Enum: + case Type::Category::Function: + case Type::Category::Mapping: + case Type::Category::TvmVector: + case Type::Category::Struct: + case Type::Category::TvmBuilder: + case Type::Category::TvmCell: + case Type::Category::Null: + case Type::Category::EmpyMap: + case Type::Category::UserDefinedValueType: + break; + default: + solUnimplemented(rightType->toString()); + break; + } +} + +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) + return ; + + bigint x = (bigint(1) << leftType->numBits()) - 1; + m_pusher << "PUSHINT " + toString(x) + << "AND"; + + if (leftType->isSigned()) { + m_pusher.startOpaque(); + m_pusher.pushS(0); + m_pusher.pushInt((bigint(1) << (leftType->numBits() - 1)) - 1); + m_pusher << "GREATER"; + m_pusher.startContinuation(); + if (rightType->isSigned()) { + m_pusher.pushInt(bigint(1) << leftType->numBits()); + m_pusher << "SUB"; + } else { + m_pusher.pushInt(-(bigint(1) << leftType->numBits())); + m_pusher << "ADD"; + } + m_pusher.endContinuation(); + m_pusher._if(); + m_pusher.endOpaque(1, 1, true); + } +} + +void TypeConversion::fixedPointToInteger(IntegerType const* leftType, FixedPointType const* rightType) { + int powerDiff = rightType->fractionalDigits(); + if (powerDiff > 0) { + m_pusher.pushInt(MathConsts::power10().at(powerDiff)); + m_pusher << "DIV"; + } + integerToInteger(leftType, rightType->asIntegerType()); +} + +void TypeConversion::fixedPointToFixedPoint(FixedPointType const* leftType, FixedPointType const* rightType) { + int powerDiff = leftType->fractionalDigits() - rightType->fractionalDigits(); + if (powerDiff != 0) { + if (powerDiff > 0) { + m_pusher.pushInt(MathConsts::power10().at(powerDiff)); + m_pusher << "MUL"; // TODO use MULDIVMOD to avoid overflow + } else { + m_pusher.pushInt(MathConsts::power10().at(-powerDiff)); + m_pusher << "DIV"; + } + } + integerToInteger(leftType->asIntegerType(), rightType->asIntegerType()); +} + +void TypeConversion::integerToFixedPoint(FixedPointType const* leftType, IntegerType const* rightType) { + int powerDiff = leftType->fractionalDigits(); + if (powerDiff > 0) { + m_pusher.pushInt(MathConsts::power10().at(powerDiff)); + m_pusher << "MUL"; // TODO use MULDIVMOD to avoid overflow + } + integerToInteger(leftType->asIntegerType(), rightType); +} + +void TypeConversion::fixedBytesToFixedBytes(FixedBytesType const* leftType, FixedBytesType const* rightType) { + int diff = 8 * (leftType->numBytes() - rightType->numBytes()); + if (diff > 0) { + m_pusher << "LSHIFT " + std::to_string(diff); + } else if (diff < 0) { + m_pusher << "RSHIFT " + std::to_string(-diff); + } +} + +void TypeConversion::bytesToFixedBytes(FixedBytesType const* rightType) { + size_t bits = rightType->numBytes() * 8; + m_pusher.startContinuation(); + m_pusher.startOpaque(); + m_pusher << "CTOS"; // slice + m_pusher.pushAsym("LDUQ " + std::to_string(bits)); + // data slice flag + + // if load succeeded drop slice + m_pusher.startContinuation(); + m_pusher.drop(1); + m_pusher.endContinuation(); + + // if load failed load all available data + m_pusher.startContinuation(); + // slice + m_pusher.pushS(0); + m_pusher << "SBITS"; + // slice slice_bits + m_pusher.pushS(0); + // slice slice_bits slice_bits + m_pusher.rotRev(); + // slice_bits slice slice_bits + m_pusher << "PLDUX"; + // slice_bits number + m_pusher.blockSwap(1, 1); + // number slice_bits + m_pusher << "NEGATE"; + m_pusher.pushInt(bits); + m_pusher << "ADD"; + m_pusher << "LSHIFT"; + // number with trailing zeros + m_pusher.endContinuation(); + m_pusher.ifElse(); + m_pusher.endOpaque(1, 1); + m_pusher.pushRefContAndCallX(1, 1, false); +} + +void TypeConversion::stringLiteralToFixedBytes(FixedBytesType const* leftType, StringLiteralType const* rightType) { + size_t bytes = 0; + u256 value = 0; + for (char c : rightType->value()) { + value = value * 256 + c; + ++bytes; + } + while (bytes < leftType->numBytes()) { + value *= 256; + ++bytes; + } + m_pusher.drop(1); // delete old value + m_pusher << "PUSHINT " + toString(value); +} + +void TypeConversion::fromFixedPoint(Type const* leftType, FixedPointType const* rightType) { + switch (leftType->category()) { + case Type::Category::FixedPoint: + fixedPointToFixedPoint(to(leftType), rightType); + break; + case Type::Category::Integer: + fixedPointToInteger(to(leftType), rightType); + break; + case Type::Category::VarInteger: + fixedPointToInteger(&to(leftType)->asIntegerType(), rightType); + break; + default: + solUnimplemented(leftType->toString()); + break; + } +} + +void TypeConversion::convertIntegerToAddress(Type const* t) { + if (auto r = to(t)) { + m_pusher.drop(); + m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(StrUtils::literalToSliceAddress(r->value2()))); + } else { + m_pusher << "NEWC"; + m_pusher << "STSLICECONST x801_"; // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 // 10 0 00000000 1 = 801 + m_pusher << "STU 256"; // address:bits256 + m_pusher << "ENDC"; + m_pusher << "CTOS"; + } +} + +void TypeConversion::convertIntegerToEnum(EnumType const* leftType, IntegerType const* /*rightType*/) { + int const size = leftType->enumDefinition().members().size(); + m_pusher.pushInt(size); + m_pusher << "MOD"; +} + +void TypeConversion::fromInteger(Type const* leftType, IntegerType const* rightType) { + switch (leftType->category()) { + case Type::Category::FixedPoint: + integerToFixedPoint(to(leftType), rightType); + break; + case Type::Category::Integer: + integerToInteger(to(leftType), rightType); + break; + case Type::Category::VarInteger: + integerToInteger(&to(leftType)->asIntegerType(), rightType); + break; + case Type::Category::Function: { + m_pusher.ctx().setPragmaSaveAllFunctions(); + break; + } + case Type::Category::FixedBytes: + // do nothing here + break; + case Type::Category::Address: + case Type::Category::Contract: + convertIntegerToAddress(rightType); + break; + case Type::Category::Enum: + convertIntegerToEnum(to(leftType), rightType); + break; + default: + solUnimplemented(leftType->toString()); + break; + } +} + +void TypeConversion::fromRational(Type const* leftType, RationalNumberType const* rightType) { + switch (leftType->category()) { + case Type::Category::FixedPoint: { + auto fixedPointLeft = to(leftType); + Type const* mob = rightType->mobileType(); + if (auto intRight = to(mob)) { + integerToFixedPoint(fixedPointLeft, intRight); + } else { + auto fixedRight = to(mob); + solAssert(fixedRight, ""); + fixedPointToFixedPoint(fixedPointLeft, fixedRight); + } + break; + } + case Type::Category::Integer: + case Type::Category::VarInteger: + break; + case Type::Category::Function: { + m_pusher.ctx().setPragmaSaveAllFunctions(); + break; + } + case Type::Category::Enum: + case Type::Category::FixedBytes: + // do nothing here + break; + case Type::Category::Address: + case Type::Category::Contract: + convertIntegerToAddress(rightType); + break; + default: + solUnimplemented(leftType->toString()); + break; + } +} + +void TypeConversion::tupleFromTuple(TupleType const* leftType, TupleType const* rightType) { + std::vector const& lc = leftType->components(); + std::vector const& rc = rightType->components(); + solAssert(lc.size() == rc.size(), ""); + int n = lc.size(); + for (int i = n - 1; 0 <= i; --i) { + convert(lc.at(i), rc.at(i)); + if (n >= 2) { + m_pusher.blockSwap(n - 1, 1); + } + } +} + +void TypeConversion::fromFixedBytesType(Type const* leftType, FixedBytesType const* rightType) { + switch (leftType->category()) { + case Type::Category::Address: { + convertIntegerToAddress(rightType); + break; + } + case Type::Category::FixedBytes: { + fixedBytesToFixedBytes(to(leftType), rightType); + break; + } + case Type::Category::Integer: { + auto intType = to(leftType); + if (intType && !intType->isSigned() && + (intType->numBits() >= 8 * rightType->numBytes())) + break; + solUnimplemented(""); + break; + } + case Type::Category::FixedPoint: { + auto fixedPoint = to(leftType); + integerToInteger(fixedPoint->asIntegerType(), TypeProvider::uint(8 * rightType->numBytes())); + break; + } + case Type::Category::Array: { + auto stringType = to(leftType); + solAssert(stringType->isByteArrayOrString(), ""); + m_pusher << "NEWC"; + m_pusher << "STU " + toString(8 * rightType->numBytes()); + m_pusher << "ENDC"; + break; + } + default: + solUnimplemented(leftType->toString()); + break; + } +} + +void TypeConversion::fromArray(Type const* leftType, ArrayType const* rightType) { + auto r = to(rightType); + if (!r->isByteArrayOrString()) { + return; + } + // bytes or string + switch (leftType->category()) { + case Type::Category::FixedBytes: + bytesToFixedBytes(to(leftType)); + break; + case Type::Category::Array: + break; + case Type::Category::TvmSlice: + m_pusher << "CTOS"; + break; + default: + solUnimplemented(""); + break; + } +} + +void TypeConversion::fromOptional(Type const* leftType, OptionalType const* rightType) { + switch (leftType->category()) { + case Type::Category::Optional: { + auto l = to(leftType); + m_pusher.startOpaque(); + + m_pusher.pushS(0); + m_pusher << "ISNULL"; + m_pusher.fixStack(-1); // fix stack + + m_pusher.startContinuation(); + if (optValueAsTuple(l->valueType())) { + m_pusher.untuple(1); + } else if (auto tt = to(l->valueType())) { + m_pusher.untuple(tt->components().size()); + } + convert(l->valueType(), rightType->valueType()); + if (optValueAsTuple(l->valueType())) { + m_pusher.makeTuple(1); + } else if (auto tt = to(l->valueType())) { + m_pusher.makeTuple(tt->components().size()); + } + m_pusher.endContinuation(); + m_pusher.ifNot(); + + m_pusher.endOpaque(1, 1, true); + break; + } + default: + break; + } +} + +void TypeConversion::fromSlice(Type const* leftType) { + switch (leftType->category()) { + case Type::Category::TvmSlice: + break; + case Type::Category::Array: { + auto arrType = to(leftType); + solAssert(arrType->isByteArrayOrString(), ""); + m_pusher << "NEWC" // s b + << "STSLICE" // b' + << "ENDC"; // cell + break; + } + default: + solUnimplemented(""); + } +} + +void TypeConversion::fromTuple(Type const* leftType, TupleType const* rightType) { + switch (leftType->category()) { + case Type::Category::Tuple: + tupleFromTuple(to(leftType), rightType); + break; + default: + solUnimplemented(leftType->toString()); + break; + } +} + +void TypeConversion::fromStringLiteral(Type const* leftType, StringLiteralType const* rightType) { + switch (leftType->category()) { + case Type::Category::FixedBytes: + stringLiteralToFixedBytes(to(leftType), rightType); + break; + case Type::Category::Array: + break; + default: + solUnimplemented(leftType->toString()); + break; + } +} diff --git a/compiler/libsolidity/codegen/TVMPusher.hpp b/compiler/libsolidity/codegen/TVMPusher.hpp index 95b886f9..1880fb08 100644 --- a/compiler/libsolidity/codegen/TVMPusher.hpp +++ b/compiler/libsolidity/codegen/TVMPusher.hpp @@ -54,6 +54,30 @@ class InherHelper { std::set m_baseFunctions; }; +class FunctionCallGraph : private boost::noncopyable { +public: + // Adds the edge and returns true if the edge doesn't create a loop + bool tryToAddEdge(std::string const& _v, std::string const& _to); + std::vector DAG(); + void addPrivateFunction(uint32_t id, std::string name) { + if (m_privcateFuncs.count(id)) + solAssert(m_privcateFuncs.at(id) == name); + else + m_privcateFuncs.emplace(id, name); + } + std::map privateFunctions() const { return m_privcateFuncs; } +private: + bool dfs(std::string const& _v); +private: + std::map> m_graph; + enum class Color { + White, Red, Black + }; + std::map m_color; + std::vector m_order; + std::map m_privcateFuncs; +}; + class TVMCompilerContext { public: TVMCompilerContext(ContractDefinition const* contract, PragmaDirectiveHelper const& pragmaHelper); @@ -73,15 +97,23 @@ class TVMCompilerContext { bool storeTimestampInC4() const; int getOffsetC4() const; std::vector> getStaticVariables() const; - void setCurrentFunction(FunctionDefinition const* f) { m_currentFunction = f; } - FunctionDefinition const* getCurrentFunction() { return m_currentFunction; } + void setCurrentFunction(FunctionDefinition const* _f, std::string _name) { + solAssert(m_currentFunction == nullptr && !m_currentFunctionName.has_value(), ""); + m_currentFunction = _f; + m_currentFunctionName = _name; + } + FunctionDefinition const* currentFunction() { return m_currentFunction; } + std::string currentFunctionName() { return m_currentFunctionName.value(); } + void resetCurrentFunction() { + m_currentFunction = nullptr; + m_currentFunctionName.reset(); + } void addInlineFunction(const std::string& name, Pointer body); Pointer getInlinedFunction(const std::string& name); void addPublicFunction(uint32_t functionId, const std::string& functionName); const std::vector>& getPublicFunctions(); - bool addAndDoesHaveLoop(FunctionDefinition const* _v, FunctionDefinition const* _to); - bool dfs(FunctionDefinition const* v); + FunctionCallGraph& callGraph() { return m_callGraph; } bool isFallBackGenerated() const { return m_isFallBackGenerated; } void setIsFallBackGenerated() { m_isFallBackGenerated = true; } bool isReceiveGenerated() const { return m_isReceiveGenerated; } @@ -105,19 +137,20 @@ class TVMCompilerContext { bool getPragmaSaveAllFunctions() const { return m_pragmaSaveAllFunctions; } void setPragmaSaveAllFunctions() { m_pragmaSaveAllFunctions = true; } + void startBlock(bool uncheckedBlock) { m_isUncheckedBlock.push(uncheckedBlock); } + void endBlock() { m_isUncheckedBlock.pop(); } + private: // TODO split to several classes ContractDefinition const* m_contract{}; bool ignoreIntOverflow{}; + std::stack m_isUncheckedBlock; PragmaDirectiveHelper const& m_pragmaHelper; std::map m_stateVarIndex; FunctionDefinition const* m_currentFunction{}; + std::optional m_currentFunctionName; std::map> m_inlinedFunctions; - std::map> graph; - enum class Color { - White, Red, Black - }; - std::map color; + FunctionCallGraph m_callGraph; std::vector> m_publicFunctions; bool m_isFallBackGenerated{}; bool m_isReceiveGenerated{}; @@ -141,7 +174,7 @@ class StackPusher { return ret; } - void pushInlineFunction(const Pointer& block, int take, int ret); + void pushInlineFunction(std::string const& name, int take, int ret); void pollLastRetOpcode(); bool tryPollEmptyPushCont(); @@ -170,7 +203,7 @@ class StackPusher { void pushCellOrSlice(const Pointer& opcode); public: void pushSlice(std::string const& data); - void pushPrivateFunctionId(FunctionDefinition const& funDef); + void pushPrivateFunctionId(FunctionDefinition const& funDef, bool isCalledByPoint); void startContinuation(); private: void endCont(CodeBlock::Type type); @@ -221,7 +254,7 @@ class StackPusher { void indexNoexcep(int index); void setIndex(int index); void setIndexQ(int index); - void tuple(int qty); + void makeTuple(int qty); void resetAllStateVars(); void getGlob(VariableDeclaration const * vd); void getGlob(int index); @@ -252,16 +285,13 @@ class StackPusher { void store(const Type *type, bool reverse); void pushZeroAddress(); - Pointer generateC7ToT4Macro(bool forAwait); - - static bigint pow10(int power); - - void hardConvert(Type const *leftType, Type const *rightType); + Pointer generateC7ToC4(bool forAwait); + void convert(Type const *leftType, Type const *rightType); void checkFit(Type const *type); void pushParameter(std::vector> const& params); - void pushMacroCallInCallRef(int take, int ret, const std::string& fname); - void pushCallOrCallRef(const std::string& functionName, FunctionType const* ft, const std::optional>& deltaStack = std::nullopt); - void pushMacro(int take, int ret, const std::string& functionName); + void pushFragmentInCallRef(int take, int ret, const std::string& fname); + void pushCallOrCallRef(FunctionDefinition const* _functionDef, const std::optional>& deltaStack, bool isCalledByPoint); + void pushFragment(int take, int ret, const std::string& functionName); void computeConstCell(std::string const& expName); void computeConstSlice(std::string const& expName); void drop(int cnt = 1); @@ -278,7 +308,7 @@ class StackPusher { int ext_msg_info(const std::set &isParamOnStack, bool isOut); void appendToBuilder(const std::string& bitString); void checkOptionalValue(); - bool doesFitInOneCellAndHaveNoStruct(Type const* key, Type const* value); + static bool doesFitInOneCellAndHaveNoStruct(Type const* key, Type const* value); [[nodiscard]] DataType prepareValueForDictOperations(Type const* keyType, Type const* valueType); [[nodiscard]] @@ -301,7 +331,6 @@ class StackPusher { bool saveOrigKeyAndNoTuple = false ); static Type const* parseIndexType(Type const* type); - static Type const* parseValueType(IndexAccess const& indexAccess); void setDict( Type const &keyType, @@ -375,4 +404,32 @@ class StackPusher { TVMCompilerContext* m_ctx{}; }; // end StackPusher +class TypeConversion { +public: + TypeConversion(StackPusher& _pusher) : m_pusher{_pusher} { } + void convert(Type const* leftType, Type const* rightType); +private: + void integerToInteger(IntegerType const* leftType, IntegerType const* rightType); + void fixedPointToInteger(IntegerType const* leftType, FixedPointType const* rightType); + void fixedPointToFixedPoint(FixedPointType const* leftType, FixedPointType const* rightType); + void integerToFixedPoint(FixedPointType const* leftType, IntegerType const* rightType); + void fixedBytesToFixedBytes(FixedBytesType const* leftType, FixedBytesType const* rightType); + void bytesToFixedBytes(FixedBytesType const* rightType); + void stringLiteralToFixedBytes(FixedBytesType const* leftType, StringLiteralType const* rightType); + void fromFixedPoint(Type const* leftType, FixedPointType const* rightType); + void convertIntegerToAddress(Type const* t); + void convertIntegerToEnum(EnumType const* leftType, IntegerType const* rightType); + void fromInteger(Type const* leftType, IntegerType const* rightType); + void fromRational(Type const* leftType, RationalNumberType const* rightType); + void tupleFromTuple(TupleType const* leftType, TupleType const* rightType); + void fromFixedBytesType(Type const* leftType, FixedBytesType const* rightType); + void fromArray(Type const* leftType, ArrayType const* rightType); + void fromOptional(Type const* leftType, OptionalType const* rightType); + void fromSlice(Type const* leftType); + void fromTuple(Type const* leftType, TupleType const* rightType); + void fromStringLiteral(Type const* leftType, StringLiteralType const* rightType); +private: + StackPusher& m_pusher; +}; // end TypeConversion + } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMStructCompiler.cpp b/compiler/libsolidity/codegen/TVMStructCompiler.cpp index 5eb2d59a..5b4d9398 100644 --- a/compiler/libsolidity/codegen/TVMStructCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMStructCompiler.cpp @@ -77,7 +77,7 @@ void StructCompiler::createDefaultStruct() { for (Type const *type: m_types) { pusher->pushDefaultValue(type); } - pusher->tuple(m_types.size()); + pusher->makeTuple(m_types.size()); } void StructCompiler::createDefaultStructAsCell() { @@ -131,7 +131,7 @@ void StructCompiler::structConstructor( } } - pusher->tuple(m_types.size()); + pusher->makeTuple(m_types.size()); } void StructCompiler::tupleToBuilder() { @@ -162,7 +162,7 @@ void StructCompiler::convertSliceToTuple() { ChainDataDecoder decoder{pusher}; decoder.decodeData(0, 0, m_types); - pusher->tuple(m_types.size()); + pusher->makeTuple(m_types.size()); solAssert(ss == pusher->stackSize(), ""); } diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.cpp b/compiler/libsolidity/codegen/TVMTypeChecker.cpp index d642a0ce..cea3b3c7 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.cpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.cpp @@ -52,7 +52,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { std::set bf2 = getAllBaseFunctions(f2); if (bf.count(f2) == 0 && bf2.count(f) == 0) { m_errorReporter.typeError( - 228_error, + 5042_error, f->location(), SecondarySourceLocation().append("Declaration of the function with the same function ID: ", funcId2Decl.at(id)->location()), "Two functions have the same functionID."); @@ -74,13 +74,13 @@ void TVMTypeChecker::checkOverrideAndOverload() { overridedFunctions.insert(base); if ((!f->functionID().has_value() && baseFunction->functionID()) || (f->functionID().has_value() && !baseFunction->functionID())) { m_errorReporter.typeError( - 228_error, + 2070_error, f->location(), SecondarySourceLocation().append("Declaration of the base function: ", baseFunction->location()), "Both override and base functions should have functionID if it is defined for one of them."); } else if (f->functionID().has_value() && f->functionID() != baseFunction->functionID()) { m_errorReporter.typeError( - 228_error, + 7277_error, f->location(), SecondarySourceLocation().append("Declaration of the base function: ", baseFunction->location()), "Override function should have functionID = " + toString(baseFunction->functionID().value()) + "."); @@ -88,7 +88,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { if (baseFunction->isResponsible() != f->isResponsible()) { m_errorReporter.typeError( - 228_error, + 9069_error, f->location(), SecondarySourceLocation().append("Declaration of the base function: ", baseFunction->location()), "Both override and base functions should be marked as responsible or not"); @@ -96,7 +96,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { if ((!f->functionID().has_value() && baseFunction->functionID()) || (f->functionID().has_value() && !baseFunction->functionID())) { m_errorReporter.typeError( - 228_error, + 4142_error, f->location(), SecondarySourceLocation().append("Declaration of the base function: ", baseFunction->location()), @@ -105,7 +105,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { if ((f->isInternalMsg() ^ baseFunction->isInternalMsg()) || (f->isExternalMsg() ^ baseFunction->isExternalMsg())) { m_errorReporter.typeError( - 228_error, + 8096_error, f->location(), SecondarySourceLocation().append("Declaration of the base function: ", baseFunction->location()), @@ -136,7 +136,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { if (f->name() == ff->name()) { if (used.count(std::make_pair(f, ff)) == 0) { m_errorReporter.typeError( - 228_error, + 3994_error, f->location(), SecondarySourceLocation().append("Another overloaded function is here:", ff->location()), "Function overloading is not supported for public functions."); @@ -151,16 +151,16 @@ void TVMTypeChecker::checkOverrideAndOverload() { void TVMTypeChecker::check_onCodeUpgrade(FunctionDefinition const& f) { const std::string s = "\nfunction onCodeUpgrade(...) (internal|private) { /*...*/ }"; if (!f.returnParameters().empty()) { - m_errorReporter.typeError(228_error, f.returnParameters().at(0)->location(), "Function mustn't return any parameters. Expected function signature:" + s); + m_errorReporter.typeError(5078_error, f.returnParameters().at(0)->location(), "Function mustn't return any parameters. Expected function signature:" + s); } if (f.isPublic()) { - m_errorReporter.typeError(228_error, f.location(), "Bad function visibility. Expected function signature:" + s); + m_errorReporter.typeError(8861_error, f.location(), "Bad function visibility. Expected function signature:" + s); } } bool TVMTypeChecker::visit(TryStatement const& _tryStatement) { if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _tryStatement.location(), + m_errorReporter.typeError(5512_error, _tryStatement.location(), "\"try-catch\"" + isNotSupportedVM); } return true; @@ -168,7 +168,7 @@ bool TVMTypeChecker::visit(TryStatement const& _tryStatement) { bool TVMTypeChecker::visit(VariableDeclaration const& _node) { if (_node.isStateVariable() && _node.type()->category() == Type::Category::TvmSlice) { - m_errorReporter.typeError(228_error, _node.location(), "This type can't be used for state variables."); + m_errorReporter.typeError(9191_error, _node.location(), "This type can't be used for state variables."); } return true; } @@ -183,7 +183,7 @@ bool TVMTypeChecker::visit(const Mapping &_mapping) { TypeInfo ti {member->type()}; if (!ti.isNumeric) { m_errorReporter.typeError( - 228_error, + 4522_error, _mapping.keyType().location(), SecondarySourceLocation().append("Bad field: ", member->location()), "If struct type is used as a key type for mapping, then " @@ -193,7 +193,7 @@ bool TVMTypeChecker::visit(const Mapping &_mapping) { bitLength += ti.numBits; } if (bitLength > TvmConst::CellBitLength) { - m_errorReporter.typeError(228_error, _mapping.keyType().location(), "If struct type is used as a key type for mapping, then " + m_errorReporter.typeError(6614_error, _mapping.keyType().location(), "If struct type is used as a key type for mapping, then " "struct must fit in " + toString(TvmConst::CellBitLength) + " bits"); } } @@ -204,18 +204,18 @@ bool TVMTypeChecker::visit(const Mapping &_mapping) { bool TVMTypeChecker::visit(const FunctionDefinition &f) { if (f.functionID().has_value()) { if (f.functionID().value() == 0) { - m_errorReporter.typeError(228_error, f.location(), "functionID can't be equal to zero because this value is reserved for receive function."); + m_errorReporter.typeError(8746_error, f.location(), "functionID can't be equal to zero because this value is reserved for receive function."); } if (!f.functionIsExternallyVisible() && f.name() != "onCodeUpgrade") { - m_errorReporter.typeError(228_error, f.location(), "Only public/external functions and function `onCodeUpgrade` can have functionID."); + m_errorReporter.typeError(2239_error, f.location(), "Only public/external functions and function `onCodeUpgrade` can have functionID."); } if (f.isReceive() || f.isFallback() || f.isOnTickTock() || f.isOnBounce()) { - m_errorReporter.typeError(228_error, f.location(), "functionID isn't supported for receive, fallback, onBounce and onTickTock functions."); + m_errorReporter.typeError(1482_error, f.location(), "functionID isn't supported for receive, fallback, onBounce and onTickTock functions."); } } if (f.isInline() && f.isPublic()) { - m_errorReporter.typeError(228_error, f.location(), "Inline function should have private or internal visibility"); + m_errorReporter.typeError(2580_error, f.location(), "Inline function should have private or internal visibility"); } if (f.name() == "onCodeUpgrade") { check_onCodeUpgrade(f); @@ -228,23 +228,23 @@ bool TVMTypeChecker::visit(const FunctionDefinition &f) { f.parameters().at(0)->type()->category() != Type::Category::TvmSlice || f.parameters().at(1)->type()->category() != Type::Category::TvmCell ) { - m_errorReporter.typeError(228_error, f.location(), + m_errorReporter.typeError(8963_error, f.location(), "Unexpected function parameters." + s); } if (f.returnParameters().size() != 1 || f.returnParameters().at(0)->type()->category() != Type::Category::TvmSlice) { - m_errorReporter.typeError(228_error, f.location(), "Should return TvmSlice." + s); + m_errorReporter.typeError(7293_error, f.location(), "Should return TvmSlice." + s); } if (f.visibility() != Visibility::Private) { - m_errorReporter.typeError(228_error, f.location(), "Should be marked as private." + s); + m_errorReporter.typeError(3640_error, f.location(), "Should be marked as private." + s); } if (!f.isInline()) { - m_errorReporter.typeError(228_error, f.location(), "Should be marked as inline." + s); + m_errorReporter.typeError(8418_error, f.location(), "Should be marked as inline." + s); } } if (!f.isFree() && f.isInlineAssembly()) { - m_errorReporter.typeError(228_error, f.location(), "Only free functions can be marked as \"assembly\"."); + m_errorReporter.typeError(7229_error, f.location(), "Only free functions can be marked as \"assembly\"."); } return true; @@ -254,7 +254,7 @@ bool TVMTypeChecker::visit(IndexRangeAccess const& indexRangeAccess) { Type const *baseType = indexRangeAccess.baseExpression().annotation().type; auto baseArrayType = to(baseType); if (baseType->category() != Type::Category::Array || !baseArrayType->isByteArrayOrString()) { - m_errorReporter.typeError(228_error, indexRangeAccess.location(), "Index range access is available only for bytes."); + m_errorReporter.typeError(4884_error, indexRangeAccess.location(), "Index range access is available only for bytes."); } return true; } @@ -268,64 +268,64 @@ void TVMTypeChecker::checkDeprecation(FunctionCall const& _functionCall) { auto functionType = to(expressionType); switch (functionType->kind()) { case FunctionType::Kind::OptionalReset: - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(5380_error, _functionCall.location(), "\".reset()\" is deprecated. Use \"delete ;\"."); break; case FunctionType::Kind::TVMSliceLoad: if (memberName == "decode") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(9518_error, _functionCall.location(), "\".decode()\" is deprecated. Use \".load()\""); } break; case FunctionType::Kind::TVMSliceLoadQ: if (memberName == "decodeQ") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(6501_error, _functionCall.location(), "\".decodeQ()\" is deprecated. Use \".loadQ()\""); } break; case FunctionType::Kind::TVMSliceLoadFunctionParams: if (memberName == "decodeFunctionParams") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(9789_error, _functionCall.location(), "\".decodeFunctionParams()\" is deprecated. Use \".loadFunctionParams()\""); } break; case FunctionType::Kind::TVMSliceLoadStateVars: if (memberName == "decodeStateVars") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(3738_error, _functionCall.location(), "\".decodeStateVars()\" is deprecated. Use \".loadStateVars()\""); } break; case FunctionType::Kind::TVMSliceLoadUint: if (memberName == "loadUnsigned") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(5093_error, _functionCall.location(), "\".loadUnsigned()\" is deprecated. Use \".loadUint()\""); } break; case FunctionType::Kind::TVMSliceLoadInt: if (memberName == "loadSigned") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(1581_error, _functionCall.location(), "\".loadSigned()\" is deprecated. Use \".loadInt()\""); } break; case FunctionType::Kind::TVMBuilderStoreUint: if (memberName == "storeUnsigned") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(1214_error, _functionCall.location(), "\".storeUnsigned()\" is deprecated. Use \".storeUint()\""); } break; case FunctionType::Kind::TVMBuilderStoreInt: if (memberName == "storeSigned") { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(9509_error, _functionCall.location(), "\".storeSigned()\" is deprecated. Use \".storeInt()\""); } break; case FunctionType::Kind::ByteToSlice: { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(9791_error, _functionCall.location(), "\".toSlice()\" is deprecated. Use explicit conversion: \"TvmSlice()\""); break; } case FunctionType::Kind::StringToSlice: { - m_errorReporter.warning(228_error, _functionCall.location(), + m_errorReporter.warning(6953_error, _functionCall.location(), "\".toSlice()\" is deprecated. Use explicit conversion: \"TvmSlice()\""); break; } @@ -347,19 +347,19 @@ void TVMTypeChecker::checkSupport(FunctionCall const& _functionCall) { switch (functionType->kind()) { case FunctionType::Kind::GasLeft: if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _functionCall.location(), + m_errorReporter.typeError(5434_error, _functionCall.location(), "\"gasleft()\"" + isNotSupportedVM); } break; case FunctionType::Kind::TVMInitCodeHash: if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _functionCall.location(), + m_errorReporter.typeError(4649_error, _functionCall.location(), "\"tvm.initCodeHash()\"" + isNotSupportedVM); } break; case FunctionType::Kind::TVMCode: if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _functionCall.location(), + m_errorReporter.typeError(7632_error, _functionCall.location(), "\"tvm.code()\"" + isNotSupportedVM); } break; @@ -373,7 +373,7 @@ void TVMTypeChecker::checkSupport(FunctionCall const& _functionCall) { } if (_functionCall.isAwait() && *GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _functionCall.location(), + m_errorReporter.typeError(5601_error, _functionCall.location(), "\"*.await\"" + isNotSupportedVM); } } @@ -393,13 +393,27 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { if (fd && fd->name() == "onCodeUpgrade") { if (m_inherHelper->isBaseFunction(fd)) { m_errorReporter.typeError( - 228_error, _functionCall.location(), + 7993_error, _functionCall.location(), "It is forbidden to call base functions of \"onCodeUpgrade\"."); } } } switch (functionType->kind()) { + case FunctionType::Kind::TVMBuilderStore: { + for (ASTPointer const& arg : arguments) { + if (auto structType = to(arg->annotation().type)) { + ABITypeSize size{structType}; + if (size.maxBits > 1023 || size.maxRefs > 4) + m_errorReporter.warning( + 228_error, arg->location(), + "The structure may not fit to the builder." + " Store manually structure's members to several builders." + ); + } + } + break; + } case FunctionType::Kind::TVMSliceLoadInt: case FunctionType::Kind::TVMSliceLoadIntQ: case FunctionType::Kind::TVMSliceLoadUint: @@ -411,7 +425,7 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { const auto& value = ExprUtils::constValue(*arguments.at(0)); if (value.has_value()) { if (value > 256) { - m_errorReporter.syntaxError(228_error, arguments.at(0)->location(), + m_errorReporter.syntaxError(8838_error, arguments.at(0)->location(), "Too big value. The value must be in the range 0 - 256."); } } @@ -432,7 +446,7 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { bool TVMTypeChecker::visit(PragmaDirective const& _pragma) { if (!_pragma.literals().empty()) { if (_pragma.literals().at(0) == "copyleft" && *GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _pragma.location(), + m_errorReporter.typeError(9186_error, _pragma.location(), "\"pragma copyleft ...\"" + isNotSupportedVM); } } @@ -448,12 +462,12 @@ bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { switch (magicType->kind()) { case MagicType::Kind::Transaction: { if (member == "timestamp") { - m_errorReporter.warning(228_error, _memberAccess.location(), + m_errorReporter.warning(6736_error, _memberAccess.location(), R"("tx.timestamp" is deprecated. Use "tx.logicaltime".)"); } if (member == "storageFee") { if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _memberAccess.location(), + m_errorReporter.typeError(3428_error, _memberAccess.location(), "\"tx.storageFee\"" + isNotSupportedVM); } } @@ -461,7 +475,7 @@ bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { } case MagicType::Kind::Gosh: { if (*GlobalParams::g_tvmVersion != TVMVersion::gosh()) { - m_errorReporter.typeError(228_error, _memberAccess.location(), + m_errorReporter.typeError(4065_error, _memberAccess.location(), "\"gosh." + member + "\"" + isNotSupportedVM); } break; diff --git a/compiler/libsolidity/codegen/TvmAst.cpp b/compiler/libsolidity/codegen/TvmAst.cpp index 6324923d..ed62fb07 100644 --- a/compiler/libsolidity/codegen/TvmAst.cpp +++ b/compiler/libsolidity/codegen/TvmAst.cpp @@ -293,8 +293,12 @@ std::string PushCellOrSlice::chainBlob() const { string s; PushCellOrSlice const* p = this; while (p != nullptr) { - solAssert(p->blob().at(0) == 'x'); - s += p->blob().substr(1); + if (p->blob().empty()) { + solAssert(p->child() == nullptr, ""); + } else { + solAssert(p->blob().at(0) == 'x'); + s += p->blob().substr(1); + } p = p->child().get(); } return s; @@ -429,16 +433,16 @@ void TryCatch::accept(TvmAstVisitor& _visitor) { } } -Function::Function(int take, int ret, std::string name, Function::FunctionType type, Pointer block, - const FunctionDefinition *_function) : - m_take{take}, - m_ret{ret}, - m_name{std::move(name)}, - m_type{type}, - m_block{std::move(block)}, - m_function{_function} +Function::Function(int take, int ret, std::string name, std::optional _functionId, + Function::FunctionType type, Pointer block, const FunctionDefinition *_function) : + m_take{take}, + m_ret{ret}, + m_name{std::move(name)}, + m_functionId{_functionId}, + m_type{type}, + m_block{std::move(block)}, + m_function{_function} { - solAssert(type != FunctionType::PrivateFunction || _function != nullptr, ""); } void Function::accept(TvmAstVisitor& _visitor) { @@ -457,10 +461,6 @@ void Contract::accept(TvmAstVisitor& _visitor) { } } -std::vector>& Contract::functions() { - return m_functions; -} - namespace solidity::frontend { Pointer gen(const std::string& cmd) { std::string op; @@ -535,7 +535,6 @@ Pointer gen(const std::string& cmd) { {"NOW", {0, 1, true}}, {"NULL", {0, 1, true}}, {"PUSHINT", {0, 1, true}}, - {"PUSHPOW2DEC", {0, 1, true}}, {"RANDSEED", {0, 1, true}}, {"RANDU256", {0, 1}}, {"STORAGEFEE", {0, 1, true}}, @@ -619,6 +618,7 @@ Pointer gen(const std::string& cmd) { {"UBITSIZE", {1, 1}}, {"UFITS", {1, 1}}, {"UNZIP", {1, 1}}, + {"XLOAD", {1, 1}}, {"ZIP", {1, 1}}, {"BBITREFS", {1, 2, true}}, @@ -644,6 +644,8 @@ Pointer gen(const std::string& cmd) { {"REWRITESTDADDR", {1, 2}}, {"SBITREFS", {1, 2, true}}, {"TPOP", {1, 2}}, + {"XCTOS", {1, 2}}, + {"XLOADQ", {1, 2}}, {"COPYLEFT", {2, 0}}, {"RAWRESERVE", {2, 0}}, @@ -665,6 +667,7 @@ Pointer gen(const std::string& cmd) { {"DIV", {2, 1}}, {"DIVC", {2, 1}}, {"DIVR", {2, 1}}, + {"ENDXC", {2, 1}}, {"EQUAL", {2, 1, true}}, {"GEQ", {2, 1, true}}, {"GREATER", {2, 1, true}}, diff --git a/compiler/libsolidity/codegen/TvmAst.hpp b/compiler/libsolidity/codegen/TvmAst.hpp index dab915a2..67142a62 100644 --- a/compiler/libsolidity/codegen/TvmAst.hpp +++ b/compiler/libsolidity/codegen/TvmAst.hpp @@ -31,517 +31,538 @@ using Pointer = std::shared_ptr; namespace solidity::frontend { - class TvmAstVisitor; +class TvmAstVisitor; - template - Pointer createNode(Args&& ... _args) - { - return std::make_shared(std::forward(_args)...); - } - - class TvmAstNode : private boost::noncopyable, public std::enable_shared_from_this { - public: - virtual ~TvmAstNode() = default; - virtual void accept(TvmAstVisitor& _visitor) = 0; - virtual bool operator==(TvmAstNode const& _node) const = 0; - }; - - class Loc : public TvmAstNode { - public: - explicit Loc(std::string _file, int _line) : m_file{std::move(_file)}, m_line{_line} { } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const& n) const override; - std::string const& file() const { return m_file; } - int line() const { return m_line; } - private: - std::string m_file; - int m_line; - }; - - class Stack : public TvmAstNode { - public: - enum class Opcode { - DROP, - BLKDROP2, // BLKDROP2 1, 1 - POP_S, // POP_S 1 - - BLKPUSH, // BLKPUSH 1, i | BLKPUSH 3, i | BLKPUSH 2, 1 (DUP2) | BLKPUSH 3, 1 (OVER2) - PUSH2_S, // | | PUSH2 S1, S0 | PUSH2 S3, S2 - PUSH3_S, // | PUSH3 Si, Si-1, Si-2 | | - PUSH_S, // PUSH Si | | | - - BLKSWAP, // BLKSWAP 1, 1 | - REVERSE, // REVERSE 2, 0 | REVERSE 3, 0 - XCHG, // XCHG S0 S1 | XCHG S0 S2, - - TUCK, - PUXC, - XCPU, - }; - explicit Stack(Opcode opcode, int i = -1, int j = -1, int k = -1); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const& _node) const override; - Opcode opcode() const { return m_opcode; } - int i() const { return m_i; } - int j() const { return m_j; } - int k() const { return m_k; } - private: - Opcode m_opcode{}; - int m_i{-1}; - int m_j{-1}; - int m_k{-1}; - }; - - // abstract - class Gen : public TvmAstNode { - public: - explicit Gen(bool _isPure) : m_isPure{_isPure} {} - virtual int take() const = 0; - virtual int ret() const = 0; - bool isPure() const { return m_isPure; } - private: - bool m_isPure{}; // it doesn't throw exception, has no side effects (doesn't change any GLOB vars) - }; - - class Glob : public Gen { - public: - enum class Opcode { - GetOrGetVar, - SetOrSetVar, - - PUSHROOT, - POPROOT, - - PUSH_C3, - POP_C3, - - PUSH_C7, - POP_C7, - }; - explicit Glob(Opcode opcode, int index = -1); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - Opcode opcode() const { return m_opcode; } - int index() const { return m_index; } - int take() const override; - int ret() const override; - private: - Opcode m_opcode{}; - int m_index{-1}; - }; - - class CodeBlock; - - class DeclRetFlag : public TvmAstNode { - public: - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - }; - - class Opaque : public Gen { - public: - explicit Opaque(Pointer _block, int take, int ret, bool isPure) : - Gen{isPure}, m_block(std::move(_block)), m_take{take}, m_ret{ret} {} - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - Pointer const& block() const { return m_block; } - int take() const override { return m_take; } - int ret() const override { return m_ret; } - private: - Pointer m_block; - int m_take{}; - int m_ret{}; - }; - - class AsymGen : public TvmAstNode { - public: - explicit AsymGen(std::string opcode); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - std::string const& opcode() const { return m_opcode; } - private: - std::string m_opcode; - }; - - class HardCode : public Gen { - public: - explicit HardCode(std::vector code, int take, int ret, bool _isPure) : - Gen{_isPure}, m_code(std::move(code)), m_take{take}, m_ret{ret} {} - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - std::vector const& code() const { return m_code; } - int take() const override { return m_take; } - int ret() const override { return m_ret; } - private: - std::vector m_code; - int m_take{}; - int m_ret{}; - }; - - class GenOpcode : public Gen { - public: - explicit GenOpcode(const std::string& opcode, int take, int ret, bool _isPure = false); - void accept(TvmAstVisitor& _visitor) override; - std::string fullOpcode() const; - std::string const &opcode() const { return m_opcode; } - std::string const &arg() const { return m_arg; } - std::string const &comment() const { return m_comment; } - int take() const override { return m_take; } - int ret() const override { return m_ret; } - bool operator==(TvmAstNode const& _node) const override; - private: - std::string m_opcode; - std::string m_arg; - std::string m_comment; - int m_take{}; - int m_ret{}; - }; - - class TvmReturn : public TvmAstNode { - public: - TvmReturn(bool _withIf, bool _withNot, bool _withAlt); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - bool withIf() const { return m_withIf; } - bool withNot() const { return m_withNot; } - bool withAlt() const { return m_withAlt; } - private: - bool m_withIf{}; - bool m_withNot{}; - bool m_withAlt{}; - }; - - class ReturnOrBreakOrCont : public TvmAstNode { - public: - explicit ReturnOrBreakOrCont(int _take, Pointer const &body) : - m_take{_take}, - m_body{body} - { - } - Pointer const &body() const { return m_body; } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - int take() const { return m_take; } - private: - int m_take{}; - Pointer m_body; - }; - - class TvmException : public TvmAstNode { - public: - explicit TvmException(bool _arg, bool _any, bool _if, bool _not, std::string _param) : - m_arg{_arg}, - m_any{_any}, - m_if{_if}, - m_not{_not}, - m_param{std::move(_param)} - { - } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - std::string opcode() const; - std::string const& arg() const { return m_param; } - int take() const; - bool withArg() const { return m_arg; } - bool withAny() const { return m_any; } - bool withIf() const { return m_if; } - bool withNot() const { return m_not; } - private: - bool m_arg{}; - bool m_any{}; - bool m_if{}; - bool m_not{}; - std::string m_param; - }; - - class PushCellOrSlice : public Gen { - public: - enum class Type { - PUSHREF_COMPUTE, - PUSHREFSLICE_COMPUTE, - PUSHREF, - PUSHREFSLICE, - CELL, - PUSHSLICE - }; - PushCellOrSlice(Type type, std::string blob, Pointer child) : - Gen{true}, // we don't execute data - m_type{type}, - m_blob{std::move(blob)}, - m_child{std::move(child)} - { - } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - bool operator<(TvmAstNode const&) const; - int take() const override { return 0; } - int ret() const override { return 1; } - Type type() const { return m_type; } - std::string const &blob() const { return m_blob; } - std::string chainBlob() const; - Pointer child() const { return m_child; } - void updToRef(); - private: - Type m_type; - std::string m_blob; - Pointer m_child; - }; - - class CodeBlock : public TvmAstNode { - public: - enum class Type { - None, - PUSHCONT, - PUSHREFCONT, - }; - static std::string toString(Type t); - CodeBlock(Type type, std::vector> instructions = {}) : - m_type{type}, m_instructions(std::move(instructions)) { - for (const Pointer& i : m_instructions) { - solAssert(i != nullptr, ""); - } - } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - Type type() const { return m_type; } - std::vector> const& instructions() const { return m_instructions; } - void upd(std::vector> instructions) { m_instructions = std::move(instructions); } - void updType(Type type) { m_type = type; } - private: - Type m_type; - std::vector> m_instructions; +template +Pointer createNode(Args&& ... _args) +{ + return std::make_shared(std::forward(_args)...); +} + +class TvmAstNode : private boost::noncopyable, public std::enable_shared_from_this { +public: + virtual ~TvmAstNode() = default; + virtual void accept(TvmAstVisitor& _visitor) = 0; + virtual bool operator==(TvmAstNode const& _node) const = 0; +}; + +class Loc : public TvmAstNode { +public: + explicit Loc(std::string _file, int _line) : m_file{std::move(_file)}, m_line{_line} { } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const& n) const override; + std::string const& file() const { return m_file; } + int line() const { return m_line; } +private: + std::string m_file; + int m_line; +}; + +class Stack : public TvmAstNode { +public: + enum class Opcode { + DROP, + BLKDROP2, // BLKDROP2 1, 1 + POP_S, // POP_S 1 + + BLKPUSH, // BLKPUSH 1, i | BLKPUSH 3, i | BLKPUSH 2, 1 (DUP2) | BLKPUSH 3, 1 (OVER2) + PUSH2_S, // | | PUSH2 S1, S0 | PUSH2 S3, S2 + PUSH3_S, // | PUSH3 Si, Si-1, Si-2 | | + PUSH_S, // PUSH Si | | | + + BLKSWAP, // BLKSWAP 1, 1 | + REVERSE, // REVERSE 2, 0 | REVERSE 3, 0 + XCHG, // XCHG S0 S1 | XCHG S0 S2, + + TUCK, + PUXC, + XCPU, }; - - class SubProgram : public Gen { - public: - SubProgram(int take, int ret, bool _isJmp, Pointer const &_block, bool isPure) : - Gen{isPure}, - m_take{take}, - m_ret{ret}, - m_isJmp{_isJmp}, - m_block{_block} - { - } - void accept(TvmAstVisitor &_visitor) override; - bool operator==(TvmAstNode const&) const override; - int take() const override { return m_take; } - int ret() const override { return m_ret; } - Pointer const &block() const { return m_block; } - bool isJmp() const { return m_isJmp; } - private: - int m_take{}; - int m_ret{}; - bool m_isJmp{}; - Pointer m_block; + explicit Stack(Opcode opcode, int i = -1, int j = -1, int k = -1); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const& _node) const override; + Opcode opcode() const { return m_opcode; } + int i() const { return m_i; } + int j() const { return m_j; } + int k() const { return m_k; } +private: + Opcode m_opcode{}; + int m_i{-1}; + int m_j{-1}; + int m_k{-1}; +}; + +// abstract +class Gen : public TvmAstNode { +public: + explicit Gen(bool _isPure) : m_isPure{_isPure} {} + virtual int take() const = 0; + virtual int ret() const = 0; + bool isPure() const { return m_isPure; } +private: + bool m_isPure{}; // it doesn't throw exception, has no side effects (doesn't change any GLOB vars) +}; + +class Glob : public Gen { +public: + enum class Opcode { + GetOrGetVar, + SetOrSetVar, + + PUSHROOT, + POPROOT, + + PUSH_C3, + POP_C3, + + PUSH_C7, + POP_C7, }; - - // Take one value from stack and return one - class LogCircuit : public TvmAstNode { - public: - enum class Type { - AND, - OR - }; - LogCircuit(Type type, Pointer const &body) : - m_type{type}, - m_body{body} - { - } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - Type type() const { return m_type; } - Pointer const &body() const { return m_body; } - private: - Type m_type{}; - Pointer m_body; + explicit Glob(Opcode opcode, int index = -1); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + Opcode opcode() const { return m_opcode; } + int index() const { return m_index; } + int take() const override; + int ret() const override; +private: + Opcode m_opcode{}; + int m_index{-1}; +}; + +class CodeBlock; + +class DeclRetFlag : public TvmAstNode { +public: + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; +}; + +class Opaque : public Gen { +public: + explicit Opaque(Pointer _block, int take, int ret, bool isPure) : + Gen{isPure}, m_block(std::move(_block)), m_take{take}, m_ret{ret} {} + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + Pointer const& block() const { return m_block; } + int take() const override { return m_take; } + int ret() const override { return m_ret; } +private: + Pointer m_block; + int m_take{}; + int m_ret{}; +}; + +class AsymGen : public TvmAstNode { +public: + explicit AsymGen(std::string opcode); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + std::string const& opcode() const { return m_opcode; } +private: + std::string m_opcode; +}; + +class HardCode : public Gen { +public: + explicit HardCode(std::vector code, int take, int ret, bool _isPure) : + Gen{_isPure}, m_code(std::move(code)), m_take{take}, m_ret{ret} {} + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + std::vector const& code() const { return m_code; } + int take() const override { return m_take; } + int ret() const override { return m_ret; } +private: + std::vector m_code; + int m_take{}; + int m_ret{}; +}; + +class GenOpcode : public Gen { +public: + explicit GenOpcode(const std::string& opcode, int take, int ret, bool _isPure = false); + void accept(TvmAstVisitor& _visitor) override; + std::string fullOpcode() const; + std::string const &opcode() const { return m_opcode; } + std::string const &arg() const { return m_arg; } + std::string const &comment() const { return m_comment; } + int take() const override { return m_take; } + int ret() const override { return m_ret; } + bool operator==(TvmAstNode const& _node) const override; +private: + std::string m_opcode; + std::string m_arg; + std::string m_comment; + int m_take{}; + int m_ret{}; +}; + +class TvmReturn : public TvmAstNode { +public: + TvmReturn(bool _withIf, bool _withNot, bool _withAlt); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + bool withIf() const { return m_withIf; } + bool withNot() const { return m_withNot; } + bool withAlt() const { return m_withAlt; } +private: + bool m_withIf{}; + bool m_withNot{}; + bool m_withAlt{}; +}; + +class ReturnOrBreakOrCont : public TvmAstNode { +public: + explicit ReturnOrBreakOrCont(int _take, Pointer const &body) : + m_take{_take}, + m_body{body} + { + } + Pointer const &body() const { return m_body; } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + int take() const { return m_take; } +private: + int m_take{}; + Pointer m_body; +}; + +class TvmException : public TvmAstNode { +public: + explicit TvmException(bool _arg, bool _any, bool _if, bool _not, std::string _param) : + m_arg{_arg}, + m_any{_any}, + m_if{_if}, + m_not{_not}, + m_param{std::move(_param)} + { + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + std::string opcode() const; + std::string const& arg() const { return m_param; } + int take() const; + bool withArg() const { return m_arg; } + bool withAny() const { return m_any; } + bool withIf() const { return m_if; } + bool withNot() const { return m_not; } +private: + bool m_arg{}; + bool m_any{}; + bool m_if{}; + bool m_not{}; + std::string m_param; +}; + +class PushCellOrSlice : public Gen { +public: + enum class Type { + PUSHREF_COMPUTE, + PUSHREFSLICE_COMPUTE, + PUSHREF, + PUSHREFSLICE, + CELL, + PUSHSLICE }; - - class TvmIfElse : public Gen { - public: - TvmIfElse(bool _withNot, bool _withJmp, - Pointer const &trueBody, - Pointer const &falseBody, - int ret); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override; - bool withNot() const { return m_withNot; } - bool withJmp() const { return m_withJmp; } - Pointer const& trueBody() const { return m_trueBody; } - Pointer const& falseBody() const { return m_falseBody; } - int take() const override { return 1; } - int ret() const override { return m_ret; } - private: - bool m_withNot{}; - bool m_withJmp{}; - Pointer m_trueBody; - Pointer m_falseBody; // nullptr for if-statement - int m_ret{}; + PushCellOrSlice(Type type, std::string blob, Pointer child) : + Gen{true}, // we don't execute data + m_type{type}, + m_blob{std::move(blob)}, + m_child{std::move(child)} + { + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + bool operator<(TvmAstNode const&) const; + int take() const override { return 0; } + int ret() const override { return 1; } + Type type() const { return m_type; } + std::string const &blob() const { return m_blob; } + std::string chainBlob() const; + Pointer child() const { return m_child; } + void updToRef(); +private: + Type m_type; + std::string m_blob; + Pointer m_child; +}; + +class CodeBlock : public TvmAstNode { +public: + enum class Type { + None, + PUSHCONT, + PUSHREFCONT, }; - - class TvmRepeat : public TvmAstNode { - public: - explicit TvmRepeat(bool _withBreakOrReturn, Pointer const &body) : - m_withBreakOrReturn{_withBreakOrReturn}, - m_body(body) - { + static std::string toString(Type t); + CodeBlock(Type type, std::vector> instructions = {}) : + m_type{type}, m_instructions(std::move(instructions)) { + for (const Pointer& i : m_instructions) { + solAssert(i != nullptr, ""); } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - bool withBreakOrReturn() const { return m_withBreakOrReturn; } - Pointer const& body() const { return m_body; } - private: - bool m_withBreakOrReturn{}; - Pointer m_body; - }; - - class TvmUntil : public TvmAstNode { - public: - explicit TvmUntil(bool _withBreakOrReturn, Pointer const &body) : - m_withBreakOrReturn{_withBreakOrReturn}, - m_body(body) { } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - bool withBreakOrReturn() const { return m_withBreakOrReturn; } - Pointer const& body() const { return m_body; } - private: - bool m_withBreakOrReturn{}; - Pointer m_body; - }; - - class While : public TvmAstNode { - public: - While(bool _infinite, bool _withBreakOrReturn, Pointer const &condition, - Pointer const &body - ) : - m_infinite{_infinite}, - m_withBreakOrReturn{_withBreakOrReturn}, - m_condition{condition}, - m_body(body) { } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - Pointer const& condition() const { return m_condition; } - Pointer const& body() const { return m_body; } - bool isInfinite() const { return m_infinite; } - bool withBreakOrReturn() const { return m_withBreakOrReturn; } - private: - bool m_infinite{}; - bool m_withBreakOrReturn{}; - Pointer m_condition; - Pointer m_body; - }; - - class TryCatch : public TvmAstNode { - public: - TryCatch(Pointer _tryBody, Pointer _catchBody, bool _saveAltC2) : - m_tryBody{std::move(_tryBody)}, - m_catchBody{std::move(_catchBody)}, - m_saveAltC2{_saveAltC2} - {} - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - Pointer const& tryBody() const { return m_tryBody; } - Pointer const& catchBody() const { return m_catchBody; } - bool saveAltC2() const { return m_saveAltC2; } - private: - Pointer m_tryBody; - Pointer m_catchBody; - bool m_saveAltC2{}; - }; - - class Function : public TvmAstNode { - public: - enum class FunctionType { - PrivateFunction, - PrivateFunctionWithObj, - Macro, - MacroGetter, - MainInternal, - MainExternal, - OnCodeUpgrade, - OnTickTock - }; - Function(int take, int ret, std::string name, FunctionType type, Pointer block, - FunctionDefinition const* _function = {}); - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - int take() const { return m_take; } - int ret() const { return m_ret; } - std::string const& name() const { return m_name; } - FunctionDefinition const* functionDefinition() const { return m_function; } - FunctionType type() const { return m_type; } - Pointer const& block() const { return m_block; } - private: - int m_take{}; - int m_ret{}; - std::string m_name; - FunctionType m_type{}; - Pointer m_block; - FunctionDefinition const* m_function{}; + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + Type type() const { return m_type; } + std::vector> const& instructions() const { return m_instructions; } + void upd(std::vector> instructions) { m_instructions = std::move(instructions); } + void updType(Type type) { m_type = type; } +private: + Type m_type; + std::vector> m_instructions; +}; + +class SubProgram : public Gen { +public: + SubProgram(int take, int ret, bool _isJmp, Pointer const &_block, bool isPure) : + Gen{isPure}, + m_take{take}, + m_ret{ret}, + m_isJmp{_isJmp}, + m_block{_block} + { + } + void accept(TvmAstVisitor &_visitor) override; + bool operator==(TvmAstNode const&) const override; + int take() const override { return m_take; } + int ret() const override { return m_ret; } + Pointer const &block() const { return m_block; } + bool isJmp() const { return m_isJmp; } +private: + int m_take{}; + int m_ret{}; + bool m_isJmp{}; + Pointer m_block; +}; + +// Take one value from stack and return one +class LogCircuit : public TvmAstNode { +public: + enum class Type { + AND, + OR }; - - class Contract : public TvmAstNode { - public: - explicit Contract( - std::vector pragmas, - std::vector> functions - ) : - m_pragmas{std::move(pragmas)}, - m_functions{std::move(functions)} - { - } - void accept(TvmAstVisitor& _visitor) override; - bool operator==(TvmAstNode const&) const override { return false; } // TODO - std::vector const &pragmas() const { return m_pragmas; } - std::vector>& functions(); - private: - std::vector m_pragmas; - std::vector> m_functions; + LogCircuit(Type type, Pointer const &body) : + m_type{type}, + m_body{body} + { + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + Type type() const { return m_type; } + Pointer const &body() const { return m_body; } +private: + Type m_type{}; + Pointer m_body; +}; + +class TvmIfElse : public Gen { +public: + TvmIfElse(bool _withNot, bool _withJmp, + Pointer const &trueBody, + Pointer const &falseBody, + int ret); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override; + bool withNot() const { return m_withNot; } + bool withJmp() const { return m_withJmp; } + Pointer const& trueBody() const { return m_trueBody; } + Pointer const& falseBody() const { return m_falseBody; } + int take() const override { return 1; } + int ret() const override { return m_ret; } +private: + bool m_withNot{}; + bool m_withJmp{}; + Pointer m_trueBody; + Pointer m_falseBody; // nullptr for if-statement + int m_ret{}; +}; + +class TvmRepeat : public TvmAstNode { +public: + explicit TvmRepeat(bool _withBreakOrReturn, Pointer const &body) : + m_withBreakOrReturn{_withBreakOrReturn}, + m_body(body) + { + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO + bool withBreakOrReturn() const { return m_withBreakOrReturn; } + Pointer const& body() const { return m_body; } +private: + bool m_withBreakOrReturn{}; + Pointer m_body; +}; + +class TvmUntil : public TvmAstNode { +public: + explicit TvmUntil(bool _withBreakOrReturn, Pointer const &body) : + m_withBreakOrReturn{_withBreakOrReturn}, + m_body(body) { } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO + bool withBreakOrReturn() const { return m_withBreakOrReturn; } + Pointer const& body() const { return m_body; } +private: + bool m_withBreakOrReturn{}; + Pointer m_body; +}; + +class While : public TvmAstNode { +public: + While(bool _infinite, bool _withBreakOrReturn, Pointer const &condition, + Pointer const &body + ) : + m_infinite{_infinite}, + m_withBreakOrReturn{_withBreakOrReturn}, + m_condition{condition}, + m_body(body) { } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO + Pointer const& condition() const { return m_condition; } + Pointer const& body() const { return m_body; } + bool isInfinite() const { return m_infinite; } + bool withBreakOrReturn() const { return m_withBreakOrReturn; } +private: + bool m_infinite{}; + bool m_withBreakOrReturn{}; + Pointer m_condition; + Pointer m_body; +}; + +class TryCatch : public TvmAstNode { +public: + TryCatch(Pointer _tryBody, Pointer _catchBody, bool _saveAltC2) : + m_tryBody{std::move(_tryBody)}, + m_catchBody{std::move(_catchBody)}, + m_saveAltC2{_saveAltC2} + {} + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO +Pointer const& tryBody() const { return m_tryBody; } + Pointer const& catchBody() const { return m_catchBody; } + bool saveAltC2() const { return m_saveAltC2; } +private: + Pointer m_tryBody; + Pointer m_catchBody; + bool m_saveAltC2{}; +}; + +class Function : public TvmAstNode { +public: + enum class FunctionType { + PrivateFunctionWithObj, + Fragment, + PublicStateVariableGetter, + MainInternal, + MainExternal, + OnCodeUpgrade, + OnTickTock }; - - Pointer gen(const std::string& cmd); - Pointer genPushSlice(const std::string& _str); - Pointer makePushCellOrSlice(std::string const& hexStr, bool toSlice); - Pointer makeDROP(int cnt = 1); - Pointer makePOP(int i); - Pointer makeBLKPUSH(int qty, int index); - Pointer makePUSH(int i); - Pointer makePUSH2(int i, int j); - Pointer makePUSH3(int i, int j, int k); - Pointer makeRET(); - Pointer makeRETALT(); - Pointer makeIFRETALT(); - Pointer makeIFRET(); - Pointer makeIFNOTRET(); - Pointer makeIFNOTRETALT(); - Pointer makeTHROW(const std::string& cmd); - Pointer makeXCH_S(int i); - Pointer makeXCH_S_S(int i, int j); - Pointer makeGetGlob(int i); - Pointer makeSetGlob(int i); - Pointer makeBLKDROP2(int droppedCount, int leftCount); - Pointer makePUSHREF(const std::string& data = ""); - Pointer makeREVERSE(int i, int j); - Pointer makeROT(); - Pointer makeROTREV(); - Pointer makeBLKSWAP(int down, int top); - Pointer makeTUCK(); - Pointer makePUXC(int i, int j); - Pointer makeXCPU(int i, int j); - Pointer flipIfElse(TvmIfElse const& node); - - bool isPureGen01(TvmAstNode const& node); - bool isSWAP(Pointer const& node); - std::optional> isBLKSWAP(Pointer const& node); - std::optional isDrop(Pointer const& node); - std::optional isPOP(Pointer const& node); - std::optional> isBLKPUSH(Pointer const& node); - bool isXCHG(Pointer const& node, int i, int j); - std::optional isXCHG_S0(Pointer const& node); - std::optional> isREVERSE(Pointer const& node); - Pointer isPlainPushSlice(Pointer const& node); // TODO DELETE, support long slices - - int getRootBitSize(PushCellOrSlice const &_node); - - Pointer getZeroOrNullAlignment(bool isZero, bool isSwap, bool isNot); + Function(int take, int ret, std::string name, std::optional functionId, FunctionType type, Pointer block, + FunctionDefinition const* _function = {}); + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO + int take() const { return m_take; } + int ret() const { return m_ret; } + std::string const& name() const { return m_name; } + std::optional functionId() const { return m_functionId; } + FunctionDefinition const* functionDefinition() const { return m_function; } + FunctionType type() const { return m_type; } + Pointer const& block() const { return m_block; } +private: + int m_take{}; + int m_ret{}; + std::string m_name; + std::optional m_functionId{}; + FunctionType m_type{}; + Pointer m_block; + FunctionDefinition const* m_function{}; +}; + +class Contract : public TvmAstNode { +public: + explicit Contract( + bool _isLib, + bool _saveAllFunction, + bool _upgradeFunc, + bool upgradeOldSolidity, + std::string _version, + std::vector> functions, + std::map _privateFunctions + ) : + m_isLib{_isLib}, + m_saveAllFunction{_saveAllFunction}, + m_upgradeFunc{_upgradeFunc}, + m_upgradeOldSolidity{upgradeOldSolidity}, + m_version{std::move(_version)}, + m_functions{std::move(functions)}, + m_privateFunctions{std::move(_privateFunctions)} + { + } + void accept(TvmAstVisitor& _visitor) override; + bool operator==(TvmAstNode const&) const override { return false; } // TODO + bool isLib() const { return m_isLib; } + bool saveAllFunction() const { return m_saveAllFunction; } + bool upgradeFunc() const { return m_upgradeFunc; } + bool upgradeOldSolidity() const { return m_upgradeOldSolidity; } + std::string const& version() const { return m_version; } + std::vector> const& functions() const { return m_functions; } + std::map const& privateFunctions() const { return m_privateFunctions; } +private: + bool m_isLib{}; + bool m_saveAllFunction{}; + bool m_upgradeFunc{}; + bool m_upgradeOldSolidity{}; + std::string m_version; + std::vector> m_functions; + std::map m_privateFunctions; +}; + +Pointer gen(const std::string& cmd); +Pointer genPushSlice(const std::string& _str); +Pointer makePushCellOrSlice(std::string const& hexStr, bool toSlice); +Pointer makeDROP(int cnt = 1); +Pointer makePOP(int i); +Pointer makeBLKPUSH(int qty, int index); +Pointer makePUSH(int i); +Pointer makePUSH2(int i, int j); +Pointer makePUSH3(int i, int j, int k); +Pointer makeRET(); +Pointer makeRETALT(); +Pointer makeIFRETALT(); +Pointer makeIFRET(); +Pointer makeIFNOTRET(); +Pointer makeIFNOTRETALT(); +Pointer makeTHROW(const std::string& cmd); +Pointer makeXCH_S(int i); +Pointer makeXCH_S_S(int i, int j); +Pointer makeGetGlob(int i); +Pointer makeSetGlob(int i); +Pointer makeBLKDROP2(int droppedCount, int leftCount); +Pointer makePUSHREF(const std::string& data = ""); +Pointer makeREVERSE(int i, int j); +Pointer makeROT(); +Pointer makeROTREV(); +Pointer makeBLKSWAP(int down, int top); +Pointer makeTUCK(); +Pointer makePUXC(int i, int j); +Pointer makeXCPU(int i, int j); +Pointer flipIfElse(TvmIfElse const& node); + +bool isPureGen01(TvmAstNode const& node); +bool isSWAP(Pointer const& node); +std::optional> isBLKSWAP(Pointer const& node); +std::optional isDrop(Pointer const& node); +std::optional isPOP(Pointer const& node); +std::optional> isBLKPUSH(Pointer const& node); +bool isXCHG(Pointer const& node, int i, int j); +std::optional isXCHG_S0(Pointer const& node); +std::optional> isREVERSE(Pointer const& node); +Pointer isPlainPushSlice(Pointer const& node); // TODO DELETE, support long slices + +int getRootBitSize(PushCellOrSlice const &_node); + +Pointer getZeroOrNullAlignment(bool isZero, bool isSwap, bool isNot); } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TvmAstVisitor.cpp b/compiler/libsolidity/codegen/TvmAstVisitor.cpp index c0707faf..544edcf0 100644 --- a/compiler/libsolidity/codegen/TvmAstVisitor.cpp +++ b/compiler/libsolidity/codegen/TvmAstVisitor.cpp @@ -152,19 +152,22 @@ bool Printer::visit(PushCellOrSlice &_node) { tabs(); switch (_node.type()) { case PushCellOrSlice::Type::PUSHREF_COMPUTE: - m_out << "PUSHREF" << std::endl; + case PushCellOrSlice::Type::PUSHREFSLICE_COMPUTE: { + if (_node.type() == PushCellOrSlice::Type::PUSHREF_COMPUTE) + m_out << "PUSHREF { " << std::endl; + else + m_out << "PUSHREFSLICE {" << std::endl; + ++m_tab; tabs(); - m_out << ".compute $" << _node.blob() << "$" << std::endl; - return false; - case PushCellOrSlice::Type::PUSHREFSLICE_COMPUTE: - m_out << "PUSHREFSLICE" << std::endl; + m_out << ".inline-computed-cell " << _node.blob() << ", 0" << std::endl; + --m_tab; tabs(); - m_out << ".compute $" << _node.blob() << "$" << std::endl; + m_out << "}" << std::endl; return false; + } case PushCellOrSlice::Type::PUSHSLICE: m_out << "PUSHSLICE " << _node.blob() << std::endl; return false; - case PushCellOrSlice::Type::PUSHREF: m_out << "PUSHREF {"; break; @@ -674,56 +677,133 @@ bool Printer::visit(While &_node) { } bool Printer::visit(Contract &_node) { - for (const std::string& pragma : _node.pragmas()) { - m_out << pragma << std::endl; - m_out << std::endl; + std::map privFuncs = _node.privateFunctions(); + for (Pointer const& fun : _node.functions()) { + if ((fun->type() == Function::FunctionType::Fragment && fun->functionId().has_value() && _node.saveAllFunction()) || + fun->type() == Function::FunctionType::OnCodeUpgrade + ) { + std::string name = fun->name(); + uint32_t id = fun->functionId().value(); + if (privFuncs.count(id) == 0) + privFuncs[id] = name; + else + solAssert(privFuncs[id] == name, ""); + } } - for (const Pointer& f : _node.functions()){ + bool hasOnTickTock = false; + for (const Pointer& f : _node.functions()) { + hasOnTickTock |= f->name() == "onTickTock"; f->accept(*this); } - return false; -} -bool Printer::visit(Function &_node) { - const std::string &funName = _node.name(); - switch (_node.type()) { - case Function::FunctionType::PrivateFunction: - case Function::FunctionType::PrivateFunctionWithObj: - m_out << ".globl\t" << funName << std::endl; - m_out << ".type\t" << funName << ", @function" << std::endl; - break; - case Function::FunctionType::OnCodeUpgrade: { - solAssert(_node.functionDefinition(), ""); - std::optional id = _node.functionDefinition()->functionID(); - if (!id.has_value()) { - id = ChainDataEncoder::toHash256(funName) & 0x7FFFFFFFu; + if (!_node.isLib()) { + if (!hasOnTickTock) { + m_out << ".fragment onTickTock, {" << std::endl; + m_out << "}" << std::endl; + m_out << std::endl; + } + + m_out << ".fragment default_data_dict_cell, {" << std::endl; + m_out << " PUSHINT 0" << std::endl; + m_out << " NEWC" << std::endl; + m_out << " STU 256" << std::endl; + m_out << " PUSHINT 0" << std::endl; + m_out << " NEWDICT" << std::endl; + m_out << " PUSHINT 64" << std::endl; + m_out << " DICTUSETB" << std::endl; + m_out << "}" << std::endl; + m_out << std::endl; + + m_out << "; The code below forms a value of the StateInit type." << std::endl; + m_out << ".blob x4_ ; split_depth = nothing" << std::endl; + m_out << ".blob x4_ ; special = nothing" << std::endl; + m_out << ".blob xc_ ; code = just" << std::endl; + + auto printCode = [&](){ + tabs(); m_out << ".cell { ; code cell" << std::endl; + ++m_tab; + tabs(); m_out << "PUSHREFCONT {" << std::endl; + tabs(); m_out << " DICTPUSHCONST 32" << std::endl; + tabs(); m_out << " DICTUGETJMPZ" << std::endl; + tabs(); m_out << " THROW 78" << std::endl; + + tabs(); m_out << " .code-dict-cell 32, {" << std::endl; + m_tab += 2; + for (auto const& [id, name] : privFuncs) { + tabs(); m_out << "x" << std::setfill('0') << std::setw(8) << std::hex << id << " = " << name << "," << std::endl; } - m_out << ".internal-alias " << funName << ", " << *id << std::endl - << ".internal " << funName << std::endl; - break; + m_tab -= 2; + tabs(); m_out << " }" << std::endl; + + tabs(); m_out << " .cell { ; version" << std::endl; + tabs(); m_out << " .blob x" << StrUtils::stringToHex(_node.version()) << " ; " << _node.version() << std::endl; + tabs(); m_out << " }" << std::endl; + + tabs(); m_out << "}" << std::endl; + tabs(); m_out << "POPCTR c3" << std::endl; + tabs(); m_out << "DUP" << std::endl; + tabs(); m_out << "IFNOTJMPREF {" << std::endl; + tabs(); m_out << " .inline main_internal" << std::endl; + tabs(); m_out << "}" << std::endl; + tabs(); m_out << "DUP" << std::endl; + tabs(); m_out << "EQINT -1" << std::endl; + tabs(); m_out << "IFJMPREF {" << std::endl; + tabs(); m_out << " .inline main_external" << std::endl; + tabs(); m_out << "}" << std::endl; + tabs(); m_out << "DUP" << std::endl; + tabs(); m_out << "EQINT -2" << std::endl; + tabs(); m_out << "IFJMPREF {" << std::endl; + tabs(); m_out << " .inline onTickTock" << std::endl; + tabs(); m_out << "}" << std::endl; + tabs(); m_out << "THROW 11" << std::endl; + --m_tab; + tabs(); m_out << "}" << std::endl; // end code + }; + if (_node.upgradeOldSolidity() || _node.upgradeFunc()) { + int func_id = _node.upgradeFunc() ? 1666 : 2; + m_out << ".cell { ; wrapper for code" << std::endl; + ++m_tab; + tabs(); m_out << "PUSHINT " << func_id << std::endl; + tabs(); m_out << "EQUAL" << std::endl; + tabs(); m_out << "THROWIFNOT 79" << std::endl; + tabs(); m_out << "PUSHREF" << std::endl; + ++m_tab; + printCode(); + --m_tab; + tabs(); m_out << "DUP" << std::endl; + tabs(); m_out << "SETCODE" << std::endl; + tabs(); m_out << "CTOS" << std::endl; + tabs(); m_out << "PLDREF" << std::endl; + tabs(); m_out << "CTOS" << std::endl; + tabs(); m_out << "BLESS" << std::endl; + tabs(); m_out << "POP C3" << std::endl; + tabs(); m_out << "CALL 2" << std::endl; + --m_tab; + m_out << "}" << std::endl; // end code + } else { + printCode(); } - case Function::FunctionType::Macro: - case Function::FunctionType::MacroGetter: - m_out << ".macro " << funName << std::endl; - break; - case Function::FunctionType::MainInternal: - solAssert(funName == "main_internal", ""); - m_out << ".internal-alias :main_internal, 0" << std::endl - << ".internal :main_internal" << std::endl; - break; - case Function::FunctionType::MainExternal: - solAssert(funName == "main_external", ""); - m_out << ".internal-alias :main_external, -1" << std::endl - << ".internal :main_external" << std::endl; - break; - case Function::FunctionType::OnTickTock: - solAssert(funName == "onTickTock", ""); - m_out << ".internal-alias :onTickTock, -2" << std::endl - << ".internal :onTickTock" << std::endl; - break; + + m_out << ".blob xc_ ; data = just" << std::endl; + m_out << ".cell { " << std::endl; + m_out << " .blob xc_" << std::endl; + m_out << " .cell { " << std::endl; + m_out << " .inline-computed-cell default_data_dict_cell, 0" << std::endl; + m_out << " }" << std::endl; + m_out << "}" << std::endl; + m_out << ".blob x4_ ; library = hme_empty" << std::endl; } + return false; +} + +bool Printer::visit(Function &_node) { + std::string const& funName = _node.name(); + m_out << ".fragment " << funName << ", {" << std::endl; + ++m_tab; _node.block()->accept(*this); - endL(); + --m_tab; + m_out << "}" << std::endl; + m_out << std::endl; return false; } @@ -741,42 +821,21 @@ void Printer::tabs() { } void Printer::printPushInt(std::string const& str, std::string const& comment) { - static std::map power2; - static std::map power2Dec; - static std::map power2Neg; - if (power2.empty()) { - bigint p2 = 128; - for (int p = 7; p <= 256; ++p) { - power2[p2] = p; - p2 *= 2; - } - } - if (power2Dec.empty()) { - bigint p2 = 256; - for (int p = 8; p <= 256; ++p) { - power2Dec[p2 - 1] = p; - p2 *= 2; - } - } - if (power2Neg.empty()) { - bigint p2 = 256; - for (int p = 8; p <= 256; ++p) { - power2Neg[-p2] = p; - p2 *= 2; - } - } + std::map const& power2Exp = MathConsts::power2Exp(); + std::map const& power2DecExp = MathConsts::power2DecExp(); + std::map const& power2NegExp = MathConsts::power2NegExp(); bool didPrint = false; if (str.at(0) != '$') { bigint val = bigint{str}; - if (power2.count(val)) { - m_out << "PUSHPOW2 " << power2.at(val); + if (power2Exp.count(val) && power2Exp.at(val) >= 7) { + m_out << "PUSHPOW2 " << power2Exp.at(val); didPrint = true; - } else if (power2Dec.count(val)) { - m_out << "PUSHPOW2DEC " << power2Dec.at(val); + } else if (power2DecExp.count(val) && power2DecExp.at(val) >= 8) { + m_out << "PUSHPOW2DEC " << power2DecExp.at(val); didPrint = true; - } else if (power2Neg.count(val)) { - m_out << "PUSHNEGPOW2 " << power2Neg.at(val); + } else if (power2NegExp.count(val) && power2NegExp.at(val) >= 8) { + m_out << "PUSHNEGPOW2 " << power2NegExp.at(val); didPrint = true; } } diff --git a/compiler/libsolidity/interface/CompilerStack.cpp b/compiler/libsolidity/interface/CompilerStack.cpp index e566cce4..8a6190ef 100644 --- a/compiler/libsolidity/interface/CompilerStack.cpp +++ b/compiler/libsolidity/interface/CompilerStack.cpp @@ -670,7 +670,7 @@ std::pair CompilerStack::compile(bool json) if (contract->name() == m_mainContract) { if (m_generateCode && !contract->canBeDeployed()) { m_errorReporter.typeError( - 228_error, + 3715_error, contract->location(), "The desired contract isn't deployable (it has not public constructor or it's abstract or it's interface or it's library)." ); @@ -683,7 +683,7 @@ std::pair CompilerStack::compile(bool json) if (m_generateAbi && !m_generateCode) { if (targetContract != nullptr) { m_errorReporter.typeError( - 228_error, + 4605_error, targetContract->location(), SecondarySourceLocation().append("Previous contract:", contract->location()), @@ -697,7 +697,7 @@ std::pair CompilerStack::compile(bool json) } else if (contract->canBeDeployed()) { if (targetContract != nullptr) { m_errorReporter.typeError( - 228_error, + 5205_error, targetContract->location(), SecondarySourceLocation().append("Previous deployable contract:", contract->location()), @@ -718,7 +718,7 @@ std::pair CompilerStack::compile(bool json) if (!m_mainContract.empty() && targetContract == nullptr) { m_errorReporter.typeError( - 228_error, + 1468_error, SourceLocation(), "Source file doesn't contain the desired contract \"" + m_mainContract + "\"." ); @@ -732,7 +732,7 @@ std::pair CompilerStack::compile(bool json) PragmaDirectiveHelper pragmaHelper{pragmaDirectives}; Contract const& c = contract(targetContract->name()); if (m_generateAbi) { - Json::Value abi = TVMABI::generateABIJson(targetContract, pragmaDirectives); + Json::Value abi = TVMABI::generateABIJson(targetContract, getSourceUnits(), pragmaDirectives); c.abi = make_unique(abi); } if (m_generateCode) { diff --git a/compiler/libsolidity/parsing/Parser.cpp b/compiler/libsolidity/parsing/Parser.cpp index 9aca0e4d..9919b299 100644 --- a/compiler/libsolidity/parsing/Parser.cpp +++ b/compiler/libsolidity/parsing/Parser.cpp @@ -569,7 +569,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari { if (token == Token::Payable) { - parserWarning(228_error, "Has no effect. Delete this."); + parserWarning(3713_error, "Has no effect. Delete this."); } if (result.stateMutability != StateMutability::NonPayable) @@ -604,7 +604,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari { m_scanner->next(); if (m_scanner->currentToken() != Token::LParen) { - parserError(228_error, "functionID modifier should be specified as: functionID(ID)."); + parserError(8342_error, "functionID modifier should be specified as: functionID(ID)."); } m_scanner->next(); ASTPointer literal = getLiteralAndAdvance(); @@ -615,24 +615,24 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari size_t pos = 0; unsigned long id = stoul(*literal, &pos, 0); if (pos != literal->size() || id > std::numeric_limits::max()) { - fatalParserError(228_error, errText.str()); + fatalParserError(7164_error, errText.str()); } else { result.functionID = static_cast(id); } } catch (const std::invalid_argument&) { - fatalParserError(228_error, errText.str()); + fatalParserError(6936_error, errText.str()); } catch (const std::out_of_range&) { - fatalParserError(228_error, errText.str()); + fatalParserError(2598_error, errText.str()); } if (m_scanner->currentToken() != Token::RParen) - fatalParserError(228_error, "functionID modifier should be specified as: functionID(ID)."); + fatalParserError(3428_error, "functionID modifier should be specified as: functionID(ID)."); m_scanner->next(); } else if (token == Token::ExternalMsg) { if (result.externalMsg) - parserError(228_error, "externalMsg already specified."); + parserError(9986_error, "externalMsg already specified."); result.externalMsg = true; m_scanner->next(); @@ -640,7 +640,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else if (token == Token::InternalMsg) { if (result.internalMsg) - parserError(228_error, "internalMsg already specified."); + parserError(7749_error, "internalMsg already specified."); result.internalMsg = true; m_scanner->next(); @@ -648,7 +648,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else if (token == Token::Inline) { if (result.isInline) - parserError(228_error, "inline already specified."); + parserError(4362_error, "inline already specified."); result.isInline = true; m_scanner->next(); @@ -656,7 +656,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else if (token == Token::Responsible) { if (result.responsible) - parserError(228_error, "responsible already specified."); + parserError(5148_error, "responsible already specified."); result.responsible = true; m_scanner->next(); @@ -664,7 +664,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else if (token == Token::Assembly) { if (result.assembly) - parserError(228_error, "assembly already specified."); + parserError(9152_error, "assembly already specified."); result.assembly = true; m_scanner->next(); @@ -840,7 +840,7 @@ ASTPointer Parser::parseVariableDeclaration( // TODO DELETE THIS FROM HERE if (_options.kind == VarDeclKind::State && dynamic_cast(type.get())) - fatalParserError(228_error, string( + fatalParserError(5069_error, string( "vector type can't be used for state variables." )); @@ -889,7 +889,7 @@ ASTPointer Parser::parseVariableDeclaration( else if (_options.kind == VarDeclKind::State && token == Token::Static) { if (isStatic) - parserError(228_error, "Static already specified."); + parserError(7494_error, "Static already specified."); isStatic = true; m_scanner->next(); @@ -1214,7 +1214,7 @@ ASTPointer Parser::parseTypeName() { if (elemTypeName.token() == Token::Address) { - parserWarning(228_error, "Has no effect. Delete this."); + parserWarning(7323_error, "Has no effect. Delete this."); nodeFactory.markEndPosition(); stateMutability = parseStateMutability(); } @@ -1534,7 +1534,7 @@ ASTPointer Parser::parseAssemblyStatement() ASTPointer Parser::parseInlineAssembly(ASTPointer const& /*_docString*/) { - m_errorReporter.fatalTypeError(228_error, currentLocation(), "Inline assembly is disabled."); + m_errorReporter.fatalTypeError(4277_error, currentLocation(), "Inline assembly is disabled."); return nullptr; } @@ -2307,7 +2307,7 @@ Parser::FunctionCallArguments Parser::parseNamedArguments() expectToken(Token::Comma); if (m_scanner->currentToken() == Token::ExtMsg) { - fatalParserError(228_error, "\"extMsg\" call option is deprecated, use suffix \".extMsg\".\nFor example: Foo(addr).bar{...}(...).extMsg;\n"); + fatalParserError(3299_error, "\"extMsg\" call option is deprecated, use suffix \".extMsg\".\nFor example: Foo(addr).bar{...}(...).extMsg;\n"); } auto identifierWithLocation = expectIdentifierWithLocation(); diff --git a/compiler/solc/CommandLineParser.cpp b/compiler/solc/CommandLineParser.cpp index b840f4ae..dd20bee5 100644 --- a/compiler/solc/CommandLineParser.cpp +++ b/compiler/solc/CommandLineParser.cpp @@ -578,6 +578,10 @@ General Information)").c_str(), po::value()->value_name("path"), "Output directory (by default, current directory is used)" ) + ( + g_strOverwrite.c_str(), + "Overwrite existing files (used together with --ast-compact-json -o)." + ) ( g_strTVMVersion.c_str(), po::value()->value_name("version")->default_value(TVMVersion{}.name()), diff --git a/lib/stdlib_sol.tvm b/lib/stdlib_sol.tvm index 48153b53..3535af20 100644 --- a/lib/stdlib_sol.tvm +++ b/lib/stdlib_sol.tvm @@ -1,520 +1,584 @@ -.macro insert_pubkey_macro -.loc stdlib.sol, 14 -SWAP -CTOS -.loc stdlib.sol, 15 -NEWC -.loc stdlib.sol, 18 -OVER -LDI 1 -POP S3 -PUSHCONT { - .loc stdlib.sol, 19 - STSLICECONST 1 - .loc stdlib.sol, 20 - OVER - LDU 32 - POP S3 - .loc stdlib.sol, 21 - SWAP - STSLICECONST 1 - STU 32 -} -PUSHCONT { - .loc stdlib.sol, 23 - STSLICECONST 0 -} -IFELSE -.loc stdlib.sol, 27 -OVER -LDI 1 -POP S3 -PUSHCONT { - .loc stdlib.sol, 28 - STSLICECONST 1 - .loc stdlib.sol, 29 - OVER - LDI 1 - LDI 1 - POP S4 - .loc stdlib.sol, 30 - XCHG S2 - STI 1 - STI 1 -} -PUSHCONT { - .loc stdlib.sol, 32 - STSLICECONST 0 -} -IFELSE -.loc stdlib.sol, 36 -OVER -LDDICT -POP S3 -SWAP -STDICT -.loc stdlib.sol, 40 -NEWDICT -.loc stdlib.sol, 41 -PUSH S2 -LDI 1 -POP S4 -PUSHCONT { - .loc stdlib.sol, 42 - PUSH S2 - LDREFRTOS - SWAP - POP S4 - .loc stdlib.sol, 43 - PLDDICT - NIP - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 45 -ROLL 3 -PUSHINT 0 -ROTREV -NEWC -STU 256 -ROTREV -PUSHINT 64 -DICTUSETB -.loc stdlib.sol, 47 -NEWC -STDICT -.loc stdlib.sol, 48 -OVER -STSLICECONST 1 -POP S2 -.loc stdlib.sol, 49 -STBREFR -.loc stdlib.sol, 53 -OVER -LDDICT -POP S3 -SWAP -STDICT -.loc stdlib.sol, 55 -SWAP -SEMPTY -THROWIFNOT 55 -.loc stdlib.sol, 57 -ENDC -.loc stdlib.sol, 0 - -.macro replay_protection_macro -.loc stdlib.sol, 61 -GETGLOB 3 -OVER -LESS -THROWIFNOT 52 -.loc stdlib.sol, 62 -DUP -NOW -PUSHINT 1000 -MUL -PUSHINT 1800000 -ADD -LESS -THROWIFNOT 52 -.loc stdlib.sol, 63 -SETGLOB 3 -.loc stdlib.sol, 0 - -.macro __tonToGas_macro -.loc stdlib.sol, 67 -PUSHPOW2 16 -SWAP -CALLREF { - .inline ____gasGasPrice_macro -} -MULDIV -.loc stdlib.sol, 0 - -.macro __gasToTon_macro -.loc stdlib.sol, 71 -CALLREF { - .inline ____gasGasPrice_macro -} -PUSHPOW2 16 -MULDIVC -.loc stdlib.sol, 0 - -.macro __gasGasPrice_macro -.loc stdlib.sol, 75 -DUP -EQINT 0 -OVER -EQINT -1 -OR -THROWIFNOT 67 -.loc stdlib.sol, 76 -PUSHINT 20 -PUSHINT 21 -CONDSEL -CONFIGOPTPARAM -.loc stdlib.sol, 77 -DUP -ISNULL -THROWIF 68 -.loc stdlib.sol, 78 -DUP -ISNULL -THROWIF 63 -CTOS -.loc stdlib.sol, 79 -LDU 8 -LDU 64 -LDU 64 -LDU 8 -PLDU 64 -BLKDROP2 4, 1 -.loc stdlib.sol, 0 - -.macro __exp_macro -.loc stdlib.sol, 83 -PUSHINT 1 -.loc stdlib.sol, 84 -PUSHCONT { - OVER - NEQINT 0 -} -PUSHCONT { - .loc stdlib.sol, 85 - OVER - MODPOW2 1 +.fragment __exp, { + .loc stdlib.sol, 83 + PUSHINT 1 + .loc stdlib.sol, 84 PUSHCONT { - .loc stdlib.sol, 86 - PUSH S2 - MUL - .loc stdlib.sol, 87 OVER - DEC + NEQINT 0 } PUSHCONT { - .loc stdlib.sol, 89 - PUSH2 S2, S2 - MUL - POP S3 - .loc stdlib.sol, 90 + .loc stdlib.sol, 85 OVER - RSHIFT 1 - } - IFELSE - POP S2 - .loc stdlib.sol, 0 -} -WHILE -.loc stdlib.sol, 93 -BLKDROP2 2, 1 -.loc stdlib.sol, 0 - -.macro parseInteger_macro -.loc stdlib.sol, 99 -TUPLE 0 -.loc stdlib.sol, 100 -PUSH S2 -PUSHCONT { - .loc stdlib.sol, 101 - PUSHINT 0 - CALLREF { - XCPU S1, S0 - TLEN + MODPOW2 1 PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT + .loc stdlib.sol, 86 + PUSH S2 + MUL + .loc stdlib.sol, 87 + OVER + DEC } PUSHCONT { - TUPLE 0 + .loc stdlib.sol, 89 + PUSH2 S2, S2 + MUL + POP S3 + .loc stdlib.sol, 90 + OVER + RSHIFT 1 } IFELSE - ROT - TPUSH - TPUSH + POP S2 + .loc stdlib.sol, 0 } - .loc stdlib.sol, 102 + WHILE + .loc stdlib.sol, 93 BLKDROP2 2, 1 .loc stdlib.sol, 0 } -IFNOTJMP -.loc stdlib.sol, 104 -PUSHINT 0 -.loc stdlib.sol, 105 -PUSHCONT { - PUSH S3 - NEQINT 0 + +.fragment __gasGasPrice, { + .loc stdlib.sol, 75 + DUP + EQINT 0 + OVER + EQINT -1 + OR + THROWIFNOT 67 + .loc stdlib.sol, 76 + PUSHINT 20 + PUSHINT 21 + CONDSEL + CONFIGOPTPARAM + .loc stdlib.sol, 77 + DUP + ISNULL + THROWIF 68 + .loc stdlib.sol, 78 + DUP + ISNULL + THROWIF 63 + CTOS + .loc stdlib.sol, 79 + LDU 8 + LDU 64 + LDU 64 + LDU 8 + PLDU 64 + BLKDROP2 4, 1 + .loc stdlib.sol, 0 } -PUSHCONT { - .loc stdlib.sol, 106 - OVER2 - DIVMOD - POP S2 - POP S4 - .loc stdlib.sol, 107 - DUP2 + +.fragment __gasToTon, { + .loc stdlib.sol, 71 CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH + .inline __gasGasPrice } - POP S2 + PUSHPOW2 16 + MULDIVC .loc stdlib.sol, 0 } -WHILE -DROP -BLKDROP2 2, 1 -.loc stdlib.sol, 0 - -.macro convertIntToDecStr_short_macro -.loc stdlib.sol, 112 -ROTREV -PUSH S2 -ABS -PUSHINT 0 -DUP -ROLL 5 -LESSINT 0 -CALLREF { - .inline __convertIntToDecStr_macro -} -.loc stdlib.sol, 0 -.macro convertIntToDecStr_macro -.loc stdlib.sol, 116 -PUSH S4 -BREMBITS -RSHIFT 3 -.loc stdlib.sol, 117 -DUP -PUSHCONT { - .loc stdlib.sol, 118 - BLKPUSH 2, 6 - CALLREF { - XCPU S1, S0 - TLEN +.fragment __strstr, { + .loc stdlib.sol, 516 + NULL + .loc stdlib.sol, 517 + PUSH S2 + CTOS + .loc stdlib.sol, 518 + PUSH S2 + CTOS + .loc stdlib.sol, 519 + DUP + PUSHPOW2DEC 32 + SDATASIZE + DROP + NIP + .loc stdlib.sol, 520 + PUSH S2 + PUSHPOW2DEC 32 + SDATASIZE + DROP + NIP + .loc stdlib.sol, 521 + DUP2 + GREATER + PUSHCONT { + .loc stdlib.sol, 522 + ROLL 4 + BLKDROP2 6, 1 + .loc stdlib.sol, 0 + } + IFJMP + .loc stdlib.sol, 524 + OVER + RSHIFT 3 + POP S2 + .loc stdlib.sol, 525 + RSHIFT 3 + .loc stdlib.sol, 526 + PUSH2 S0, S2 + .loc stdlib.sol, 527 + LDU 8 + POP S5 + .loc stdlib.sol, 528 + FALSE ; decl return flag + PUSHCONT { + PUSH2 S3, S4 + GEQ + } + PUSHCONT { + .loc stdlib.sol, 529 + PUSH S6 + LDU 8 + POP S8 + .loc stdlib.sol, 530 + PUSH S7 + SBITREFS + .loc stdlib.sol, 531 + OVER + LESSINT 8 + OVER + GTINT 0 + AND PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT + .loc stdlib.sol, 532 + PUSH S9 + LDREFRTOS + NIP + POP S10 + .loc stdlib.sol, 0 } + IF + .loc stdlib.sol, 534 + PUSH2 S2, S4 + EQUAL PUSHCONT { - TUPLE 0 + .loc stdlib.sol, 535 + BLKPUSH 2, 9 + PUSHCONT { + .loc stdlib.sol, 493 + OVER + SBITS + .loc stdlib.sol, 494 + OVER + SBITS + .loc stdlib.sol, 495 + FALSE ; decl return flag + PUSHCONT { + PUSH S2 + NEQINT 0 + PUSH S2 + NEQINT 0 + AND + } + PUSHCONT { + .loc stdlib.sol, 496 + BLKPUSH 2, 2 + MIN + .loc stdlib.sol, 497 + PUSH2 S5, S0 + LDSLICEX + POP S7 + .loc stdlib.sol, 498 + PUSH2 S5, S1 + LDSLICEX + POP S7 + .loc stdlib.sol, 499 + PUSH2 S5, S2 + SUB + POP S6 + .loc stdlib.sol, 500 + ROT + PUSH S4 + SUBR + POP S4 + .loc stdlib.sol, 501 + SDEQ + PUSHCONT { + .loc stdlib.sol, 502 + BLKDROP 5 + FALSE + PUSHINT 4 + RETALT + .loc stdlib.sol, 0 + } + IFNOTJMP + .loc stdlib.sol, 504 + PUSH S2 + EQINT 0 + PUSH S5 + SREFS + NEQINT 0 + AND + PUSHCONT { + .loc stdlib.sol, 505 + PUSH S4 + LDREFRTOS + NIP + POP S5 + .loc stdlib.sol, 506 + PUSH S4 + SBITS + POP S3 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 508 + OVER + EQINT 0 + PUSH S4 + SREFS + NEQINT 0 + AND + PUSHCONT { + .loc stdlib.sol, 509 + PUSH S3 + LDREFRTOS + NIP + POP S4 + .loc stdlib.sol, 510 + PUSH S3 + SBITS + POP S2 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 0 + } + WHILEBRK + IFRET + .loc stdlib.sol, 513 + BLKDROP 4 + TRUE + .loc stdlib.sol, 492 + } + CALLX + .loc stdlib.sol, 0 + PUSHCONT { + .loc stdlib.sol, 536 + BLKSWAP 2, 5 + SUBR + MODPOW2 32 + POP S9 + .loc stdlib.sol, 537 + ROLL 8 + BLKDROP2 10, 1 + PUSHINT 4 + RETALT + .loc stdlib.sol, 0 + } + IFJMP } - IFELSE - ROT - TPUSH - TPUSH + IF + .loc stdlib.sol, 540 + BLKDROP 3 + PUSH S3 + DEC + POP S4 + .loc stdlib.sol, 0 } - POP S7 - .loc stdlib.sol, 119 - NEWC - POP S6 - .loc stdlib.sol, 120 - DROP - PUSHINT 127 - .loc stdlib.sol, 0 -} -IFNOT -.loc stdlib.sol, 122 -ROT -PUSHINT 48 -PUSHINT 32 -CONDSEL -.loc stdlib.sol, 123 -ROT -PUSHCONT { - .loc stdlib.sol, 124 - PUSH S4 - STSLICECONST x2d - POP S5 + WHILEBRK + IFRET + .loc stdlib.sol, 542 + BLKDROP 6 + BLKDROP2 2, 1 .loc stdlib.sol, 0 } -IF -.loc stdlib.sol, 125 -OVER -DEC -POP S2 -.loc stdlib.sol, 126 -OVER -PUSHCONT { - .loc stdlib.sol, 127 - BLKPUSH 2, 5 - CALLREF { - XCPU S1, S0 + +.fragment assembleList, { + .loc stdlib.sol, 302 + PUSHCONT { + OVER TLEN - PUSHCONT { + ISZERO + NOT + } + PUSHCONT { + .loc stdlib.sol, 303 + OVER + CALLREF { TPOP + TPOP + ROTREV DUP TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { TPUSH - TUPLE 0 } - IFNOT - } - PUSHCONT { - TUPLE 0 + PUSHCONT { + DROP + } + IFELSE } - IFELSE - ROT - TPUSH - TPUSH + POP S3 + .loc stdlib.sol, 304 + STBREF + .loc stdlib.sol, 0 } - POP S6 - .loc stdlib.sol, 128 - NEWC - POP S5 - .loc stdlib.sol, 129 - PUSHINT 127 - POP S2 + WHILE + .loc stdlib.sol, 307 + ENDC + NIP .loc stdlib.sol, 0 } -IFNOT -.loc stdlib.sol, 131 -ROLL 3 -PUSHINT 10 -CALLREF { - .inline __parseInteger_macro -} -.loc stdlib.sol, 132 -DUP -CALLREF { - DUP - TLEN - DUP + +.fragment __subCell, { + .loc stdlib.sol, 406 + PUSHREF { + } + .loc stdlib.sol, 407 + OVER PUSHCONT { - DEC - PUSHPOW2DEC 8 - MUL - SWAP - LAST - TLEN - ADD + .loc stdlib.sol, 408 + BLKDROP2 3, 1 + .loc stdlib.sol, 0 } + IFNOTJMP + .loc stdlib.sol, 410 + DROP + SWAP + PUSHINT 127 + DIVMOD + .loc stdlib.sol, 411 + ROLL 3 + CTOS + .loc stdlib.sol, 412 + ROT PUSHCONT { + .loc stdlib.sol, 413 + LDREFRTOS NIP + .loc stdlib.sol, 0 } - IFELSE -} -.loc stdlib.sol, 133 -PUSH S4 -PUSHCONT { - .loc stdlib.sol, 134 - PUSH2 S4, S0 - LESS - PUSH S5 - GTINT 127 - OR - THROWIF 66 - .loc stdlib.sol, 136 - PUSH2 S4, S0 - SUB - .loc stdlib.sol, 137 - PUSH2 S0, S4 - LEQ + REPEAT + .loc stdlib.sol, 415 + SWAP + MULCONST 8 + SDSKIPFIRST + .loc stdlib.sol, 416 + TUPLE 0 + .loc stdlib.sol, 417 + NEWC + .loc stdlib.sol, 418 PUSHCONT { - .loc stdlib.sol, 138 - DUP - PUSHCONT { - .loc stdlib.sol, 139 - PUSH2 S6, S3 - STUR 8 - POP S7 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 141 - PUSH2 S4, S0 - SUB - POP S5 - .loc stdlib.sol, 142 + PUSH S3 + NEQINT 0 + } + PUSHCONT { + .loc stdlib.sol, 419 + OVER2 + SBITS + RSHIFT 3 + PUSH S2 + BREMBITS + RSHIFT 3 + MIN + MIN + .loc stdlib.sol, 420 + PUSH3 S1, S3, S0 + LSHIFT 3 + MODPOW2 10 + LDSLICEX + POP S6 + STSLICER + POP S2 + .loc stdlib.sol, 421 PUSH S4 + SUBR + POP S4 + .loc stdlib.sol, 422 + PUSH S3 PUSHCONT { - .loc stdlib.sol, 143 - BLKPUSH 2, 7 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP + .loc stdlib.sol, 423 + PUSH S2 + SBITS + PUSHCONT { + .loc stdlib.sol, 424 + PUSH S2 + LDREFRTOS + NIP + POP S3 + .loc stdlib.sol, 0 + } + IFNOT + .loc stdlib.sol, 426 + DUP + BREMBITS + LESSINT 8 + PUSHCONT { + .loc stdlib.sol, 427 + CALLREF { + XCPU S1, S0 TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { TUPLE 0 } - IFNOT - } - PUSHCONT { - TUPLE 0 + IFELSE + ROT + TPUSH + TPUSH } - IFELSE - ROT - TPUSH - TPUSH + .loc stdlib.sol, 428 + NEWC + .loc stdlib.sol, 0 } - POP S8 - .loc stdlib.sol, 144 - NEWC - POP S7 - .loc stdlib.sol, 145 - PUSHINT 127 - POP S5 + IF .loc stdlib.sol, 0 } - IFNOT + IF + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 432 + CALLREF { + .inline assembleList + } + BLKDROP2 2, 1 + .loc stdlib.sol, 0 +} + +.fragment __substr_from, { + .loc stdlib.sol, 392 + OVER + PUSHPOW2DEC 32 + CDATASIZE + DROP + NIP + .loc stdlib.sol, 393 + RSHIFT 3 + .loc stdlib.sol, 394 + PUSH2 S0, S1 + GEQ + THROWIFNOT 70 + .loc stdlib.sol, 395 + OVER + SUB + .loc stdlib.sol, 396 + CALLREF { + .inline __subCell + } + .loc stdlib.sol, 0 +} + +.fragment __substr_from_count, { + .loc stdlib.sol, 400 + PUSH S2 + PUSHPOW2DEC 32 + CDATASIZE + DROP + NIP + .loc stdlib.sol, 401 + RSHIFT 3 + .loc stdlib.sol, 402 + BLKPUSH 2, 2 + ADD + GEQ + THROWIFNOT 70 + .loc stdlib.sol, 403 + CALLREF { + .inline __subCell } + .loc stdlib.sol, 0 +} + +.fragment __tonToGas, { + .loc stdlib.sol, 67 + PUSHPOW2 16 + SWAP + CALLREF { + .inline __gasGasPrice + } + MULDIV + .loc stdlib.sol, 0 +} + +.fragment bytes_substr, { + .loc stdlib.sol, 381 + PUSH S3 + PUSHPOW2DEC 32 + CDATASIZE + DROP + NIP + .loc stdlib.sol, 382 + RSHIFT 3 + .loc stdlib.sol, 383 + SWAP PUSHCONT { - .loc stdlib.sol, 148 - PUSH S4 - PUSHCONT { - .loc stdlib.sol, 149 - PUSH2 S6, S3 - STUR 8 - POP S7 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 151 - BLKPUSH 2, 7 + .loc stdlib.sol, 384 + DUP + POP S2 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 385 + PUSH2 S1, S2 + GEQ + THROWIFNOT 70 + .loc stdlib.sol, 386 + XCPU S1, S2 + SUB + .loc stdlib.sol, 387 + SWAP + BLKPUSH 2, 2 + ADD + GEQ + THROWIFNOT 70 + .loc stdlib.sol, 388 + CALLREF { + .inline __subCell + } + .loc stdlib.sol, 0 +} + +.fragment storeStringInBuilders, { + .loc stdlib.sol, 284 + OVER + BREMBITS + ADDCONST -7 + .loc stdlib.sol, 285 + OVER + SBITREFS + .loc stdlib.sol, 286 + DUP + PUSHCONT { + .loc stdlib.sol, 287 + PUSH S3 + PUSHINT 0 + PUSH S2 + SSKIPFIRST + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 288 + DROP + OVER + LEQ + .loc stdlib.sol, 289 + PUSHCONT { + .loc stdlib.sol, 292 + DUP2 + LDSLICEX + POP S3 + .loc stdlib.sol, 293 + PUSH S3 + STSLICE + POP S3 + .loc stdlib.sol, 294 + OVER2 CALLREF { XCPU S1, S0 TLEN @@ -538,528 +602,210 @@ PUSHCONT { TPUSH TPUSH } - POP S8 - .loc stdlib.sol, 152 + POP S4 + .loc stdlib.sol, 295 NEWC - POP S7 - .loc stdlib.sol, 153 - PUSH2 S0, S4 - SUB - PUSHCONT { - .loc stdlib.sol, 154 - PUSH2 S6, S3 - STUR 8 - POP S7 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 156 - PUSHINT 127 - OVER - SUB - PUSH S5 - ADD - POP S5 + POP S3 + .loc stdlib.sol, 296 } - IFELSE + IFNOT + PUSH2 S1, S2 + STSLICE + BLKDROP2 3, 1 .loc stdlib.sol, 0 - DROP } -IF -.loc stdlib.sol, 159 -PUSH2 S0, S3 -LEQ -PUSHCONT { - .loc stdlib.sol, 160 - DUP + +.fragment strToList, { + .loc stdlib.sol, 366 + TUPLE 0 + .loc stdlib.sol, 367 + SWAP + CTOS + .loc stdlib.sol, 368 PUSHCONT { - .loc stdlib.sol, 161 - OVER + DUP + SREFS + NEQINT 0 + } + PUSHCONT { + .loc stdlib.sol, 369 + LDREFRTOS + .loc stdlib.sol, 371 + SWAP + NEWC + STSLICE + .loc stdlib.sol, 372 + PUXC S2, S-1 CALLREF { - TPOP - TPOP - ROTREV - DUP + XCPU S1, S0 TLEN PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT } PUSHCONT { - DROP + TUPLE 0 } IFELSE + ROT + TPUSH + TPUSH } - POP S3 - .loc stdlib.sol, 162 - PUSH S6 - PUSHINT 48 - ROT - ADD - STUR 8 - POP S6 + POP S2 .loc stdlib.sol, 0 } - REPEAT + WHILE + .loc stdlib.sol, 376 + NEWC + STSLICE + .loc stdlib.sol, 0 } -PUSHCONT { - .loc stdlib.sol, 165 - PUSH S3 + +.fragment concatenateStrings, { + .loc stdlib.sol, 461 + SWAP + CALLREF { + .inline strToList + } + .loc stdlib.sol, 462 + ROT + CTOS + .loc stdlib.sol, 463 + BLKPUSH 3, 2 + CALLREF { + .inline storeStringInBuilders + } + POP S3 + POP S3 + .loc stdlib.sol, 464 PUSHCONT { - .loc stdlib.sol, 166 - OVER + DUP + PUSHINT 1 + SCHKREFSQ + } + PUSHCONT { + .loc stdlib.sol, 465 + LDREFRTOS + NIP + .loc stdlib.sol, 466 + BLKPUSH 3, 2 CALLREF { - TPOP - TPOP - ROTREV - DUP - TLEN - PUSHCONT { - TPUSH - } - PUSHCONT { - DROP - } - IFELSE + .inline storeStringInBuilders } POP S3 - .loc stdlib.sol, 167 - PUSH S6 - PUSHINT 48 - ROT - ADD - STUR 8 - POP S6 + POP S3 .loc stdlib.sol, 0 } - REPEAT - .loc stdlib.sol, 169 - BLKPUSH 2, 6 + WHILE + .loc stdlib.sol, 468 + DROP CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH + .inline assembleList } - POP S7 - .loc stdlib.sol, 170 - NEWC - POP S6 - .loc stdlib.sol, 171 - PUSH2 S0, S3 - SUB + .loc stdlib.sol, 0 +} + +.fragment parseInteger, { + .loc stdlib.sol, 99 + TUPLE 0 + .loc stdlib.sol, 100 + PUSH S2 PUSHCONT { - .loc stdlib.sol, 172 - OVER + .loc stdlib.sol, 101 + PUSHINT 0 CALLREF { - TPOP - TPOP - ROTREV - DUP + XCPU S1, S0 TLEN PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT } PUSHCONT { - DROP + TUPLE 0 } IFELSE + ROT + TPUSH + TPUSH } - POP S3 - .loc stdlib.sol, 173 - PUSH S6 - PUSHINT 48 - ROT - ADD - STUR 8 - POP S6 + .loc stdlib.sol, 102 + BLKDROP2 2, 1 .loc stdlib.sol, 0 } - REPEAT -} -IFELSE -.loc stdlib.sol, 177 -BLKDROP 5 -.loc stdlib.sol, 0 - -.macro convertAddressToHexString_macro -.loc stdlib.sol, 181 -REWRITESTDADDR -.loc stdlib.sol, 182 -OVER2 -ROLL 3 -CALLREF { - .inline __convertIntToHexStr_short_macro -} -POP S3 -POP S3 -.loc stdlib.sol, 183 -OVER -BREMBITS -.loc stdlib.sol, 184 -GTINT 8 -PUSHCONT { - .loc stdlib.sol, 187 - BLKPUSH 2, 2 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH + IFNOTJMP + .loc stdlib.sol, 104 + PUSHINT 0 + .loc stdlib.sol, 105 + PUSHCONT { + PUSH S3 + NEQINT 0 } - POP S3 - .loc stdlib.sol, 188 - NEWC - POP S2 - .loc stdlib.sol, 189 -} -IFNOT -OVER -STSLICECONST x3a -POP S2 -.loc stdlib.sol, 191 -PUSHINT 64 -TRUE -DUP -FALSE -CALLREF { - .inline __convertIntToHexStr_macro -} -.loc stdlib.sol, 0 - -.macro convertFixedPointToString_macro -.loc stdlib.sol, 195 -OVER -ABS -.loc stdlib.sol, 196 -PUSHINT 10 -PUSH3 S2, S0, S2 -OR -THROWIFNOT 69 -CALLREF { - .inline ____exp_macro -} -DIVMOD -.loc stdlib.sol, 197 -BLKPUSH 2, 5 -ROLL 3 -PUSHINT 0 -DUP -ROLL 7 -SGN -LESSINT 0 -CALLREF { - .inline __convertIntToDecStr_macro -} -POP S4 -POP S4 -.loc stdlib.sol, 198 -PUSH S2 -BREMBITS -.loc stdlib.sol, 199 -GTINT 8 -PUSHCONT { - .loc stdlib.sol, 202 - OVER2 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP + PUSHCONT { + .loc stdlib.sol, 106 + OVER2 + DIVMOD + POP S2 + POP S4 + .loc stdlib.sol, 107 + DUP2 + CALLREF { + XCPU S1, S0 TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { - TPUSH - TUPLE 0 + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH - } - POP S4 - .loc stdlib.sol, 203 - NEWC - POP S3 - .loc stdlib.sol, 204 -} -IFNOT -PUSH S2 -STSLICECONST x2e -POP S3 -.loc stdlib.sol, 206 -SWAP -TRUE -FALSE -CALLREF { - .inline __convertIntToDecStr_macro -} -.loc stdlib.sol, 0 - -.macro convertIntToHexStr_short_macro -.loc stdlib.sol, 210 -ROTREV -PUSH S2 -ABS -PUSHINT 0 -DUP -TRUE -ROLL 6 -LESSINT 0 -CALLREF { - .inline __convertIntToHexStr_macro -} -.loc stdlib.sol, 0 - -.macro convertIntToHexStr_macro -.loc stdlib.sol, 214 -PUSH S5 -BREMBITS -RSHIFT 3 -.loc stdlib.sol, 215 -DUP -PUSHCONT { - .loc stdlib.sol, 216 - BLKPUSH 2, 7 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { - TPUSH TUPLE 0 } - IFNOT - } - PUSHCONT { - TUPLE 0 + IFELSE + ROT + TPUSH + TPUSH } - IFELSE - ROT - TPUSH - TPUSH + POP S2 + .loc stdlib.sol, 0 } - POP S8 - .loc stdlib.sol, 217 - NEWC - POP S7 - .loc stdlib.sol, 218 + WHILE DROP - PUSHINT 127 + BLKDROP2 2, 1 .loc stdlib.sol, 0 } -IFNOT -.loc stdlib.sol, 220 -ROLL 3 -PUSHINT 48 -PUSHINT 32 -CONDSEL -.loc stdlib.sol, 221 -ROT -PUSHCONT { - .loc stdlib.sol, 222 + +.fragment convertIntToHexStr, { + .loc stdlib.sol, 210 PUSH S5 - STSLICECONST x2d - POP S6 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 223 -OVER -DEC -POP S2 -.loc stdlib.sol, 224 -OVER -PUSHCONT { - .loc stdlib.sol, 225 - BLKPUSH 2, 6 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH - } - POP S7 - .loc stdlib.sol, 226 - NEWC - POP S6 - .loc stdlib.sol, 227 - PUSHINT 127 - POP S2 - .loc stdlib.sol, 0 -} -IFNOT -.loc stdlib.sol, 229 -ROLL 4 -PUSHINT 16 -CALLREF { - .inline __parseInteger_macro -} -.loc stdlib.sol, 230 -DUP -CALLREF { - DUP - TLEN + BREMBITS + RSHIFT 3 + .loc stdlib.sol, 211 DUP PUSHCONT { - DEC - PUSHPOW2DEC 8 - MUL - SWAP - LAST - TLEN - ADD - } - PUSHCONT { - NIP - } - IFELSE -} -.loc stdlib.sol, 232 -PUSH S5 -PUSHCONT { - .loc stdlib.sol, 233 - PUSH2 S5, S0 - LESS - PUSH S6 - GTINT 127 - OR - THROWIF 69 - .loc stdlib.sol, 235 - PUSH2 S5, S0 - SUB - .loc stdlib.sol, 236 - PUSH2 S0, S4 - LEQ - PUSHCONT { - .loc stdlib.sol, 237 - DUP - PUSHCONT { - .loc stdlib.sol, 238 - PUSH2 S7, S3 - STUR 8 - POP S8 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 240 - PUSH2 S4, S0 - SUB - POP S5 - .loc stdlib.sol, 241 - PUSH S4 - PUSHCONT { - .loc stdlib.sol, 242 - BLKPUSH 2, 8 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP - TLEN - PUSHPOW2DEC 8 - SUB - PUSHCONT { - TPUSH - TUPLE 0 - } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH - } - POP S9 - .loc stdlib.sol, 243 - NEWC - POP S8 - .loc stdlib.sol, 244 - PUSHINT 127 - POP S5 - .loc stdlib.sol, 0 - } - IFNOT - } - PUSHCONT { - .loc stdlib.sol, 247 - PUSH S4 - PUSHCONT { - .loc stdlib.sol, 248 - PUSH2 S7, S3 - STUR 8 - POP S8 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 250 - BLKPUSH 2, 8 + .loc stdlib.sol, 212 + BLKPUSH 2, 7 CALLREF { XCPU S1, S0 TLEN @@ -1083,753 +829,696 @@ PUSHCONT { TPUSH TPUSH } - POP S9 - .loc stdlib.sol, 251 - NEWC POP S8 - .loc stdlib.sol, 252 - PUSH2 S0, S4 - SUB - PUSHCONT { - .loc stdlib.sol, 253 - PUSH2 S7, S3 - STUR 8 - POP S8 - .loc stdlib.sol, 0 - } - REPEAT - .loc stdlib.sol, 255 + .loc stdlib.sol, 213 + NEWC + POP S7 + .loc stdlib.sol, 214 + DROP PUSHINT 127 - OVER - SUB + .loc stdlib.sol, 0 + } + IFNOT + .loc stdlib.sol, 216 + ROLL 3 + PUSHINT 48 + PUSHINT 32 + CONDSEL + .loc stdlib.sol, 217 + ROT + PUSHCONT { + .loc stdlib.sol, 218 PUSH S5 - ADD - POP S5 + STSLICECONST x2d + POP S6 + .loc stdlib.sol, 0 } - IFELSE - .loc stdlib.sol, 0 - DROP -} -IF -.loc stdlib.sol, 258 -PUSH2 S0, S3 -LEQ -PUSHCONT { - .loc stdlib.sol, 259 - DUP + IF + .loc stdlib.sol, 219 + OVER + DEC + POP S2 + .loc stdlib.sol, 220 + OVER PUSHCONT { - .loc stdlib.sol, 260 - OVER + .loc stdlib.sol, 221 + BLKPUSH 2, 6 CALLREF { - TPOP - TPOP - ROTREV - DUP + XCPU S1, S0 TLEN PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT } PUSHCONT { - DROP + TUPLE 0 } IFELSE + ROT + TPUSH + TPUSH } - POP S3 - .loc stdlib.sol, 261 + POP S7 + .loc stdlib.sol, 222 + NEWC + POP S6 + .loc stdlib.sol, 223 + PUSHINT 127 + POP S2 + .loc stdlib.sol, 0 + } + IFNOT + .loc stdlib.sol, 225 + ROLL 4 + PUSHINT 16 + CALLREF { + .inline parseInteger + } + .loc stdlib.sol, 226 + DUP + CALLREF { + DUP + TLEN DUP - LESSINT 10 PUSHCONT { - .loc stdlib.sol, 262 - PUSH S7 - PUSHINT 48 + DEC + PUSHPOW2DEC 8 + MUL + SWAP + LAST + TLEN + ADD } PUSHCONT { - .loc stdlib.sol, 264 - PUSH2 S7, S5 - PUSHINT 87 - PUSHINT 55 - CONDSEL + NIP } IFELSE - PUSH S2 - ADD - STUR 8 - POP S8 - .loc stdlib.sol, 0 - DROP } - REPEAT -} -PUSHCONT { - .loc stdlib.sol, 267 - PUSH S3 + .loc stdlib.sol, 228 + PUSH S5 PUSHCONT { - .loc stdlib.sol, 268 - OVER - CALLREF { - TPOP - TPOP - ROTREV + .loc stdlib.sol, 229 + PUSH2 S5, S0 + LESS + PUSH S6 + GTINT 127 + OR + THROWIF 69 + .loc stdlib.sol, 231 + PUSH2 S5, S0 + SUB + .loc stdlib.sol, 232 + PUSH2 S0, S4 + LEQ + PUSHCONT { + .loc stdlib.sol, 233 DUP - TLEN PUSHCONT { - TPUSH + .loc stdlib.sol, 234 + PUSH2 S7, S3 + STUR 8 + POP S8 + .loc stdlib.sol, 0 } + REPEAT + .loc stdlib.sol, 236 + PUSH2 S4, S0 + SUB + POP S5 + .loc stdlib.sol, 237 + PUSH S4 PUSHCONT { - DROP + .loc stdlib.sol, 238 + BLKPUSH 2, 8 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S9 + .loc stdlib.sol, 239 + NEWC + POP S8 + .loc stdlib.sol, 240 + PUSHINT 127 + POP S5 + .loc stdlib.sol, 0 } - IFELSE - } - POP S3 - .loc stdlib.sol, 269 - DUP - LESSINT 10 - PUSHCONT { - .loc stdlib.sol, 270 - PUSH S7 - PUSHINT 48 + IFNOT } PUSHCONT { - .loc stdlib.sol, 272 - PUSH2 S7, S5 - PUSHINT 87 - PUSHINT 55 - CONDSEL + .loc stdlib.sol, 243 + PUSH S4 + PUSHCONT { + .loc stdlib.sol, 244 + PUSH2 S7, S3 + STUR 8 + POP S8 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 246 + BLKPUSH 2, 8 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S9 + .loc stdlib.sol, 247 + NEWC + POP S8 + .loc stdlib.sol, 248 + PUSH2 S0, S4 + SUB + PUSHCONT { + .loc stdlib.sol, 249 + PUSH2 S7, S3 + STUR 8 + POP S8 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 251 + PUSHINT 127 + OVER + SUB + PUSH S5 + ADD + POP S5 } IFELSE - PUSH S2 - ADD - STUR 8 - POP S8 .loc stdlib.sol, 0 DROP } - REPEAT - .loc stdlib.sol, 274 - BLKPUSH 2, 7 - CALLREF { - XCPU S1, S0 - TLEN + IF + .loc stdlib.sol, 254 + PUSH2 S0, S3 + LEQ + PUSHCONT { + .loc stdlib.sol, 255 + DUP PUSHCONT { - TPOP + .loc stdlib.sol, 256 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 257 DUP - TLEN - PUSHPOW2DEC 8 - SUB + LESSINT 10 PUSHCONT { - TPUSH - TUPLE 0 + .loc stdlib.sol, 258 + PUSH S7 + PUSHINT 48 } - IFNOT - } - PUSHCONT { - TUPLE 0 + PUSHCONT { + .loc stdlib.sol, 260 + PUSH2 S7, S5 + PUSHINT 87 + PUSHINT 55 + CONDSEL + } + IFELSE + PUSH S2 + ADD + STUR 8 + POP S8 + .loc stdlib.sol, 0 + DROP } - IFELSE - ROT - TPUSH - TPUSH + REPEAT } - POP S8 - .loc stdlib.sol, 275 - NEWC - POP S7 - .loc stdlib.sol, 276 - PUSH2 S0, S3 - SUB PUSHCONT { - .loc stdlib.sol, 277 - OVER - CALLREF { - TPOP - TPOP - ROTREV + .loc stdlib.sol, 263 + PUSH S3 + PUSHCONT { + .loc stdlib.sol, 264 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 265 DUP - TLEN + LESSINT 10 PUSHCONT { - TPUSH + .loc stdlib.sol, 266 + PUSH S7 + PUSHINT 48 } PUSHCONT { - DROP + .loc stdlib.sol, 268 + PUSH2 S7, S5 + PUSHINT 87 + PUSHINT 55 + CONDSEL } IFELSE + PUSH S2 + ADD + STUR 8 + POP S8 + .loc stdlib.sol, 0 + DROP } - POP S3 - .loc stdlib.sol, 278 - DUP - LESSINT 10 - PUSHCONT { - .loc stdlib.sol, 279 - PUSH S7 - PUSHINT 48 - } - PUSHCONT { - .loc stdlib.sol, 281 - PUSH2 S7, S5 - PUSHINT 87 - PUSHINT 55 - CONDSEL - } - IFELSE - PUSH S2 - ADD - STUR 8 - POP S8 - .loc stdlib.sol, 0 - DROP - } - REPEAT -} -IFELSE -.loc stdlib.sol, 284 -BLKDROP 6 -.loc stdlib.sol, 0 - -.macro storeStringInBuilders_macro -.loc stdlib.sol, 288 -OVER -BREMBITS -ADDCONST -7 -.loc stdlib.sol, 289 -OVER -SBITREFS -.loc stdlib.sol, 290 -DUP -PUSHCONT { - .loc stdlib.sol, 291 - PUSH S3 - PUSHINT 0 - PUSH S2 - SSKIPFIRST - POP S4 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 292 -DROP -OVER -LEQ -.loc stdlib.sol, 293 -PUSHCONT { - .loc stdlib.sol, 296 - DUP2 - LDSLICEX - POP S3 - .loc stdlib.sol, 297 - PUSH S3 - STSLICE - POP S3 - .loc stdlib.sol, 298 - OVER2 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP + REPEAT + .loc stdlib.sol, 270 + BLKPUSH 2, 7 + CALLREF { + XCPU S1, S0 TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { TUPLE 0 } - IFNOT - } - PUSHCONT { - TUPLE 0 - } - IFELSE - ROT - TPUSH - TPUSH - } - POP S4 - .loc stdlib.sol, 299 - NEWC - POP S3 - .loc stdlib.sol, 300 -} -IFNOT -PUSH2 S1, S2 -STSLICE -BLKDROP2 3, 1 -.loc stdlib.sol, 0 - -.macro assembleList_macro -.loc stdlib.sol, 306 -PUSHCONT { - OVER - TLEN - ISZERO - NOT -} -PUSHCONT { - .loc stdlib.sol, 307 - OVER - CALLREF { - TPOP - TPOP - ROTREV - DUP - TLEN - PUSHCONT { + IFELSE + ROT + TPUSH TPUSH } + POP S8 + .loc stdlib.sol, 271 + NEWC + POP S7 + .loc stdlib.sol, 272 + PUSH2 S0, S3 + SUB PUSHCONT { + .loc stdlib.sol, 273 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 274 + DUP + LESSINT 10 + PUSHCONT { + .loc stdlib.sol, 275 + PUSH S7 + PUSHINT 48 + } + PUSHCONT { + .loc stdlib.sol, 277 + PUSH2 S7, S5 + PUSHINT 87 + PUSHINT 55 + CONDSEL + } + IFELSE + PUSH S2 + ADD + STUR 8 + POP S8 + .loc stdlib.sol, 0 DROP } - IFELSE + REPEAT } - POP S3 - .loc stdlib.sol, 308 - STBREF + IFELSE + .loc stdlib.sol, 280 + BLKDROP 6 .loc stdlib.sol, 0 } -WHILE -.loc stdlib.sol, 311 -ENDC -NIP -.loc stdlib.sol, 0 -.macro __stoi_macro -.loc stdlib.sol, 315 -CTOS -.loc stdlib.sol, 316 -DUP -SBITS -LESSINT 8 -PUSHCONT { - .loc stdlib.sol, 317 - DROP - NULL +.fragment convertIntToHexStr_short, { + .loc stdlib.sol, 206 + ROTREV + PUSH S2 + ABS + PUSHINT 0 + DUP + TRUE + ROLL 6 + LESSINT 0 + CALLREF { + .inline convertIntToHexStr + } .loc stdlib.sol, 0 } -IFJMP -.loc stdlib.sol, 319 -BLKPUSH 2, 0 -.loc stdlib.sol, 320 -LDU 8 -POP S2 -.loc stdlib.sol, 321 -DUP -EQINT 45 -.loc stdlib.sol, 322 -PUSHINT 0 -.loc stdlib.sol, 323 -PUSH S3 -SBITS -.loc stdlib.sol, 324 -PUSH2 S2, S0 -GTINT 15 -AND -PUSHCONT { - .loc stdlib.sol, 325 - PUSH S4 - LDU 8 - LDU 8 - POP S7 + +.fragment convertAddressToHexString, { + .loc stdlib.sol, 177 + REWRITESTDADDR + .loc stdlib.sol, 178 + OVER2 + ROLL 3 + CALLREF { + .inline convertIntToHexStr_short + } POP S3 - POP S4 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 327 -PUSH S2 -NOT -SWAP -GTINT 7 -AND -PUSHCONT { - .loc stdlib.sol, 328 - PUSH S3 - LDU 8 - POP S5 - NIP - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 330 -ROT -EQINT 48 -SWAP -EQINT 120 -AND -.loc stdlib.sol, 332 -OVER -PUSHCONT { - .loc stdlib.sol, 333 - PUSH S3 - LDU 8 - NIP - POP S4 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 334 -DUP -PUSHCONT { - .loc stdlib.sol, 335 - PUSH S3 - LDU 8 - LDU 8 - BLKDROP2 2, 1 - POP S4 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 337 -PUSHINT 0 -.loc stdlib.sol, 339 -PUSH S4 -SBITS -RSHIFT 3 -.loc stdlib.sol, 341 -FALSE ; decl return flag -ROLL 3 -PUSHCONT { - .loc stdlib.sol, 342 - FALSE ; decl return flag - PUSH S2 + POP S3 + .loc stdlib.sol, 179 + OVER + BREMBITS + .loc stdlib.sol, 180 + GTINT 8 PUSHCONT { - .loc stdlib.sol, 343 - PUSH S6 - LDU 8 - POP S8 - .loc stdlib.sol, 344 - PUSH S4 - MULCONST 16 - POP S5 - .loc stdlib.sol, 345 - DUP - GTINT 47 - OVER - LESSINT 58 - AND - PUSHCONT { - .loc stdlib.sol, 346 - DUP - ADDCONST -48 - PUSH S5 - ADD - POP S5 - .loc stdlib.sol, 0 - } - PUSHCONT { - DUP - GTINT 64 - OVER - LESSINT 71 - AND + .loc stdlib.sol, 183 + BLKPUSH 2, 2 + CALLREF { + XCPU S1, S0 + TLEN PUSHCONT { - .loc stdlib.sol, 348 + TPOP DUP - ADDCONST -55 - PUSH S5 - ADD - POP S5 - .loc stdlib.sol, 0 + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT } PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S3 + .loc stdlib.sol, 184 + NEWC + POP S2 + .loc stdlib.sol, 185 + } + IFNOT + OVER + STSLICECONST x3a + POP S2 + .loc stdlib.sol, 187 + PUSHINT 64 + TRUE + DUP + FALSE + CALLREF { + .inline convertIntToHexStr + } + .loc stdlib.sol, 0 +} + +.fragment convertIntToDecStr, { + .loc stdlib.sol, 112 + PUSH S4 + BREMBITS + RSHIFT 3 + .loc stdlib.sol, 113 + DUP + PUSHCONT { + .loc stdlib.sol, 114 + BLKPUSH 2, 6 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP DUP - GTINT 96 - OVER - LESSINT 103 - AND - PUSHCONT { - .loc stdlib.sol, 350 - DUP - ADDCONST -87 - PUSH S5 - ADD - POP S5 - .loc stdlib.sol, 0 - } + TLEN + PUSHPOW2DEC 8 + SUB PUSHCONT { - .loc stdlib.sol, 352 - BLKDROP 8 - NULL - PUSHINT 4 - RETALT - .loc stdlib.sol, 0 + TPUSH + TUPLE 0 } - IFELSE + IFNOT + } + PUSHCONT { + TUPLE 0 } IFELSE + ROT + TPUSH + TPUSH } - IFELSE + POP S7 + .loc stdlib.sol, 115 + NEWC + POP S6 + .loc stdlib.sol, 116 DROP + PUSHINT 127 .loc stdlib.sol, 0 } - REPEATBRK - DUP - IFRET - DROP - .loc stdlib.sol, 0 -} -PUSHCONT { - .loc stdlib.sol, 356 - FALSE ; decl return flag - PUSH S2 + IFNOT + .loc stdlib.sol, 118 + ROT + PUSHINT 48 + PUSHINT 32 + CONDSEL + .loc stdlib.sol, 119 + ROT PUSHCONT { - .loc stdlib.sol, 357 - PUSH S6 - LDU 8 - POP S8 - .loc stdlib.sol, 358 - DUP - LESSINT 48 - OVER - GTINT 57 - OR - PUSHCONT { - BLKDROP 8 - NULL - PUSHINT 4 - RETALT - } - IFJMP - .loc stdlib.sol, 360 + .loc stdlib.sol, 120 PUSH S4 - MULCONST 10 + STSLICECONST x2d POP S5 - .loc stdlib.sol, 361 - ADDCONST -48 - PUSH S4 - ADD - POP S4 .loc stdlib.sol, 0 } - REPEATBRK - DUP - IFRET - DROP - .loc stdlib.sol, 0 -} -IFELSE -IFRET -.loc stdlib.sol, 364 -DROP -SWAP -PUSHCONT { - .loc stdlib.sol, 365 - NEGATE - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 366 -BLKDROP2 2, 1 -.loc stdlib.sol, 0 - -.macro strToList_macro -.loc stdlib.sol, 370 -TUPLE 0 -.loc stdlib.sol, 371 -SWAP -CTOS -.loc stdlib.sol, 372 -PUSHCONT { - DUP - SREFS - NEQINT 0 -} -PUSHCONT { - .loc stdlib.sol, 373 - LDREFRTOS - .loc stdlib.sol, 375 - SWAP - NEWC - STSLICE - .loc stdlib.sol, 376 - PUXC S2, S-1 - CALLREF { - XCPU S1, S0 - TLEN - PUSHCONT { - TPOP - DUP + IF + .loc stdlib.sol, 121 + OVER + DEC + POP S2 + .loc stdlib.sol, 122 + OVER + PUSHCONT { + .loc stdlib.sol, 123 + BLKPUSH 2, 5 + CALLREF { + XCPU S1, S0 TLEN - PUSHPOW2DEC 8 - SUB PUSHCONT { - TPUSH + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { TUPLE 0 } - IFNOT - } - PUSHCONT { - TUPLE 0 + IFELSE + ROT + TPUSH + TPUSH } - IFELSE - ROT - TPUSH - TPUSH + POP S6 + .loc stdlib.sol, 124 + NEWC + POP S5 + .loc stdlib.sol, 125 + PUSHINT 127 + POP S2 + .loc stdlib.sol, 0 } - POP S2 - .loc stdlib.sol, 0 -} -WHILE -.loc stdlib.sol, 380 -NEWC -STSLICE -.loc stdlib.sol, 0 - -.macro bytes_substr_macro -.loc stdlib.sol, 385 -PUSH S3 -PUSHPOW2DEC 32 -CDATASIZE -DROP -NIP -.loc stdlib.sol, 386 -RSHIFT 3 -.loc stdlib.sol, 387 -SWAP -PUSHCONT { - .loc stdlib.sol, 388 + IFNOT + .loc stdlib.sol, 127 + ROLL 3 + PUSHINT 10 + CALLREF { + .inline parseInteger + } + .loc stdlib.sol, 128 DUP - POP S2 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 389 -PUSH2 S1, S2 -GEQ -THROWIFNOT 70 -.loc stdlib.sol, 390 -XCPU S1, S2 -SUB -.loc stdlib.sol, 391 -SWAP -BLKPUSH 2, 2 -ADD -GEQ -THROWIFNOT 70 -.loc stdlib.sol, 392 -CALLREF { - .inline ____subCell_macro -} -.loc stdlib.sol, 0 - -.macro __substr_macro -.loc stdlib.sol, 396 -PUSH S2 -PUSHPOW2DEC 32 -CDATASIZE -DROP -NIP -.loc stdlib.sol, 397 -RSHIFT 3 -.loc stdlib.sol, 398 -OVER -EQINT -1 -PUSHCONT { - .loc stdlib.sol, 399 - PUSH2 S0, S2 - SUB - FITS 256 - POP S2 - .loc stdlib.sol, 0 -} -IF -.loc stdlib.sol, 400 -BLKPUSH 2, 2 -UFITS 256 -ADD -GEQ -THROWIFNOT 70 -.loc stdlib.sol, 401 -UFITS 256 -CALLREF { - .inline ____subCell_macro -} -.loc stdlib.sol, 0 - -.macro __subCell_macro -.loc stdlib.sol, 404 -PUSHREF { -} -.loc stdlib.sol, 405 -OVER -PUSHCONT { - .loc stdlib.sol, 406 - BLKDROP2 3, 1 - .loc stdlib.sol, 0 -} -IFNOTJMP -.loc stdlib.sol, 408 -DROP -SWAP -PUSHINT 127 -DIVMOD -.loc stdlib.sol, 409 -ROLL 3 -CTOS -.loc stdlib.sol, 410 -ROT -PUSHCONT { - .loc stdlib.sol, 411 - LDREFRTOS - NIP - .loc stdlib.sol, 0 -} -REPEAT -.loc stdlib.sol, 413 -SWAP -MULCONST 8 -SDSKIPFIRST -.loc stdlib.sol, 414 -TUPLE 0 -.loc stdlib.sol, 415 -NEWC -.loc stdlib.sol, 416 -PUSHCONT { - PUSH S3 - NEQINT 0 -} -PUSHCONT { - .loc stdlib.sol, 417 - OVER2 - SBITS - RSHIFT 3 - PUSH S2 - BREMBITS - RSHIFT 3 - MIN - MIN - .loc stdlib.sol, 418 - PUSH3 S1, S3, S0 - LSHIFT 3 - UFITS 10 - LDSLICEX - POP S6 - STSLICER - POP S2 - .loc stdlib.sol, 419 + CALLREF { + DUP + TLEN + DUP + PUSHCONT { + DEC + PUSHPOW2DEC 8 + MUL + SWAP + LAST + TLEN + ADD + } + PUSHCONT { + NIP + } + IFELSE + } + .loc stdlib.sol, 129 PUSH S4 - SUBR - POP S4 - .loc stdlib.sol, 420 - PUSH S3 PUSHCONT { - .loc stdlib.sol, 421 - PUSH S2 - SBITS + .loc stdlib.sol, 130 + PUSH2 S4, S0 + LESS + PUSH S5 + GTINT 127 + OR + THROWIF 66 + .loc stdlib.sol, 132 + PUSH2 S4, S0 + SUB + .loc stdlib.sol, 133 + PUSH2 S0, S4 + LEQ PUSHCONT { - .loc stdlib.sol, 422 - PUSH S2 - LDREFRTOS - NIP - POP S3 - .loc stdlib.sol, 0 + .loc stdlib.sol, 134 + DUP + PUSHCONT { + .loc stdlib.sol, 135 + PUSH2 S6, S3 + STUR 8 + POP S7 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 137 + PUSH2 S4, S0 + SUB + POP S5 + .loc stdlib.sol, 138 + PUSH S4 + PUSHCONT { + .loc stdlib.sol, 139 + BLKPUSH 2, 7 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S8 + .loc stdlib.sol, 140 + NEWC + POP S7 + .loc stdlib.sol, 141 + PUSHINT 127 + POP S5 + .loc stdlib.sol, 0 + } + IFNOT } - IFNOT - .loc stdlib.sol, 424 - DUP - BREMBITS - LESSINT 8 PUSHCONT { - .loc stdlib.sol, 425 + .loc stdlib.sol, 144 + PUSH S4 + PUSHCONT { + .loc stdlib.sol, 145 + PUSH2 S6, S3 + STUR 8 + POP S7 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 147 + BLKPUSH 2, 7 CALLREF { XCPU S1, S0 TLEN @@ -1853,728 +1542,1063 @@ PUSHCONT { TPUSH TPUSH } - .loc stdlib.sol, 426 + POP S8 + .loc stdlib.sol, 148 NEWC - .loc stdlib.sol, 0 + POP S7 + .loc stdlib.sol, 149 + PUSH2 S0, S4 + SUB + PUSHCONT { + .loc stdlib.sol, 150 + PUSH2 S6, S3 + STUR 8 + POP S7 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 152 + PUSHINT 127 + OVER + SUB + PUSH S5 + ADD + POP S5 } - IF + IFELSE .loc stdlib.sol, 0 + DROP } IF + .loc stdlib.sol, 155 + PUSH2 S0, S3 + LEQ + PUSHCONT { + .loc stdlib.sol, 156 + DUP + PUSHCONT { + .loc stdlib.sol, 157 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 158 + PUSH S6 + PUSHINT 48 + ROT + ADD + STUR 8 + POP S6 + .loc stdlib.sol, 0 + } + REPEAT + } + PUSHCONT { + .loc stdlib.sol, 161 + PUSH S3 + PUSHCONT { + .loc stdlib.sol, 162 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 163 + PUSH S6 + PUSHINT 48 + ROT + ADD + STUR 8 + POP S6 + .loc stdlib.sol, 0 + } + REPEAT + .loc stdlib.sol, 165 + BLKPUSH 2, 6 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S7 + .loc stdlib.sol, 166 + NEWC + POP S6 + .loc stdlib.sol, 167 + PUSH2 S0, S3 + SUB + PUSHCONT { + .loc stdlib.sol, 168 + OVER + CALLREF { + TPOP + TPOP + ROTREV + DUP + TLEN + PUSHCONT { + TPUSH + } + PUSHCONT { + DROP + } + IFELSE + } + POP S3 + .loc stdlib.sol, 169 + PUSH S6 + PUSHINT 48 + ROT + ADD + STUR 8 + POP S6 + .loc stdlib.sol, 0 + } + REPEAT + } + IFELSE + .loc stdlib.sol, 173 + BLKDROP 5 .loc stdlib.sol, 0 } -WHILE -.loc stdlib.sol, 430 -CALLREF { - .inline __assembleList_macro -} -BLKDROP2 2, 1 -.loc stdlib.sol, 0 -.macro compareLongStrings_macro -.loc stdlib.sol, 437 -SWAP -CTOS -.loc stdlib.sol, 438 -SWAP -CTOS -.loc stdlib.sol, 439 -FALSE ; decl return flag -PUSHCONT { - .loc stdlib.sol, 440 - BLKPUSH 2, 2 - SDLEXCMP - .loc stdlib.sol, 441 +.fragment convertFixedPointToString, { + .loc stdlib.sol, 191 + OVER + ABS + .loc stdlib.sol, 192 + PUSHINT 10 + PUSH3 S2, S0, S2 + OR + THROWIFNOT 69 + CALLREF { + .inline __exp + } + DIVMOD + .loc stdlib.sol, 193 + BLKPUSH 2, 5 + ROLL 3 + PUSHINT 0 DUP - PUSHCONT { - .loc stdlib.sol, 442 - BLKDROP2 3, 1 - PUSHINT 4 - RETALT - .loc stdlib.sol, 0 + ROLL 7 + SGN + LESSINT 0 + CALLREF { + .inline convertIntToDecStr } - IFJMP - .loc stdlib.sol, 444 - DROP - PUSH S2 - SREFS - .loc stdlib.sol, 445 + POP S4 + POP S4 + .loc stdlib.sol, 194 PUSH S2 - SREFS - .loc stdlib.sol, 446 - DUP2 - GREATER - PUSHCONT { - BLKDROP 5 - PUSHINT 1 - PUSHINT 4 - RETALT - } - IFJMP - .loc stdlib.sol, 448 - PUSH2 S0, S1 - GREATER - PUSHCONT { - BLKDROP 5 - PUSHINT -1 - PUSHINT 4 - RETALT - } - IFJMP - .loc stdlib.sol, 450 - ADD + BREMBITS + .loc stdlib.sol, 195 + GTINT 8 PUSHCONT { - BLKDROP 3 - PUSHINT 0 - PUSHINT 4 - RETALT + .loc stdlib.sol, 198 + OVER2 + CALLREF { + XCPU S1, S0 + TLEN + PUSHCONT { + TPOP + DUP + TLEN + PUSHPOW2DEC 8 + SUB + PUSHCONT { + TPUSH + TUPLE 0 + } + IFNOT + } + PUSHCONT { + TUPLE 0 + } + IFELSE + ROT + TPUSH + TPUSH + } + POP S4 + .loc stdlib.sol, 199 + NEWC + POP S3 + .loc stdlib.sol, 200 } - IFNOTJMP - .loc stdlib.sol, 452 + IFNOT PUSH S2 - LDREFRTOS - NIP + STSLICECONST x2e POP S3 - .loc stdlib.sol, 453 - OVER - LDREFRTOS - NIP - POP S2 - .loc stdlib.sol, 0 -} -AGAINBRK -IFRET -.loc stdlib.sol, 455 -DROP2 -PUSHINT 0 -.loc stdlib.sol, 0 - -.macro concatenateStrings_macro -.loc stdlib.sol, 459 -SWAP -CALLREF { - .inline __strToList_macro -} -.loc stdlib.sol, 460 -ROT -CTOS -.loc stdlib.sol, 461 -BLKPUSH 3, 2 -CALLREF { - .inline __storeStringInBuilders_macro -} -POP S3 -POP S3 -.loc stdlib.sol, 462 -PUSHCONT { - DUP - PUSHINT 1 - SCHKREFSQ -} -PUSHCONT { - .loc stdlib.sol, 463 - LDREFRTOS - NIP - .loc stdlib.sol, 464 - BLKPUSH 3, 2 + .loc stdlib.sol, 202 + SWAP + TRUE + FALSE CALLREF { - .inline __storeStringInBuilders_macro + .inline convertIntToDecStr } - POP S3 - POP S3 .loc stdlib.sol, 0 } -WHILE -.loc stdlib.sol, 466 -DROP -CALLREF { - .inline __assembleList_macro -} -.loc stdlib.sol, 0 -.macro __strchr_macro -.loc stdlib.sol, 469 -NULL -.loc stdlib.sol, 470 -PUSHINT 0 -.loc stdlib.sol, 471 -ROLL 3 -CTOS -NULL -FALSE ; decl return flag -PUSHCONT { - PUSH S2 - SEMPTY - NOT -} -PUSHCONT { - PUSH S2 - LDUQ 8 +.fragment insert_pubkey, { + .loc stdlib.sol, 14 + SWAP + CTOS + .loc stdlib.sol, 15 + NEWC + .loc stdlib.sol, 18 + OVER + LDI 1 + POP S3 PUSHCONT { - PLDREF - CTOS - LDU 8 + .loc stdlib.sol, 19 + STSLICECONST 1 + .loc stdlib.sol, 20 + OVER + LDU 32 + POP S3 + .loc stdlib.sol, 21 + SWAP + STSLICECONST 1 + STU 32 } - IFNOT - POP S4 - POP S2 - .loc stdlib.sol, 472 - PUSH2 S1, S5 - EQUAL - PUSHCONT { - .loc stdlib.sol, 473 - ROLL 3 - BLKDROP2 5, 1 - PUSHINT 4 - RETALT - .loc stdlib.sol, 0 + PUSHCONT { + .loc stdlib.sol, 23 + STSLICECONST 0 } - IFJMP - .loc stdlib.sol, 475 - PUSH S3 - INC - POP S4 - .loc stdlib.sol, 0 -} -WHILEBRK -IFRET -BLKDROP 3 -NIP -.loc stdlib.sol, 0 - -.macro __strrchr_macro -.loc stdlib.sol, 479 -NULL -.loc stdlib.sol, 480 -PUSHINT 0 -.loc stdlib.sol, 481 -ROLL 3 -CTOS -NULL -PUSHCONT { - OVER - SEMPTY - NOT -} -PUSHCONT { + IFELSE + .loc stdlib.sol, 27 OVER - LDUQ 8 + LDI 1 + POP S3 PUSHCONT { - PLDREF - CTOS - LDU 8 + .loc stdlib.sol, 28 + STSLICECONST 1 + .loc stdlib.sol, 29 + OVER + LDI 1 + LDI 1 + POP S4 + .loc stdlib.sol, 30 + XCHG S2 + STI 1 + STI 1 } - IFNOT + PUSHCONT { + .loc stdlib.sol, 32 + STSLICECONST 0 + } + IFELSE + .loc stdlib.sol, 36 + OVER + LDDICT POP S3 - NIP - .loc stdlib.sol, 482 - PUSH2 S0, S4 - EQUAL + SWAP + STDICT + .loc stdlib.sol, 40 + NEWDICT + .loc stdlib.sol, 41 + PUSH S2 + LDI 1 + POP S4 PUSHCONT { - .loc stdlib.sol, 483 + .loc stdlib.sol, 42 PUSH S2 + LDREFRTOS + SWAP POP S4 + .loc stdlib.sol, 43 + PLDDICT + NIP .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 485 - PUSH S2 - INC + .loc stdlib.sol, 45 + ROLL 3 + PUSHINT 0 + ROTREV + NEWC + STU 256 + ROTREV + PUSHINT 64 + DICTUSETB + .loc stdlib.sol, 47 + NEWC + STDICT + .loc stdlib.sol, 48 + OVER + STSLICECONST 1 + POP S2 + .loc stdlib.sol, 49 + STBREFR + .loc stdlib.sol, 53 + OVER + LDDICT POP S3 + SWAP + STDICT + .loc stdlib.sol, 55 + SWAP + SEMPTY + THROWIFNOT 55 + .loc stdlib.sol, 57 + ENDC .loc stdlib.sol, 0 } -WHILE -BLKDROP 3 -NIP -.loc stdlib.sol, 0 -.macro __strstr_macro -.loc stdlib.sol, 514 -NULL -.loc stdlib.sol, 515 -PUSH S2 -CTOS -.loc stdlib.sol, 516 -PUSH S2 -CTOS -.loc stdlib.sol, 517 -DUP -PUSHPOW2DEC 32 -SDATASIZE -DROP -NIP -.loc stdlib.sol, 518 -PUSH S2 -PUSHPOW2DEC 32 -SDATASIZE -DROP -NIP -.loc stdlib.sol, 519 -DUP2 -GREATER -PUSHCONT { - .loc stdlib.sol, 520 - ROLL 4 - BLKDROP2 6, 1 +.fragment replay_protection, { + .loc stdlib.sol, 61 + GETGLOB 3 + OVER + LESS + THROWIFNOT 52 + .loc stdlib.sol, 62 + DUP + NOW + PUSHINT 1000 + MUL + PUSHINT 1800000 + ADD + LESS + THROWIFNOT 52 + .loc stdlib.sol, 63 + SETGLOB 3 .loc stdlib.sol, 0 } -IFJMP -.loc stdlib.sol, 522 -OVER -RSHIFT 3 -POP S2 -.loc stdlib.sol, 523 -RSHIFT 3 -.loc stdlib.sol, 524 -PUSH2 S0, S2 -.loc stdlib.sol, 525 -LDU 8 -POP S5 -.loc stdlib.sol, 526 -FALSE ; decl return flag -PUSHCONT { - PUSH2 S3, S4 - GEQ -} -PUSHCONT { - .loc stdlib.sol, 527 - PUSH S6 - LDU 8 - POP S8 - .loc stdlib.sol, 528 - PUSH S7 - SBITREFS - .loc stdlib.sol, 529 - OVER + +.fragment __stoi, { + .loc stdlib.sol, 311 + CTOS + .loc stdlib.sol, 312 + DUP + SBITS LESSINT 8 - OVER - GTINT 0 + PUSHCONT { + .loc stdlib.sol, 313 + DROP + NULL + .loc stdlib.sol, 0 + } + IFJMP + .loc stdlib.sol, 315 + BLKPUSH 2, 0 + .loc stdlib.sol, 316 + LDU 8 + POP S2 + .loc stdlib.sol, 317 + DUP + EQINT 45 + .loc stdlib.sol, 318 + PUSHINT 0 + .loc stdlib.sol, 319 + PUSH S3 + SBITS + .loc stdlib.sol, 320 + PUSH2 S2, S0 + GTINT 15 AND PUSHCONT { - .loc stdlib.sol, 530 - PUSH S9 - LDREFRTOS + .loc stdlib.sol, 321 + PUSH S4 + LDU 8 + LDU 8 + POP S7 + POP S3 + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 323 + PUSH S2 + NOT + SWAP + GTINT 7 + AND + PUSHCONT { + .loc stdlib.sol, 324 + PUSH S3 + LDU 8 + POP S5 + NIP + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 326 + ROT + EQINT 48 + SWAP + EQINT 120 + AND + .loc stdlib.sol, 328 + OVER + PUSHCONT { + .loc stdlib.sol, 329 + PUSH S3 + LDU 8 NIP - POP S10 + POP S4 .loc stdlib.sol, 0 } IF - .loc stdlib.sol, 532 - PUSH2 S2, S4 - EQUAL + .loc stdlib.sol, 330 + DUP + PUSHCONT { + .loc stdlib.sol, 331 + PUSH S3 + LDU 8 + LDU 8 + BLKDROP2 2, 1 + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 333 + PUSHINT 0 + .loc stdlib.sol, 335 + PUSH S4 + SBITS + RSHIFT 3 + .loc stdlib.sol, 337 + FALSE ; decl return flag + ROLL 3 PUSHCONT { - .loc stdlib.sol, 533 - BLKPUSH 2, 9 + .loc stdlib.sol, 338 + FALSE ; decl return flag + PUSH S2 PUSHCONT { - .loc stdlib.sol, 491 - OVER - SBITS - .loc stdlib.sol, 492 + .loc stdlib.sol, 339 + PUSH S6 + LDU 8 + POP S8 + .loc stdlib.sol, 340 + PUSH S4 + MULCONST 16 + POP S5 + .loc stdlib.sol, 341 + DUP + GTINT 47 OVER - SBITS - .loc stdlib.sol, 493 - FALSE ; decl return flag + LESSINT 58 + AND PUSHCONT { - PUSH S2 - NEQINT 0 - PUSH S2 - NEQINT 0 - AND + .loc stdlib.sol, 342 + DUP + ADDCONST -48 + PUSH S5 + ADD + POP S5 + .loc stdlib.sol, 0 } PUSHCONT { - .loc stdlib.sol, 494 - BLKPUSH 2, 2 - MIN - .loc stdlib.sol, 495 - PUSH2 S5, S0 - LDSLICEX - POP S7 - .loc stdlib.sol, 496 - PUSH2 S5, S1 - LDSLICEX - POP S7 - .loc stdlib.sol, 497 - PUSH2 S5, S2 - SUB - POP S6 - .loc stdlib.sol, 498 - ROT - PUSH S4 - SUBR - POP S4 - .loc stdlib.sol, 499 - SDEQ - PUSHCONT { - .loc stdlib.sol, 500 - BLKDROP 5 - FALSE - PUSHINT 4 - RETALT - .loc stdlib.sol, 0 - } - IFNOTJMP - .loc stdlib.sol, 502 - PUSH S2 - EQINT 0 - PUSH S5 - SREFS - NEQINT 0 + DUP + GTINT 64 + OVER + LESSINT 71 AND PUSHCONT { - .loc stdlib.sol, 503 - PUSH S4 - LDREFRTOS - NIP + .loc stdlib.sol, 344 + DUP + ADDCONST -55 + PUSH S5 + ADD POP S5 - .loc stdlib.sol, 504 - PUSH S4 - SBITS - POP S3 .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 506 - OVER - EQINT 0 - PUSH S4 - SREFS - NEQINT 0 - AND PUSHCONT { - .loc stdlib.sol, 507 - PUSH S3 - LDREFRTOS - NIP - POP S4 - .loc stdlib.sol, 508 - PUSH S3 - SBITS - POP S2 - .loc stdlib.sol, 0 + DUP + GTINT 96 + OVER + LESSINT 103 + AND + PUSHCONT { + .loc stdlib.sol, 346 + DUP + ADDCONST -87 + PUSH S5 + ADD + POP S5 + .loc stdlib.sol, 0 + } + PUSHCONT { + .loc stdlib.sol, 348 + BLKDROP 8 + NULL + PUSHINT 4 + RETALT + .loc stdlib.sol, 0 + } + IFELSE } - IF - .loc stdlib.sol, 0 + IFELSE } - WHILEBRK - IFRET - .loc stdlib.sol, 511 - BLKDROP 4 - TRUE - .loc stdlib.sol, 490 + IFELSE + DROP + .loc stdlib.sol, 0 + } + REPEATBRK + DUP + IFRET + DROP + .loc stdlib.sol, 0 + } + PUSHCONT { + .loc stdlib.sol, 352 + FALSE ; decl return flag + PUSH S2 + PUSHCONT { + .loc stdlib.sol, 353 + PUSH S6 + LDU 8 + POP S8 + .loc stdlib.sol, 354 + DUP + LESSINT 48 + OVER + GTINT 57 + OR + PUSHCONT { + BLKDROP 8 + NULL + PUSHINT 4 + RETALT + } + IFJMP + .loc stdlib.sol, 356 + PUSH S4 + MULCONST 10 + POP S5 + .loc stdlib.sol, 357 + ADDCONST -48 + PUSH S4 + ADD + POP S4 + .loc stdlib.sol, 0 + } + REPEATBRK + DUP + IFRET + DROP + .loc stdlib.sol, 0 + } + IFELSE + IFRET + .loc stdlib.sol, 360 + DROP + SWAP + PUSHCONT { + .loc stdlib.sol, 361 + NEGATE + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 362 + BLKDROP2 2, 1 + .loc stdlib.sol, 0 +} + +.fragment compareLongStrings, { + .loc stdlib.sol, 439 + SWAP + CTOS + .loc stdlib.sol, 440 + SWAP + CTOS + .loc stdlib.sol, 441 + FALSE ; decl return flag + PUSHCONT { + .loc stdlib.sol, 442 + BLKPUSH 2, 2 + SDLEXCMP + .loc stdlib.sol, 443 + DUP + PUSHCONT { + .loc stdlib.sol, 444 + BLKDROP2 3, 1 + PUSHINT 4 + RETALT + .loc stdlib.sol, 0 + } + IFJMP + .loc stdlib.sol, 446 + DROP + PUSH S2 + SREFS + .loc stdlib.sol, 447 + PUSH S2 + SREFS + .loc stdlib.sol, 448 + DUP2 + GREATER + PUSHCONT { + BLKDROP 5 + PUSHINT 1 + PUSHINT 4 + RETALT + } + IFJMP + .loc stdlib.sol, 450 + PUSH2 S0, S1 + GREATER + PUSHCONT { + BLKDROP 5 + PUSHINT -1 + PUSHINT 4 + RETALT + } + IFJMP + .loc stdlib.sol, 452 + ADD + PUSHCONT { + BLKDROP 3 + PUSHINT 0 + PUSHINT 4 + RETALT } - CALLX + IFNOTJMP + .loc stdlib.sol, 454 + PUSH S2 + LDREFRTOS + NIP + POP S3 + .loc stdlib.sol, 455 + OVER + LDREFRTOS + NIP + POP S2 .loc stdlib.sol, 0 + } + AGAINBRK + IFRET + .loc stdlib.sol, 457 + DROP2 + PUSHINT 0 + .loc stdlib.sol, 0 +} + +.fragment __strchr, { + .loc stdlib.sol, 471 + NULL + .loc stdlib.sol, 472 + PUSHINT 0 + .loc stdlib.sol, 473 + ROLL 3 + CTOS + NULL + FALSE ; decl return flag + PUSHCONT { + PUSH S2 + SEMPTY + NOT + } + PUSHCONT { + PUSH S2 + LDUQ 8 PUSHCONT { - .loc stdlib.sol, 534 - BLKSWAP 2, 5 - SUBR - UFITS 32 - POP S9 - .loc stdlib.sol, 535 - ROLL 8 - BLKDROP2 10, 1 + PLDREF + CTOS + LDU 8 + } + IFNOT + POP S4 + POP S2 + .loc stdlib.sol, 474 + PUSH2 S1, S5 + EQUAL + PUSHCONT { + .loc stdlib.sol, 475 + ROLL 3 + BLKDROP2 5, 1 PUSHINT 4 RETALT .loc stdlib.sol, 0 } IFJMP + .loc stdlib.sol, 477 + PUSH S3 + INC + POP S4 + .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 538 + WHILEBRK + IFRET BLKDROP 3 - PUSH S3 - DEC - POP S4 + NIP .loc stdlib.sol, 0 } -WHILEBRK -IFRET -.loc stdlib.sol, 540 -BLKDROP 6 -BLKDROP2 2, 1 -.loc stdlib.sol, 0 -.macro __toLowerCase_macro -.loc stdlib.sol, 549 -NEWC -NULL -PAIR -.loc stdlib.sol, 550 -PUSHINT 0 -.loc stdlib.sol, 551 -ROT -CTOS -NULL -PUSHCONT { - OVER - SEMPTY - NOT -} -PUSHCONT { - OVER - LDUQ 8 +.fragment __strrchr, { + .loc stdlib.sol, 481 + NULL + .loc stdlib.sol, 482 + PUSHINT 0 + .loc stdlib.sol, 483 + ROLL 3 + CTOS + NULL PUSHCONT { - PLDREF - CTOS - LDU 8 + OVER + SEMPTY + NOT } - IFNOT - POP S3 + PUSHCONT { + OVER + LDUQ 8 + PUSHCONT { + PLDREF + CTOS + LDU 8 + } + IFNOT + POP S3 + NIP + .loc stdlib.sol, 484 + PUSH2 S0, S4 + EQUAL + PUSHCONT { + .loc stdlib.sol, 485 + PUSH S2 + POP S4 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 487 + PUSH S2 + INC + POP S3 + .loc stdlib.sol, 0 + } + WHILE + BLKDROP 3 NIP + .loc stdlib.sol, 0 +} + +.fragment __toLowerCase, { + .loc stdlib.sol, 551 + NEWC + NULL + PAIR .loc stdlib.sol, 552 - BLKPUSH 2, 0 + PUSHINT 0 .loc stdlib.sol, 553 - GTINT 64 - OVER - LESSINT 91 - AND + ROT + CTOS + NULL PUSHCONT { - .loc stdlib.sol, 554 - ADDCONST 32 - .loc stdlib.sol, 0 + OVER + SEMPTY + NOT } - IF - .loc stdlib.sol, 556 - PUSH2 S4, S4 - FIRST - XCHG S1, S2 - STU 8 - SETINDEX 0 - POP S4 - .loc stdlib.sol, 557 - PUSH S2 - INC - POP S3 - .loc stdlib.sol, 558 - PUSH S2 - EQINT 127 PUSHCONT { + OVER + LDUQ 8 + PUSHCONT { + PLDREF + CTOS + LDU 8 + } + IFNOT + POP S3 + NIP + .loc stdlib.sol, 554 + BLKPUSH 2, 0 + .loc stdlib.sol, 555 + GTINT 64 + OVER + LESSINT 91 + AND + PUSHCONT { + .loc stdlib.sol, 556 + ADDCONST 32 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 558 + PUSH2 S4, S4 + FIRST + XCHG S1, S2 + STU 8 + SETINDEX 0 + POP S4 .loc stdlib.sol, 559 - NEWC + PUSH S2 + INC + POP S3 .loc stdlib.sol, 560 - PUSH S4 - PAIR - POP S4 + PUSH S2 + EQINT 127 + PUSHCONT { + .loc stdlib.sol, 561 + NEWC + .loc stdlib.sol, 562 + PUSH S4 + PAIR + POP S4 + .loc stdlib.sol, 0 + } + IF .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 0 -} -WHILE -BLKDROP 3 -.loc stdlib.sol, 563 -DUP -FIRST -.loc stdlib.sol, 564 -PUSHCONT { - OVER - SECOND - ISNULL - NOT -} -PUSHCONT { + WHILE + BLKDROP 3 .loc stdlib.sol, 565 - OVER - SECOND DUP - ISNULL - THROWIF 63 - POP S2 - .loc stdlib.sol, 566 - OVER FIRST - .loc stdlib.sol, 567 - STBREF - .loc stdlib.sol, 0 -} -WHILE -.loc stdlib.sol, 570 -ENDC -NIP -.loc stdlib.sol, 0 - -.macro __toUpperCase_macro -.loc stdlib.sol, 574 -NEWC -NULL -PAIR -.loc stdlib.sol, 575 -PUSHINT 0 -.loc stdlib.sol, 576 -ROT -CTOS -NULL -PUSHCONT { - OVER - SEMPTY - NOT -} -PUSHCONT { - OVER - LDUQ 8 + .loc stdlib.sol, 566 PUSHCONT { - PLDREF - CTOS - LDU 8 + OVER + SECOND + ISNULL + NOT } - IFNOT - POP S3 + PUSHCONT { + .loc stdlib.sol, 567 + OVER + SECOND + DUP + ISNULL + THROWIF 63 + POP S2 + .loc stdlib.sol, 568 + OVER + FIRST + .loc stdlib.sol, 569 + STBREF + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 572 + ENDC NIP + .loc stdlib.sol, 0 +} + +.fragment __toUpperCase, { + .loc stdlib.sol, 576 + NEWC + NULL + PAIR .loc stdlib.sol, 577 - BLKPUSH 2, 0 + PUSHINT 0 .loc stdlib.sol, 578 - GTINT 96 - OVER - LESSINT 123 - AND + ROT + CTOS + NULL PUSHCONT { - .loc stdlib.sol, 579 - ADDCONST -32 - .loc stdlib.sol, 0 + OVER + SEMPTY + NOT } - IF - .loc stdlib.sol, 581 - PUSH2 S4, S4 - FIRST - XCHG S1, S2 - STU 8 - SETINDEX 0 - POP S4 - .loc stdlib.sol, 582 - PUSH S2 - INC - POP S3 - .loc stdlib.sol, 583 - PUSH S2 - EQINT 127 PUSHCONT { + OVER + LDUQ 8 + PUSHCONT { + PLDREF + CTOS + LDU 8 + } + IFNOT + POP S3 + NIP + .loc stdlib.sol, 579 + BLKPUSH 2, 0 + .loc stdlib.sol, 580 + GTINT 96 + OVER + LESSINT 123 + AND + PUSHCONT { + .loc stdlib.sol, 581 + ADDCONST -32 + .loc stdlib.sol, 0 + } + IF + .loc stdlib.sol, 583 + PUSH2 S4, S4 + FIRST + XCHG S1, S2 + STU 8 + SETINDEX 0 + POP S4 .loc stdlib.sol, 584 - NEWC + PUSH S2 + INC + POP S3 .loc stdlib.sol, 585 - PUSH S4 - PAIR - POP S4 + PUSH S2 + EQINT 127 + PUSHCONT { + .loc stdlib.sol, 586 + NEWC + .loc stdlib.sol, 587 + PUSH S4 + PAIR + POP S4 + .loc stdlib.sol, 0 + } + IF .loc stdlib.sol, 0 } - IF - .loc stdlib.sol, 0 -} -WHILE -BLKDROP 3 -.loc stdlib.sol, 588 -DUP -FIRST -.loc stdlib.sol, 589 -PUSHCONT { - OVER - SECOND - ISNULL - NOT -} -PUSHCONT { + WHILE + BLKDROP 3 .loc stdlib.sol, 590 - OVER - SECOND DUP - ISNULL - THROWIF 63 - POP S2 - .loc stdlib.sol, 591 - OVER FIRST - .loc stdlib.sol, 592 - STBREF + .loc stdlib.sol, 591 + PUSHCONT { + OVER + SECOND + ISNULL + NOT + } + PUSHCONT { + .loc stdlib.sol, 592 + OVER + SECOND + DUP + ISNULL + THROWIF 63 + POP S2 + .loc stdlib.sol, 593 + OVER + FIRST + .loc stdlib.sol, 594 + STBREF + .loc stdlib.sol, 0 + } + WHILE + .loc stdlib.sol, 597 + ENDC + NIP .loc stdlib.sol, 0 } -WHILE -.loc stdlib.sol, 595 -ENDC -NIP -.loc stdlib.sol, 0 - -.macro stateInitHash_macro -.loc stdlib.sol, 599 -NEWC -.loc stdlib.sol, 601 -STSLICECONST x020134 -.loc stdlib.sol, 613 -ROT -STUR 16 -.loc stdlib.sol, 614 -STU 16 -.loc stdlib.sol, 616 -ROT -STUR 256 -.loc stdlib.sol, 617 -STU 256 -.loc stdlib.sol, 618 -ENDC -CTOS -SHA256U -.loc stdlib.sol, 0 -.macro __forwardFee_macro -.loc stdlib.sol, 622 -DEPTH -ADDCONST -3 -PICK -CTOS -.loc stdlib.sol, 623 -DUP -LDU 1 -POP S2 -.loc stdlib.sol, 624 -PUSHCONT { - .loc stdlib.sol, 635 - DROP - PUSHINT 0 - .loc stdlib.sol, 0 -} -PUSHCONT { - .loc stdlib.sol, 629 - LDU 3 - LDMSGADDR - LDMSGADDR - LDVARUINT16 - LDDICT - LDVARUINT16 - BLKDROP2 6, 1 - .loc stdlib.sol, 633 - LDVARUINT16 - DROP +.fragment stateInitHash, { + .loc stdlib.sol, 601 + NEWC + .loc stdlib.sol, 603 + STSLICECONST x020134 + .loc stdlib.sol, 615 + ROT + STUR 16 + .loc stdlib.sol, 616 + STU 16 + .loc stdlib.sol, 618 + ROT + STUR 256 + .loc stdlib.sol, 619 + STU 256 + .loc stdlib.sol, 620 + ENDC + CTOS + SHA256U .loc stdlib.sol, 0 } -IFELSE -.loc stdlib.sol, 0 -.macro __importFee_macro -.loc stdlib.sol, 640 -DEPTH -ADDCONST -3 -PICK -CTOS -.loc stdlib.sol, 641 -DUP -LDU 2 -POP S2 -.loc stdlib.sol, 642 -EQINT 2 -PUSHCONT { - .loc stdlib.sol, 645 - LDMSGADDR - LDMSGADDR - BLKDROP2 2, 1 - .loc stdlib.sol, 646 - LDVARUINT16 - DROP +.fragment __forwardFee, { + .loc stdlib.sol, 624 + DEPTH + ADDCONST -3 + PICK + CTOS + .loc stdlib.sol, 625 + DUP + LDU 1 + POP S2 + .loc stdlib.sol, 626 + PUSHCONT { + .loc stdlib.sol, 637 + DROP + PUSHINT 0 + .loc stdlib.sol, 0 + } + PUSHCONT { + .loc stdlib.sol, 631 + LDU 3 + LDMSGADDR + LDMSGADDR + LDVARUINT16 + LDDICT + LDVARUINT16 + BLKDROP2 6, 1 + .loc stdlib.sol, 635 + LDVARUINT16 + DROP + .loc stdlib.sol, 0 + } + IFELSE .loc stdlib.sol, 0 } -PUSHCONT { - .loc stdlib.sol, 648 - DROP - PUSHINT 0 + +.fragment __importFee, { + .loc stdlib.sol, 642 + DEPTH + ADDCONST -3 + PICK + CTOS + .loc stdlib.sol, 643 + DUP + LDU 2 + POP S2 + .loc stdlib.sol, 644 + EQINT 2 + PUSHCONT { + .loc stdlib.sol, 647 + LDMSGADDR + LDMSGADDR + BLKDROP2 2, 1 + .loc stdlib.sol, 648 + LDVARUINT16 + DROP + .loc stdlib.sol, 0 + } + PUSHCONT { + .loc stdlib.sol, 650 + DROP + PUSHINT 0 + .loc stdlib.sol, 0 + } + IFELSE .loc stdlib.sol, 0 } -IFELSE -.loc stdlib.sol, 0 diff --git a/sold/Cargo.toml b/sold/Cargo.toml index f454a2b8..05b9dad5 100644 --- a/sold/Cargo.toml +++ b/sold/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2021' name = 'sold' -version = '0.71.0' +version = '0.72.0' [[bin]] name = 'sold' @@ -11,22 +11,22 @@ path = 'src/main.rs' atty = '0.2' dunce = '1.0' failure = '0.1' -once_cell = '1.17' +once_cell = '1.18' serde_json = { features = [ 'unbounded_depth' ], version = '1.0' } -strip-ansi-escapes = '0.1' -clap = { features = [ 'derive' ], version = '4.2' } +strip-ansi-escapes = '0.2' +clap = { features = [ 'derive' ], version = '4.3' } serde = { features = [ 'derive' ], version = '1.0' } -ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.3.129' } -ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.88' } -ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.18' } -tvm_linker = { git = 'https://github.com/tonlabs/TVM-linker.git', tag = '0.20.4' } +ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.3.147' } +ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.104' } +ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.28' } +ton_labs_assembler = { features = [ 'gosh' ], git = 'https://github.com/tonlabs/ever-assembler.git', tag = '1.4.15' } [build-dependencies] cmake = '0.1' [dev-dependencies] assert_cmd = '2.0' -predicates = "3.0" +predicates = '3.0' [lib] name = 'sold_lib' diff --git a/sold/src/lib.rs b/sold/src/lib.rs index d4b94431..7d853b21 100644 --- a/sold/src/lib.rs +++ b/sold/src/lib.rs @@ -8,10 +8,8 @@ use clap::{ValueEnum, Parser}; use failure::{bail, format_err}; use serde::Deserialize; -use ton_block::Serializable; -use ton_types::{Result, SliceData, Status}; -use ton_utils::parser::{ParseEngine, ParseEngineInput}; -use ton_utils::program::Program; +use ton_types::{Result, Status}; +use ton_labs_assembler::{DbgInfo, Engine, Units}; mod libsolc; mod printer; @@ -205,7 +203,7 @@ fn parse_comp_result( let message = if atty::is(atty::Stream::Stderr) && !cfg!(target_family = "windows") { message.to_string() } else { - let bytes = strip_ansi_escapes::strip(message)?; + let bytes = strip_ansi_escapes::strip(message); String::from_utf8(bytes)? }; eprint!("{}", message); @@ -261,7 +259,7 @@ fn parse_comp_result( } } -static STDLIB: &[u8] = include_bytes!("../../lib/stdlib_sol.tvm"); +static STDLIB: &'static str = include_str!("../../lib/stdlib_sol.tvm"); fn parse_positional_args(args: Vec) -> Result<(String, Vec)> { let mut input = None; @@ -380,15 +378,23 @@ pub fn build(args: Args) -> Status { let mut inputs = Vec::new(); if let Some(lib) = args.lib { - let lib_file = File::open(&lib)?; - inputs.push(ParseEngineInput { buf: Box::new(lib_file), name: lib }); + let input = std::fs::read_to_string(lib.clone())?; + inputs.push((input, lib)); } else { - inputs.push(ParseEngineInput { buf: Box::new(STDLIB), name: String::from("stdlib_sol.tvm") }); + inputs.push((STDLIB.to_string(), String::from("stdlib_sol.tvm"))); } - inputs.push(ParseEngineInput { buf: Box::new(assembly.as_bytes()), name: format!("{}/{}", output_dir, assembly_file_name) }); - - let mut prog = Program::new(ParseEngine::new_generic(inputs, Some(format!("{}", abi)))?)?; - + inputs.push((assembly, format!("{}/{}", output_dir, assembly_file_name))); + + let mut engine = Engine::new(""); + let mut units = Units::new(); + for (input, filename) in inputs { + engine.reset(filename); + units = engine.compile_toplevel(&input) + .map_err(|e| format_err!("{}", e))?; + } + let (b, d) = units.finalize(); + let output = b.into_cell()?; + let dbgmap = DbgInfo::from(output.clone(), d); let output_filename = if output_dir == "." { output_tvc @@ -396,35 +402,13 @@ pub fn build(args: Args) -> Status { format!("{}/{}", output_dir, output_tvc) }; - prog.set_silent(args.silent); - prog.set_print_code(args.print_code); - - prog.compile_to_file_ex( - -1, - Some(&output_filename), - None, - )?; - if !args.print_code { - let mut dbg_file = File::create(format!("{}/{}.debug.json", output_dir, output_prefix))?; - serde_json::to_writer_pretty(&mut dbg_file, &prog.dbgmap)?; - writeln!(dbg_file)?; - } + let bytes = ton_types::write_boc(&output)?; + let mut file = File::create(output_filename)?; + file.write_all(&bytes)?; - if let Some(params_data) = args.init { - let mut state = ton_utils::program::load_from_file(&output_filename)?; - let new_data = ton_abi::json_abi::update_contract_data( - &serde_json::to_string(abi)?, - ¶ms_data, - SliceData::load_cell(state.data.clone().unwrap_or_default())?, - )?; - state.set_data(new_data.into_cell()); - - let root_cell = state.write_to_new_cell()?.into_cell()?; - let buffer = ton_types::write_boc(&root_cell)?; - - let mut file = File::create(&output_filename)?; - file.write_all(&buffer)?; - } + let mut dbg_file = File::create(format!("{}/{}.debug.json", output_dir, output_prefix))?; + serde_json::to_writer_pretty(&mut dbg_file, &dbgmap)?; + writeln!(dbg_file)?; Ok(()) } @@ -491,9 +475,6 @@ pub struct Args { pub tvm_version: Option, //Output Components: - /// Print the code cell to stdout - #[clap(long("print-code"), value_parser)] - pub print_code: bool, /// ABI specification of the contracts #[clap(long, value_parser)] pub abi_json: bool, @@ -512,12 +493,4 @@ pub struct Args { /// Natspec developer documentation of all contracts. #[clap(long, value_parser)] pub devdoc: bool, - - // TODO ? - /// Set newly generated keypair - #[clap(long, value_parser, value_names = &["FILENAME"])] - pub init: Option, - /// Mute all notifications - #[clap(long, value_parser)] - pub silent: bool, } diff --git a/sold/src/printer.rs b/sold/src/printer.rs index 22b2957f..36414d3b 100644 --- a/sold/src/printer.rs +++ b/sold/src/printer.rs @@ -76,6 +76,19 @@ fn print_data(out: &mut File, value: &serde_json::Value) -> Status { Ok(()) } +fn print_array(out: &mut File, array: &Vec) -> Status { + for i in 0..array.len() { + write!(out, "\t\t\t\t")?; + write!(out, "{}", to_string_pretty_no_indent(&array[i])?)?; + if i + 1 == array.len() { + writeln!(out)?; + } else { + writeln!(out, ",")?; + } + } + Ok(()) +} + fn print(out: &mut File, value: &serde_json::Value) -> Status { let json = value.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; for f in 0..json.len() { @@ -91,30 +104,14 @@ fn print(out: &mut File, value: &serde_json::Value) -> Status { writeln!(out, "\t\t\t\"inputs\": [")?; if let Some(inputs) = function.get("inputs") { let array = inputs.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; - for i in 0..array.len() { - write!(out, "\t\t\t\t")?; - write!(out, "{}", to_string_pretty_no_indent(&array[i])?)?; - if i + 1 == array.len() { - writeln!(out)?; - } else { - writeln!(out, ",")?; - } - } + print_array(out, array)?; } writeln!(out, "\t\t\t],")?; writeln!(out, "\t\t\t\"outputs\": [")?; if let Some(outputs) = function.get("outputs") { let array = outputs.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; - for o in 0..array.len() { - write!(out, "\t\t\t\t")?; - write!(out, "{}", to_string_pretty_no_indent(&array[o])?)?; - if o + 1 == array.len() { - writeln!(out)?; - } else { - writeln!(out, ",")?; - } - } + print_array(out, array)?; } writeln!(out, "\t\t\t]")?; diff --git a/sold/tests/tests.rs b/sold/tests/tests.rs index cd006523..36452097 100644 --- a/sold/tests/tests.rs +++ b/sold/tests/tests.rs @@ -31,17 +31,7 @@ fn test_trivial() -> Status { .arg("--output-dir") .arg("tests") .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); - - Command::cargo_bin(BIN_NAME)? - .arg("tests/Trivial.sol") - .arg("--output-dir") - .arg("tests") - .arg("--print-code") - .assert() - .success() - .stdout(predicate::str::contains("code\":\"te6ccgECDAEAA")); + .success(); remove_all_outputs("Trivial")?; Ok(()) @@ -54,8 +44,7 @@ fn test_combined() -> Status { .arg("--output-dir") .arg("tests") .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); + .success(); remove_all_outputs("Combined")?; Ok(()) @@ -70,8 +59,7 @@ fn test_multi() -> Status { .arg("--contract") .arg("Contract1") .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); + .success(); remove_all_outputs("Multi")?; Ok(()) @@ -145,32 +133,12 @@ fn test_cycle() -> Status { .arg("--base-path") .arg("tests") .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); + .success(); remove_all_outputs("CycleA")?; Ok(()) } -#[test] -fn test_init() -> Status { - Command::cargo_bin(BIN_NAME)? - .arg("tests/Init.sol") - .arg("--output-dir") - .arg("tests") - .arg("--init") - .arg("{\"field1\":0,\"field2\":\"dummy\"}") - .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); - - let abi = std::fs::read_to_string("tests/Init.abi.json")?; - assert!(abi.contains("ABI version")); - - remove_all_outputs("Init")?; - Ok(()) -} - #[test] fn test_private_function_ids() -> Status { Command::cargo_bin(BIN_NAME)? @@ -211,8 +179,7 @@ fn test_remapping() -> Status { .arg("tests") .arg("github.com/tonlabs/debots/=tests/remote/") .assert() - .success() - .stdout(predicate::str::contains("Contract successfully compiled")); + .success(); remove_all_outputs("ImportRemote")?; Ok(())