From 3e3eb04e249c2d3ffd6284c93fb18d8555fcaf96 Mon Sep 17 00:00:00 2001 From: IgorKoval Date: Wed, 14 Jun 2023 12:57:20 +0300 Subject: [PATCH] 0.70.0 --- API.md | 528 ++++-- Cargo.lock | 237 +-- Changelog_TON.md | 73 +- compiler/CMakeLists.txt | 2 +- .../analysis/ControlFlowBuilder.cpp | 7 +- .../libsolidity/analysis/StaticAnalyzer.cpp | 21 +- compiler/libsolidity/analysis/TypeChecker.cpp | 39 +- .../libsolidity/analysis/ViewPureChecker.cpp | 8 +- compiler/libsolidity/ast/AST.cpp | 5 + compiler/libsolidity/ast/AST.h | 43 +- compiler/libsolidity/ast/ASTJsonExporter.cpp | 8 + compiler/libsolidity/ast/ASTJsonExporter.h | 1 + compiler/libsolidity/ast/ASTJsonImporter.cpp | 8 +- compiler/libsolidity/ast/ASTVisitor.h | 4 + compiler/libsolidity/ast/AST_accept.h | 12 + compiler/libsolidity/ast/TypeProvider.cpp | 1 + compiler/libsolidity/ast/TypeProvider.h | 1 + compiler/libsolidity/ast/Types.cpp | 1502 ++++++++++++----- compiler/libsolidity/ast/Types.h | 40 +- .../libsolidity/codegen/DictOperations.cpp | 4 +- .../libsolidity/codegen/PeepholeOptimizer.cpp | 47 +- .../libsolidity/codegen/StackOptimizer.cpp | 60 +- compiler/libsolidity/codegen/TVM.cpp | 6 +- compiler/libsolidity/codegen/TVM.hpp | 8 +- compiler/libsolidity/codegen/TVMABI.cpp | 108 +- compiler/libsolidity/codegen/TVMABI.hpp | 3 +- compiler/libsolidity/codegen/TVMAnalyzer.cpp | 5 - compiler/libsolidity/codegen/TVMAnalyzer.hpp | 10 +- compiler/libsolidity/codegen/TVMCommons.cpp | 34 +- compiler/libsolidity/codegen/TVMCommons.hpp | 33 +- compiler/libsolidity/codegen/TVMConstants.hpp | 1 - .../codegen/TVMContractCompiler.cpp | 39 +- .../codegen/TVMExpressionCompiler.cpp | 285 ++-- .../libsolidity/codegen/TVMFunctionCall.cpp | 1199 ++++++++----- .../libsolidity/codegen/TVMFunctionCall.hpp | 5 +- .../codegen/TVMFunctionCompiler.cpp | 364 ++-- .../codegen/TVMFunctionCompiler.hpp | 2 + compiler/libsolidity/codegen/TVMPusher.cpp | 485 +++--- compiler/libsolidity/codegen/TVMPusher.hpp | 30 +- compiler/libsolidity/codegen/TVMSimulator.cpp | 6 +- compiler/libsolidity/codegen/TVMSimulator.hpp | 4 +- .../libsolidity/codegen/TVMStructCompiler.cpp | 4 +- .../libsolidity/codegen/TVMTypeChecker.cpp | 201 ++- .../libsolidity/codegen/TVMTypeChecker.hpp | 4 + compiler/libsolidity/codegen/TvmAst.cpp | 32 +- compiler/libsolidity/codegen/TvmAst.hpp | 37 +- .../libsolidity/codegen/TvmAstVisitor.cpp | 17 +- compiler/libsolidity/parsing/Parser.cpp | 63 +- compiler/libsolidity/parsing/Parser.h | 3 + lib/stdlib_sol.tvm | 2 +- sold/Cargo.toml | 10 +- 51 files changed, 3566 insertions(+), 2085 deletions(-) diff --git a/API.md b/API.md index b8170bc7..69188dfb 100644 --- a/API.md +++ b/API.md @@ -24,18 +24,27 @@ contract development. * [\.hasNBits(), \.hasNRefs() and \.hasNBitsAndRefs()](#tvmslicehasnbits-tvmslicehasnrefs-and-tvmslicehasnbitsandrefs) * [\.compare()](#tvmslicecompare) * [TvmSlice load primitives](#tvmslice-load-primitives) - * [\.decode()](#tvmslicedecode) - * [\.decodeQ()](#tvmslicedecodeq) + * [\.load()](#tvmsliceload) + * [\.loadQ()](#tvmsliceloadq) * [\.loadRef()](#tvmsliceloadref) * [\.loadRefAsSlice()](#tvmsliceloadrefasslice) - * [\.loadSigned()](#tvmsliceloadsigned) - * [\.loadUnsigned()](#tvmsliceloadunsigned) + * [\.loadInt() and \.loadIntQ()](#tvmsliceloadint-and-tvmsliceloadintq) + * [\.loadUint() and \.loadUintQ()](#tvmsliceloaduint-and-tvmsliceloaduintq) + * [Load little-endian integers](#load-little-endian-integers) * [\.loadTons()](#tvmsliceloadtons) - * [\.loadSlice()](#tvmsliceloadslice) - * [\.decodeFunctionParams()](#tvmslicedecodefunctionparams) - * [\.decodeStateVars()](#tvmslicedecodestatevars) + * [\.loadSlice() and \.loadSliceQ()](#tvmsliceloadslice-and-tvmsliceloadsliceq) + * [\.loadFunctionParams()](#tvmsliceloadfunctionparams) + * [\.loadStateVars()](#tvmsliceloadstatevars) * [\.skip()](#tvmsliceskip) * [\.loadZeroes(), \.loadOnes() and \.loadSame()](#tvmsliceloadzeroes-tvmsliceloadones-and-tvmsliceloadsame) + * [TvmSlice preload primitives](#tvmslice-preload-primitives) + * [\.preload()](#tvmslicepreload) + * [\.preloadQ()](#tvmslicepreloadq) + * [\.preloadRef()](#tvmslicepreloadref) + * [\.preloadInt() and \.preloadIntQ()](#tvmslicepreloadint-and-tvmslicepreloadintq) + * [\.preloadUint() and \.preloadUintQ()](#tvmslicepreloaduint-and-tvmslicepreloaduintq) + * [Preload little-endian integers](#preload-little-endian-integers) + * [\.preloadSlice() and \.preloadSliceQ()](#tvmslicepreloadslice-and-tvmslicepreloadsliceq) * [TvmBuilder](#tvmbuilder) * [\.toSlice()](#tvmbuildertoslice) * [\.toCell()](#tvmbuildertocell) @@ -48,8 +57,9 @@ contract development. * [\.depth()](#tvmbuilderdepth) * [\.store()](#tvmbuilderstore) * [\.storeZeroes(), \.storeOnes() and \.storeSame()](#tvmbuilderstorezeroes-tvmbuilderstoreones-and-tvmbuilderstoresame) - * [\.storeSigned()](#tvmbuilderstoresigned) - * [\.storeUnsigned()](#tvmbuilderstoreunsigned) + * [\.storeInt()](#tvmbuilderstoreint) + * [\.storeUint()](#tvmbuilderstoreuint) + * [Store little-endian integers](#store-little-endian-integers) * [\.storeRef()](#tvmbuilderstoreref) * [\.storeTons()](#tvmbuilderstoretons) * [ExtraCurrencyCollection](#extracurrencycollection) @@ -89,7 +99,6 @@ contract development. * [\.operator[]](#bytesoperator) * [\ slice](#bytes-slice) * [\.length](#byteslength) - * [\.toSlice()](#bytestoslice) * [\.dataSize()](#bytesdatasize) * [\.dataSizeQ()](#bytesdatasizeq) * [\.append()](#bytesappend) @@ -101,7 +110,6 @@ contract development. * [\.append()](#stringappend) * [\.operator+](#stringoperator) * [\.find() and \.findLast()](#stringfind-and-stringfindlast) - * [\.toSlice()](#stringtoslice) * [\.dataSize()](#stringdatasize) * [\.dataSizeQ()](#stringdatasizeq) * [\.toUpperCase()` and \.toLowerCase()](#stringtouppercase-and-stringtolowercase) @@ -174,6 +182,7 @@ contract development. * [Function specifiers](#function-specifiers) * [Function mutability: pure, view and default](#function-mutability-pure-view-and-default) * [Keyword inline](#keyword-inline) + * [Assembly](#assembly) * [functionID()](#functionid) * [externalMsg and internalMsg](#externalmsg-and-internalmsg) * [Events and return](#events-and-return) @@ -183,6 +192,7 @@ contract development. * [Synchronous calls](#synchronous-calls) * [Delete variables](#delete-variables) * [API functions and members](#api-functions-and-members) + * [Type information](#type-information) * [**msg** namespace](#msg-namespace) * [msg.sender](#msgsender) * [msg.value](#msgvalue) @@ -247,7 +257,7 @@ contract development. * [math.divmod()](#mathdivmod) * [math.sign()](#mathsign) * [**tx** namespace](#tx-namespace) - * [tx.timestamp](#txtimestamp) + * [tx.logicaltime](#txlogicaltime) * [tx.storageFee](#txstoragefee) * [**block** namespace](#block-namespace) * [block.timestamp](#blocktimestamp) @@ -282,9 +292,9 @@ TON Solidity compiler add its current version to the generated code. This versio 1) using [tvm_linker](https://github.com/tonlabs/TVM-linker#2-decoding-of-boc-messages-prepared-externally) from a `*.tvc` file: -```bash -tvm_linker decode --tvm -``` + ```bash + tvm_linker decode --tvm + ``` 2) using [tonos-cli](https://github.com/tonlabs/tonos-cli#48-decode-commands) from a `*.boc` file, a `*.tvc` file, or a network account: @@ -391,6 +401,8 @@ Comparison operators: 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. + ##### \.empty() ```TVMSolidity @@ -461,9 +473,9 @@ otherwise function result is one plus the maximum of depths of the cells referre ##### \.hasNBits(), \.hasNRefs() and \.hasNBitsAndRefs() ```TVMSolidity -.hasNBits(uint16 bits) returns (bool); -.hasNRefs(uint8 refs) returns (bool); -.hasNBitsAndRefs(uint16 bits, uint8 refs) returns (bool); +.hasNBits(uint10 bits) returns (bool); +.hasNRefs(uint2 refs) returns (bool); +.hasNBitsAndRefs(uint10 bits, uint2 refs) returns (bool); ``` Checks whether the `TvmSlice` contains the specified amount of data bits and references. @@ -471,7 +483,7 @@ Checks whether the `TvmSlice` contains the specified amount of data bits and ref ##### \.compare() ```TVMSolidity -.compare(TvmSlice other) returns (int8); +.compare(TvmSlice other) returns (int2); ``` Lexicographically compares the `slice` and `other` data bits of the root slices and returns result as an integer: @@ -482,49 +494,41 @@ Lexicographically compares the `slice` and `other` data bits of the root slices ##### TvmSlice load primitives -All functions below modify the `TvmSlice` object they were called for and all of them work -consistently. It means that if user wants to load second ref from the slice, he should -load the first one with [\.loadRef()](#tvmsliceloadref), [\.loadRefAsSlice()](#tvmsliceloadrefasslice) -or just skip it with [\.skip()](#tvmsliceskip) and then load the ref he needs. -The same rule is applied to data bits. To load bits from 2 to 10 positions, user should load -or skip first two bits. +All `load*` functions below modify the `TvmSlice` object. If you wants to load second reference from the `TvmSlice`, you should load the first one with [\.loadRef()](#tvmsliceloadref) and then load the reference you need. The same rule is applied to data bits. To load bits from 2 to 10 positions, you should load or skip first two bits. -###### \.decode() +###### \.load() ```TVMSolidity -.decode(TypeA, TypeB, ...) returns (TypeA /*a*/, TypeB /*b*/, ...); +.load(TypeA, TypeB, ...) returns (TypeA /*a*/, TypeB /*b*/, ...); ``` -Sequentially decodes values of the specified types from the `TvmSlice`. +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 `struct`. Example: ```TVMSolidity TvmSlice slice = ...; -(uint8 a, uint16 b) = slice.decode(uint8, uint16); -(uint16 num0, uint32 num1, address addr) = slice.decode(uint16, uint32, address); +(uint8 a, uint16 b) = slice.load(uint8, uint16); +(uint16 num0, uint32 num1, address addr) = slice.load(uint16, uint32, address); ``` See also: [\.store()](#tvmbuilderstore). -**Note**: if all the argument types can't be decoded from the slice a cell underflow [exception](#tvm-exception-codes) is thrown. +**Note**: if all the argument types can't be loaded from the slice a cell underflow [exception](#tvm-exception-codes) is thrown. -###### \.decodeQ() +###### \.loadQ() ```TVMSolidity -.decodeQ(TypeA, TypeB, ...) returns (optional(TypeA, TypeB, ...)); +.loadQ(TypeA, TypeB, ...) returns (optional(TypeA, TypeB, ...)); ``` Sequentially decodes values of the specified types from the `TvmSlice` if the `TvmSlice` holds sufficient data for all specified types. Otherwise, returns `null`. -Supported types: `uintN`, `intN`, `bytesN`, `bool`, `ufixedMxN`, `fixedMxN`, `address`, `contract`, -`TvmCell`, `bytes`, `string`, `mapping`, `ExtraCurrencyCollection`, and `array`. - ```TVMSolidity TvmSlice slice = ...; -optional(uint) a = slice.decodeQ(uint); -optional(uint8, uint16) b = slice.decodeQ(uint8, uint16); +optional(uint) a = slice.loadQ(uint); +optional(uint8, uint16) b = slice.loadQ(uint8, uint16); ``` See also: [\.store()](#tvmbuilderstore). @@ -545,21 +549,52 @@ Loads a cell from the `TvmSlice` reference. Loads a cell from the `TvmSlice` reference and converts it into a `TvmSlice`. -###### \.loadSigned() +###### \.loadInt() and \.loadIntQ() ```TVMSolidity -.loadSigned(uint16 bitSize) returns (int); +(1) +.loadInt(uint9 bitSize) returns (int); +(2) +.loadIntQ(uint9 bitSize) returns (optional(int)); ``` -Loads a signed integer with the given **bitSize** from the `TvmSlice`. +(1) Loads a signed integer with the given **bitSize** from the `TvmSlice`. + +(2) Loads a signed integer with the given **bitSize** from the `TvmSlice` if `TvmSlice` contains it. Otherwise, returns `null`. -###### \.loadUnsigned() +###### \.loadUint() and \.loadUintQ() ```TVMSolidity -.loadUnsigned(uint16 bitSize) returns (uint); +(1) +.loadUint(uint9 bitSize) returns (uint); +(2) +.loadUintQ(uint9 bitSize) returns (optional(uint)); ``` -Loads an unsigned integer with the given **bitSize** from the `TvmSlice`. +(1) Loads an unsigned integer with the given **bitSize** from the `TvmSlice`. + +(2) Loads an unsigned integer with the given **bitSize** from the `TvmSlice` if `TvmSlice` contains it. Otherwise, returns `null`. + +###### Load little-endian integers + +```TVMSolidity +(1) +.loadIntLE2() returns (int16) +.loadIntLE4() returns (int32) +.loadIntLE8() returns (int64) +.loadUintLE2() returns (uint16) +.loadUintLE4() returns (uint32) +.loadUintLE8() returns (uint64) +(2) +.loadIntLE4Q() returns (optional(int32)) +.loadIntLE8Q() returns (optional(int64)) +.loadUintLE4Q() returns (optional(uint32)) +.loadUintLE8Q() returns (optional(uint64)) +``` + +(1) Loads the little-endian integer from `TvmSlice`. + +(2) Same as (1) but returns `null` if it's impossible to load the integer. ###### \.loadTons() @@ -569,42 +604,50 @@ Loads an unsigned integer with the given **bitSize** from the `TvmSlice`. Loads (deserializes) **VarUInteger 16** and returns an unsigned 128-bit integer. See [TL-B scheme][3]. -###### \.loadSlice() +###### \.loadSlice() and \.loadSliceQ() ```TVMSolidity -.loadSlice(uint length) returns (TvmSlice); -.loadSlice(uint length, uint refs) returns (TvmSlice); +(1) +.loadSlice(uint10 bits) returns (TvmSlice); +(2) +.loadSlice(uint10 bits, uint refs) returns (TvmSlice); +(3) +.loadSliceQ(uint10 bits) returns (optional(TvmSlice)); +(4) +.loadSliceQ(uint10 bits, uint2 refs) returns (optional(TvmSlice)); ``` -Loads the first `length` bits and `refs` references from the `TvmSlice` into a separate `TvmSlice`. +(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. -###### \.decodeFunctionParams() +###### \.loadFunctionParams() ```TVMSolidity -// Decode parameters of the public/external function without "responsible" attribute -.decodeFunctionParams(functionName) returns (TypeA /*a*/, TypeB /*b*/, ...); +// Loads parameters of the public/external function without "responsible" attribute +.loadFunctionParams(functionName) returns (TypeA /*a*/, TypeB /*b*/, ...); -// Decode parameters of the public/external function with "responsible" attribute -.decodeFunctionParams(functionName) returns (uint32 callbackFunctionId, TypeA /*a*/, TypeB /*b*/, ...); +// Loads parameters of the public/external function with "responsible" attribute +.loadFunctionParams(functionName) returns (uint32 callbackFunctionId, TypeA /*a*/, TypeB /*b*/, ...); -// Decode constructor parameters -.decodeFunctionParams(ContractName) returns (TypeA /*a*/, TypeB /*b*/, ...); +// Loads constructor parameters +.loadFunctionParams(ContractName) returns (TypeA /*a*/, TypeB /*b*/, ...); ``` -Decodes parameters of the function or constructor (if contract type is provided). This function is usually used in +Loads parameters of the function or constructor (if contract type is provided). This function is usually used in **[onBounce](#onbounce)** function. See example of how to use **onBounce** function: * [onBounceHandler](https://github.com/tonlabs/samples/blob/master/solidity/16_onBounceHandler.sol) -###### \.decodeStateVars() +###### \.loadStateVars() ```TVMSolidity -.decodeStateVars(ContractName) returns (uint256 /*pubkey*/, uint64 /*timestamp*/, bool /*constructorFlag*/, Type1 /*var1*/, Type2 /*var2*/, ...); +.loadStateVars(ContractName) returns (uint256 /*pubkey*/, uint64 /*timestamp*/, bool /*constructorFlag*/, Type1 /*var1*/, Type2 /*var2*/, ...); ``` -Decode state variables from `slice` that is obtained from the field `data` of `stateInit` +Loads state variables from `slice` that is obtained from the field `data` of `stateInit` Example: @@ -622,7 +665,7 @@ contract B { function f(TvmCell data) public pure { TvmSlice s = data.toSlice(); (uint256 pubkey, uint64 timestamp, bool flag, - uint a, uint b, uint c, uint d, address e, address f) = s.decodeStateVars(A); + uint a, uint b, uint c, uint d, address e, address f) = s.loadStateVars(A); // pubkey - pubkey of the contract A // timestamp - timestamp that used for replay protection @@ -641,11 +684,11 @@ contract B { ###### \.skip() ```TVMSolidity -.skip(uint length); -.skip(uint length, uint refs); +.skip(uint10 bits); +.skip(uint10 bits, uint2 refs); ``` -Skips the first `length` bits and `refs` references from the `TvmSlice`. +Skips the first `bits` bits and `refs` references from the `TvmSlice`. ###### \.loadZeroes(), \.loadOnes() and \.loadSame() @@ -664,6 +707,93 @@ Skips the first `length` bits and `refs` references from the `TvmSlice`. (3) Returns the count `n` of leading bits equal to `0 ≤ value ≤ 1` in `TvmSlice`, and removes these bits from `TvmSlice`. +See also: [\.storeZeroes(), \.storeOnes() and \.storeSame()](#tvmbuilderstorezeroes-tvmbuilderstoreones-and-tvmbuilderstoresame). + +##### TvmSlice preload primitives + +All `preload*` functions below don't modify the `TvmSlice` object. + +###### \.preload() + +```TVMSolidity +.preload(TypeA, TypeB, ...) returns (TypeA /*a*/, TypeB /*b*/, ...); +``` + +Same as [\.load()](#tvmsliceload) but doesn't modify `TvmSlice`. + +###### \.preloadQ() + +```TVMSolidity +.preloadQ(TypeA, TypeB, ...) returns (optional(TypeA, TypeB, ...)); +``` + +Same as [\.loadQ()](#tvmsliceloadq) but doesn't modify `TvmSlice`. + +###### \.preloadRef() + +```TVMSolidity +(1) +.preloadRef() returns (TvmCell); +(2) +.preloadRef(uint2 index) returns (TvmCell); +``` + +(1) Returns the first cell reference of `TvmSlice`. + +(2) Returns the `index` cell reference of `TvmSlice`, where `0 ≤ index ≤ 3`. + +###### \.preloadInt() and \.preloadIntQ() + +```TVMSolidity +(1) +.preloadInt(uint9 bitSize) returns (int); +(2) +.preloadIntQ(uint9 bitSize) returns (optional(int)); +``` + +Same as [\.loadInt() and \.loadIntQ()](#tvmsliceloadint-and-tvmsliceloadintq) but doesn't modify `TvmSlice`. + +###### \.preloadUint() and \.preloadUintQ() + +```TVMSolidity +(1) +.preloadUint(uint9 bitSize) returns (uint); +(2) +.preloadUintQ(uint9 bitSize) returns (optional(uint)); +``` + +Same as [\.loadUint() and \.loadUintQ()](#tvmsliceloaduint-and-tvmsliceloaduintq) but doesn't modify `TvmSlice`. + +###### Preload little-endian integers + +```TVMSolidity +.preloadIntLE4() returns (int32) +.preloadIntLE8() returns (int64) +.preloadUintLE4() returns (uint32) +.preloadUintLE8() returns (uint64) + +.preloadIntLE4Q() returns (optional(int32)) +.preloadIntLE8Q() returns (optional(int64)) +.preloadUintLE4Q() returns (optional(uint32)) +.preloadUintLE8Q() returns (optional(uint64)) +``` + +Same as [Load little-endian integers](#load-little-endian-integers) but doesn't modify `TvmSlice`. + +###### \.preloadSlice() and \.preloadSliceQ() + +```TVMSolidity +(1) +.preloadSlice(uint10 bits) returns (TvmSlice); +(2) +.preloadSlice(uint10 bits, uint refs) returns (TvmSlice); +(3) +.preloadSliceQ(uint10 bits) returns (optional(TvmSlice)); +(4) +.preloadSliceQ(uint10 bits, uint4 refs) returns (optional(TvmSlice)); +``` + +Same as [\.loadSlice() and \.loadSliceQ()](#tvmsliceloadslice-and-tvmsliceloadsliceq) but doesn't modify `TvmSlice`. #### TvmBuilder @@ -786,13 +916,16 @@ TvmBuilder builder; builder.store(a, b, uint(33)); ``` -See also: [\.decode()](#tvmslicedecode). +See also: [\.load()](#tvmsliceload). ##### \.storeZeroes(), \.storeOnes() and \.storeSame() ```TVMSolidity +(1) .storeZeroes(uint10 n); +(2) .storeOnes(uint10 n); +(3) .storeSame(uint10 n, uint1 value); ``` @@ -802,22 +935,37 @@ See also: [\.decode()](#tvmslicedecode). (3) Stores `n` binary `value`s (0 ≤ value ≤ 1) into the `TvmBuilder`. -##### \.storeSigned() +See also: [\.loadZeroes(), \.loadOnes() and \.loadSame()](#tvmsliceloadzeroes-tvmsliceloadones-and-tvmsliceloadsame). + +##### \.storeInt() ```TVMSolidity -.storeSigned(int256 value, uint16 bitSize); +.storeInt(int256 value, uint9 bitSize); ``` Stores a signed integer **value** with given **bitSize** in the `TvmBuilder`. -##### \.storeUnsigned() +##### \.storeUint() ```TVMSolidity -.storeUnsigned(uint256 value, uint16 bitSize); +.storeUint(uint256 value, uint9 bitSize); ``` Stores an unsigned integer **value** with given **bitSize** in the `TvmBuilder`. +##### Store little-endian integers + +```TVMSolidity +.storeIntLE2(int16) +.storeIntLE4(int32) +.storeIntLE8(int64) +.storeUintLE2(uint16) +.storeUintLE4(uint32) +.storeUintLE8(uint64) +``` + +Stores the little-endian integer. + ##### \.storeRef() ```TVMSolidity @@ -1098,7 +1246,7 @@ It is an experimental feature available only in certain blockchain networks. 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. -`catch` block gets two parameters of type variant and uint, whick contain exception argument and code respectively. +`catch` block gets two parameters of type variant and uint16, which contain exception argument and code respectively. Example: ```TVMSolidity @@ -1109,7 +1257,7 @@ try { require(c != 42, 100, 22); require(c != 43, 100, 33); builder.store(c); -} catch (variant value, uint errorCode) { +} catch (variant value, uint16 errorCode) { uint errorValue; if (value.isUint()) { errorValue = value.toUint(); @@ -1136,7 +1284,7 @@ try { #### Integers ``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uintN`` and ``intN`` -where ``N`` is a number from ``8`` to ``256`` in steps of 8 denotes the number of bits. ``uint`` and ``int`` +where ``N`` is a number from ``1`` to ``256`` in steps of 1 denotes the number of bits. ``uint`` and ``int`` are aliases for ``uint256`` and ``int256``, respectively. Operators: @@ -1300,6 +1448,8 @@ 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. + ##### \.empty() ```TVMSolidity @@ -1350,16 +1500,6 @@ slice = byteArray[:]; // slice == "01234567890123456789" Returns length of the `bytes` array. -##### \.toSlice() - -```TVMSolidity -.toSlice() returns (TvmSlice); -``` - -Converts `bytes` 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. - ##### \.dataSize() ```TVMSolidity @@ -1400,6 +1540,8 @@ TON Solidity compiler expands `string` type with the following functions: **Note**: Due to VM restrictions string length can't exceed `1024 * 127 = 130048` bytes. +`string` can be converted to `TvmSlice`. + ##### \.empty() ```TVMSolidity @@ -1483,14 +1625,6 @@ optional(uint32) c = str.find(sub); bool s = c.hasValue(); // s == false ``` -##### \.toSlice() - -```TVMSolidity -.toSlice() returns (TvmSlice); -``` - -Converts `string` to `TvmSlice`. - ##### \.dataSize() ```TVMSolidity @@ -1642,7 +1776,7 @@ uint bitCnt; address addrExtern = address.makeAddrExtern(addrNumber, bitCnt); ``` -Constructs an `address` of type **addr_extern** with given **value** with **bitCnt** bit length. +Constructs an `address` of type **addr_extern** with given **value** with **bitCnt** bit-length. ##### Members @@ -1838,8 +1972,8 @@ function test(uint x, uint y, uint z, address addr) public { } ``` -If you use `mapping(KeyType => TvmSlice) map;` be sure that sum of bit length of `KeyType` -and bit length of `TvmSlice` is less than 1023 bit. +If you use `mapping(KeyType => TvmSlice) map;` be sure that sum of bit-length of `KeyType` +and bit-length of `TvmSlice` is less than 1023 bit. Struct `KeyType` can be used to sort keys of the mapping in ascending order. In the example above addresses in the mapping are sorted by their keys. `x` field of the Point struct @@ -2376,7 +2510,7 @@ Defines that code is compiled with special selector that is needed to upgrade fu You can decode state variables using tonos-cli. See `tonos-cli decode account --help`. -See also: [\.decodeStateVars()](#tvmslicedecodestatevars). +See also: [\.loadStateVars()](#tvmsliceloadstatevars). #### Keyword `constant` @@ -2477,8 +2611,8 @@ contract Bomber { `fallback` function is called on receiving an inbound internal/external message in such cases: 1. The message contains a function id that the contract doesn't contain. -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). +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 the transaction fail with an exception (e.g. [cell underflow exception](#tvm-exception-codes)). @@ -2517,14 +2651,14 @@ contract ContractB { { TvmBuilder b; b.storeUnsigned(1, 1); - // Bit length of the message is equal to 20 bits. + // Bit-length of the message is equal to 20 bits. addr.transfer({value: 2 ton, body: b.toCell()}); } { TvmBuilder b; b.storeRef(b); - // Bit length of the message is equal to zero but the message contains one reference. + // Bit-length of the message is equal to zero but the message contains one reference. addr.transfer({value: 1 ton, body: b.toCell()}); } @@ -2703,6 +2837,37 @@ function sum(uint a, uint b) private inline returns (uint) { } ``` +#### Assembly + +To make inline assembler you should mark free function as `assembly`. Function body must contain lines of assembler code separated by commas. + +It is up to user to set correct mutability (`pure`, `view` or default), return parameters of the function and so on. + +```TVMSolidity +function checkOverflow(uint a, uint b) assembly pure returns (bool) { + "QADD", + "ISNAN", + "NOT", +} + +contract Contract { + function f(uint a, uint b) private { + bool ok = checkOverflow(a, b); + if (ok) { + uint c = a + b; + } + } +} +``` + +You can use inline assembler to support new opcodes in experimental or another implementations of TVM. + +```TVMSolidity +function incomingValue() assembly pure returns (uint) { + ".blob xF82B", // it's opcode INCOMINGVALUE +} +``` + #### functionID() `functionID` keyword allows assigning function identifier explicitly. @@ -2992,6 +3157,15 @@ uint8 refs = b.refs(); // refs == 1 ### API functions and members +#### Type information + +The expression `type(T)` can be used to retrieve information about the type T. + +The following properties are available for an integer, variable integer and enum type `T`: + * `type(T).min` - the smallest value representable by type `T`. + + * `type(T).max` - the largest value representable by type `T`. + #### **msg** namespace ##### msg.sender @@ -3435,7 +3609,7 @@ 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 build from `pubkey` and `varInit`. +`varInit`. Can be omitted, in this case data field would be built from `pubkey` and `varInit`. * `splitDepth` (`uint8`) - splitting depth. `0 <= splitDepth <= 31`. Can be omitted. By default, it has no value. * `pubkey` (`uint256`) - defines the public key of the new contract. Conflicts with `data`. @@ -3812,7 +3986,7 @@ addr.transfer({value: 10 ton, body: body}); See also: * [External function calls](#external-function-calls) -* [\.decodeFunctionParams()](#tvmslicedecodefunctionparams) +* [\.loadFunctionParams()](#tvmsliceloadfunctionparams) * [tvm.buildIntMsg()](#tvmbuildintmsg) ##### tvm.exit() and tvm.exit1() @@ -4021,6 +4195,9 @@ It will happen because the transaction will fail at the action phase. #### **math** namespace +`T` is an integer, [variable integer](#varint-and-varuint) or fixed point type in the `math.*` functions where applicable. +Fixed point type is not applicable for `math.modpow2()`, `math.muldiv[r|c]()`, `math.muldivmod()` and `math.divmod()`. + ##### math.min() math.max() ```TVMSolidity @@ -4028,7 +4205,7 @@ math.min(T a, T b, ...) returns (T); math.max(T a, T b, ...) returns (T); ``` -Returns the minimal (maximal) value of the passed arguments. `T` should be an integer or fixed point type +Returns the minimal (maximal) value of the passed arguments. ##### math.minmax() @@ -4036,19 +4213,18 @@ Returns the minimal (maximal) value of the passed arguments. `T` should be an in math.minmax(T a, T b) returns (T /*min*/, T /*max*/); ``` -Returns minimal and maximal values of the passed arguments. `T` should be an integer or fixed point type +Returns minimal and maximal values of the passed arguments. Example: ```TVMSolidity -(uint a, uint b) = math.minmax(20, 10); // (10, 20) +(uint a, uint b) = math.minmax(20, 10); // (a, b) == (10, 20) ``` ##### math.abs() ```TVMSolidity -math.abs(intM val) returns (intM); -math.abs(fixedMxN val) returns (fixedMxN); +math.abs(T val) returns (T); ``` Computes the absolute value of the given integer. @@ -4056,26 +4232,26 @@ Computes the absolute value of the given integer. Example: ```TVMSolidity -int a = math.abs(-4123); // 4123 -int b = -333; -int c = math.abs(b); // 333 +int a = math.abs(-100); // a == 100 +int b = -100; +int c = math.abs(b); // c == 100 ``` ##### math.modpow2() ```TVMSolidity -math.modpow2(uint value, uint power) returns (uint); +math.modpow2(T value, uint power) returns (T); ``` -Computes the value modulo 2^**power**. Note that **power** should be a constant integer. +Computes the `value mod 2^power`. Note: `power` should be a constant integer. Example: ```TVMSolidity -uint constant pow = 12; -uint val = 12313; -uint a = math.modpow2(val, 10); -uint b = math.modpow2(val, pow); +uint constant pow = 10; +uint val = 1026; +uint a = math.modpow2(21, 4); // a == 5 +uint b = math.modpow2(val, pow); // b == 2 ``` ##### math.divr() math.divc() @@ -4085,16 +4261,14 @@ math.divc(T a, T b) returns (T); math.divr(T a, T b) returns (T); ``` -Returns result of the division of two integers. `T` should be an integer or fixed point type. -The return value is rounded. **ceiling** and **nearest** modes are used for `divc` and `divr` -respectively. -See also: [Division and rounding](#division-and-rounding). +Returns result of the division of two integers. The return value is rounded. **ceiling** and **nearest** modes are used for `divc` and `divr` +respectively. See also: [Division and rounding](#division-and-rounding). Example: ```TVMSolidity -int c = math.divc(10, 3); // c = 4 -int c = math.divr(10, 3); // c = 3 +int c = math.divc(10, 3); // c == 4 +int c = math.divr(10, 3); // c == 3 fixed32x2 a = 0.25; fixed32x2 res = math.divc(a, 2); // res == 0.13 @@ -4108,10 +4282,8 @@ math.muldivr(T a, T b, T c) returns (T); math.muldivc(T a, T b, T c) returns (T); ``` -Multiplies two values and then divides the result by a third value. `T` is integer type. -The return value is rounded. **floor**, **ceiling** and **nearest** modes are used for `muldiv`, -`muldivc` and `muldivr` respectively. -See also: [Division and rounding](#division-and-rounding). +Multiplies two values and then divides the result by a third value. The return value is rounded. **floor**, **ceiling** and **nearest** modes are used for `muldiv`, +`muldivc` and `muldivr` respectively. See also: [Division and rounding](#division-and-rounding). Example: @@ -4137,11 +4309,11 @@ Example: uint a = 3; uint b = 2; uint c = 5; -(uint d, uint r) = math.muldivmod(a, b, c); // (1, 1) +(uint d, uint r) = math.muldivmod(a, b, c); // (d, r) == (1, 1) int e = -1; int f = 3; int g = 2; -(int h, int p) = math.muldivmod(e, f, g); // (-2, 1) +(int h, int p) = math.muldivmod(e, f, g); // (h, p) == (-2, 1) ``` ##### math.divmod() @@ -4151,45 +4323,45 @@ math.divmod(T a, T b) returns (T /*result*/, T /*remainder*/); ``` This function divides the first number by the second and returns the result and the -remainder. Result is rounded to the floor. `T` must be an integer type. +remainder. Result is rounded to the floor. Example: ```TVMSolidity -uint a = 3; -uint b = 2; -(uint d, uint r) = math.divmod(a, b); -int e = -1; +uint a = 11; +uint b = 3; +(uint d, uint r) = math.divmod(a, b); // (d, r) == (3, 2) + +int e = -11; int f = 3; -(int h, int p) = math.divmod(e, f); +(int h, int p) = math.divmod(e, f); // (h, p) == (-3, 2) ``` ##### math.sign() ```TVMSolidity -math.sign(int val) returns (int8); +math.sign(T val) returns (int2); ``` -Returns the number in case of the sign of the argument value `val`: - -* -1 if `val` is negative; -* 0 if `val` is zero; -* 1 if `val` is positive. +Returns: + * -1 if `val` is negative; + * 0 if `val` is zero; + * 1 if `val` is positive. Example: ```TVMSolidity -int8 sign = math.sign(-1333); // sign == -1 -int8 sign = math.sign(44); // sign == 1 +int8 sign = math.sign(-100); // sign == -1 +int8 sign = math.sign(100); // sign == 1 int8 sign = math.sign(0); // sign == 0 ``` ##### **tx** namespace -##### tx.timestamp +##### tx.logicaltime ```TVMSolidity -tx.timestamp returns (uint64); +tx.logicaltime returns (uint64); ``` Returns the logical time of the current transaction. @@ -4199,7 +4371,7 @@ Returns the logical time of the current transaction. It's an experimental feature and is available only in certain blockchain networks. ```TVMSolidity -tx.storageFee returns (uint64); +tx.storageFee returns (uint120); ``` Returns the storage fee paid in the current transaction. @@ -4430,7 +4602,7 @@ sha256(bytes b) returns (uint256) 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 +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], @@ -4463,28 +4635,22 @@ If `wid` is omitted than used the contract's `wid`. ### TVM exception codes -Tvm exception code can differ in different networks: -for C++ nodes networks they should correspond original documentation -([TVM][1] - 4.5.7); -for Rust nodes network codes are converted to other values: - `rust_code = -(c++_code + 1)` - -List of codes: - -| Name | C++ code | Rust code | Definition | -|-------------------|:--------:|:---------:|----------------------------------------------------------------------------------------------------------------------------------------------------| -| Stack underflow | 2 | -3 | Not enough arguments in the stack for a primitive | -| Stack overflow | 3 | -4 | More values have been stored on a stack than allowed by this version of TVM | -| Integer overflow | 4 | -5 | Integer does not fit into expected range (by default −2256 ≤ x < 2256), or a division by zero has occurred | -| Range check error | 5 | -6 | Integer out of expected range | -| Invalid opcode | 6 | -7 | Instruction or its immediate arguments cannot be decoded | -| Type check error | 7 | -8 | An argument to a primitive is of incorrect value type | -| Cell overflow | 8 | -9 | Error in one of the serialization primitives | -| Cell underflow | 9 | -10 | Deserialization error | -| Dictionary error | 10 | -11 | Error while deserializing a dictionary object | -| Unknown error | 11 | -12 | Unknown error, may be thrown by user programs | -| Fatal error | 12 | -13 | Thrown by TVM in situations deemed impossible | -| Out of gas | 13 | -14 | Thrown by TVM when the remaining gas (g r ) becomes negative. This exception usually cannot be caught and leads to an immediate termination of TVM | +| Name | Code | Definition | +|-------------------|:----:|----------------------------------------------------------------------------------------------------------------------------------------------------| +| Stack underflow | 2 | Not enough arguments in the stack for a primitive | +| Stack overflow | 3 | More values have been stored on a stack than allowed by this version of TVM | +| Integer overflow | 4 | Integer does not fit into expected range (by default −2256 ≤ x < 2256), or a division by zero has occurred | +| Range check error | 5 | Integer out of expected range | +| Invalid opcode | 6 | Instruction or its immediate arguments cannot be decoded | +| Type check error | 7 | An argument to a primitive is of incorrect value type | +| Cell overflow | 8 | Error in one of the serialization primitives | +| Cell underflow | 9 | Deserialization error | +| Dictionary error | 10 | Error while deserializing a dictionary object | +| Unknown error | 11 | Unknown error, may be thrown by user programs | +| Fatal error | 12 | Thrown by TVM in situations deemed impossible | +| Out of gas | 13 | Thrown by TVM when the remaining gas (g r ) becomes negative. This exception usually cannot be caught and leads to an immediate termination of TVM | + +See also: [TVM][1] - 4.5.7 ### Solidity runtime errors @@ -4569,24 +4735,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 is present and `pubkey` header isn't defined then `tvm.pubkey()` is used -for checking. -- If signature isn't present and `pubkey` header isn't defined then signature isn't checked. -- If signature is present, `pubkey` header is defined and `pubkey` isn't present in the -message then `tvm.pubkey()` is used for checking. -- If signature is present, `pubkey` header is defined and `pubkey` is present in the -message then `msg.pubkey()` is used for checking. -- If signature isn't present, `pubkey` header is defined and `pubkey` is present in the -message then an [exception with code 58](#solidity-runtime-errors) is thrown. + - If signature is present and `pubkey` header isn't defined then `tvm.pubkey()` is used + for checking. + - If signature isn't present and `pubkey` header isn't defined then signature isn't checked. + - If signature is present, `pubkey` header is defined and `pubkey` isn't present in the + message then `tvm.pubkey()` is used for checking. + - If signature is present, `pubkey` header is defined and `pubkey` is present in the + message then `msg.pubkey()` is used for checking. + - If signature isn't present, `pubkey` header is defined and `pubkey` is present in the + message then an [exception with code 58](#solidity-runtime-errors) is thrown. 2. Replay protection: -- `time` is present and there is no `afterSignatureCheck` 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. + - `time` is present and there is no `afterSignatureCheck` 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. 3. Message expiration: -- `expire` is present and there is no `afterSignatureCheck` then the contract checks whether -`expire` > `now`. -- there is `afterSignatureCheck` (despite usage of `expire`) then make your own check. + - `expire` is present and there is no `afterSignatureCheck` then the contract checks whether + `expire` > `now`. + - 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 4b13f6fd..e04b66c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,13 +51,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -144,7 +150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" dependencies = [ "anstyle", - "bstr 1.4.0", + "bstr 1.5.0", "doc-comment", "predicates", "predicates-core", @@ -245,9 +251,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" dependencies = [ "memchr", "once_cell", @@ -257,9 +263,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -284,13 +290,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time", "wasm-bindgen", @@ -323,9 +329,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.7" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed" dependencies = [ "clap_builder", "clap_derive", @@ -334,9 +340,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636" dependencies = [ "anstream", "anstyle", @@ -347,21 +353,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cmake" @@ -392,9 +398,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -416,9 +422,9 @@ checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -481,9 +487,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -518,7 +524,16 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "signature 2.1.0", ] [[package]] @@ -528,7 +543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", @@ -616,9 +631,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -627,9 +642,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "heck" @@ -666,9 +681,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -689,9 +704,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -736,9 +751,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -751,15 +766,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lockfree" @@ -771,12 +786,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -888,18 +900,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -958,18 +970,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -1033,7 +1045,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -1064,9 +1076,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", @@ -1081,9 +1093,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "rust-argon2" @@ -1105,9 +1117,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno", @@ -1125,22 +1137,22 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1169,13 +1181,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -1184,6 +1196,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" + [[package]] name = "similar" version = "2.2.1" @@ -1212,11 +1230,11 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "sold" -version = "0.69.0" +version = "0.70.0" dependencies = [ "assert_cmd", "atty", - "clap 4.2.7", + "clap 4.3.4", "cmake", "dunce", "failure", @@ -1271,9 +1289,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -1331,13 +1349,13 @@ dependencies = [ [[package]] name = "ton_abi" -version = "2.3.80" -source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.3.80#0c8efc4a4e43212316fd36cd6eec3911b87bf7df" +version = "2.3.108" +source = "git+https://github.com/tonlabs/ever-abi.git?tag=2.3.108#af5ae77b9f15c9c40ef8af567391fae0ee711e21" dependencies = [ "base64 0.10.1", "byteorder", "chrono", - "ed25519", + "ed25519 1.5.3", "ed25519-dalek", "failure", "hex 0.3.2", @@ -1346,33 +1364,33 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "ton_block", "ton_types", ] [[package]] name = "ton_block" -version = "1.9.43" -source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.43#a6d20909632b00bf73f0cbbea8d9a11534837f8d" +version = "1.9.70" +source = "git+https://github.com/tonlabs/ever-block.git?tag=1.9.70#fb352a619905ea2866ceeae59bbdca451fff0ded" dependencies = [ "base64 0.13.1", "crc", - "ed25519", + "ed25519 1.5.3", "ed25519-dalek", "failure", "hex 0.4.3", "log", "num", "num-traits", - "sha2 0.10.6", + "sha2 0.10.7", "ton_types", ] [[package]] name = "ton_labs_assembler" -version = "1.2.95" -source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.2.95#7e8fc6425227f9f0fc55db6e371fdd8fc684257b" +version = "1.2.118" +source = "git+https://github.com/tonlabs/ever-assembler.git?tag=1.2.118#d2009265ae21f6bc332bcadf5adb7fd41d63611f" dependencies = [ "failure", "hex 0.4.3", @@ -1386,14 +1404,14 @@ dependencies = [ [[package]] name = "ton_types" -version = "2.0.2" -source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.2#7f63474425e0102422e82fd8b18c489851875c14" +version = "2.0.12" +source = "git+https://github.com/tonlabs/ever-types.git?tag=2.0.12#4dd14bdbf060b744665ed7745cf771c0e0c9914d" dependencies = [ "aes-ctr", "base64 0.13.1", "crc", "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "ed25519-dalek", "failure", "hex 0.4.3", @@ -1406,18 +1424,18 @@ dependencies = [ "rand 0.7.3", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "x25519-dalek", ] [[package]] name = "ton_vm" -version = "1.8.132" -source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.8.132#72b50dc57beaa21a5f62f64d7d03d686e9b1716d" +version = "1.8.162" +source = "git+https://github.com/tonlabs/ever-vm.git?tag=1.8.162#448722b486f9fdfe99cac2befb01184592bf8dad" dependencies = [ "diffy", - "ed25519", + "ed25519 1.5.3", "ed25519-dalek", "failure", "hex 0.4.3", @@ -1426,7 +1444,6 @@ dependencies = [ "num", "num-traits", "rand 0.7.3", - "sha2 0.10.6", "similar", "ton_block", "ton_types", @@ -1435,13 +1452,13 @@ dependencies = [ [[package]] name = "tvm_linker" -version = "0.20.1" -source = "git+https://github.com/tonlabs/TVM-linker.git?tag=0.20.1#1a49f2a5d6322603bd77df77c35e9f6cf50a4004" +version = "0.20.3" +source = "git+https://github.com/tonlabs/TVM-linker.git?tag=0.20.3#d52a117d09694e4af92578ccd5098dc0f3a00514" dependencies = [ "base64 0.13.1", "clap 2.34.0", "crc", - "ed25519", + "ed25519 2.2.1", "ed25519-dalek", "failure", "hex 0.4.3", @@ -1453,7 +1470,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "simplelog", "ton_abi", "ton_block", @@ -1470,9 +1487,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-width" @@ -1554,9 +1571,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1564,24 +1581,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1589,22 +1606,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" @@ -1731,7 +1748,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] diff --git a/Changelog_TON.md b/Changelog_TON.md index 169de9f4..300523c8 100644 --- a/Changelog_TON.md +++ b/Changelog_TON.md @@ -1,7 +1,76 @@ +### 0.70.0 (2023-06-14) + +Compiler features: + * Supported [inline assembly](./API.md#assembly). + * Supported overriding `onCodeUpgrade` function. + * Supported [User-defined Value Types](https://docs.soliditylang.org/en/latest/types.html#user-defined-value-types). You can also use this type in public functions. + * [Supported `type(T).min` and `type(T).max`](API.md#type-information). + * New `.store*()` functions: + * `.storeIntLE2()` + * `.storeIntLE4()` + * `.storeIntLE8()` + * `.storeUintLE2()` + * `.storeUintLE4()` + * `.storeUintLE8()` + * New `.load*()` functions: + * `.loadIntQ()` + * `.loadUintQ()` + * `.loadSliceQ()` + * `.loadIntLE2()` + * `.loadIntLE4()` + * `.loadIntLE8()` + * `.loadUintLE2()` + * `.loadUintLE4()` + * `.loadUintLE8()` + * `.loadIntLE4Q()` + * `.loadIntLE8Q()` + * `.loadUintLE4Q()` + * `.loadUintLE8Q()` + * New `.preload*()` functions: + * `.preload()` + * `.preloadQ()` + * `.preloadRef()` + * `.preloadInt()` + * `.preloadIntQ()` + * `.preloadUint()` + * `.preloadUintQ()` + * `.preloadSlice()` + * `.preloadSliceQ()` + * `.preloadIntLE4()` + * `.preloadIntLE8()` + * `.preloadUintLE4()` + * `.preloadUintLE8()` + * `.preloadIntLE4Q()` + * `.preloadIntLE8Q()` + * `.preloadUintLE4Q()` + * `.preloadUintLE8Q()` + * Supported `variable integer` type in arguments of `math.*` functions. + * Supported conversion `bytes`/`string` <=> `TvmSlice`. + +Gas optimizations: + * Assorted stack optimizations. + +Bugfixes: + * Fixed compilation fail if you use `.loadSlice(l)` where `l` is constant and `l == 0` or `l > 256`. + +Other changes: + * Renamed some functions. Old functions are available and marked as deprecated. Renaming: + * `.decode()` -> `.load()` + * `.decodeQ()` -> `.loadQ()` + * `.decodeFunctionParams()` -> `.loadFunctionParams()` + * `.decodeStateVars()` -> `.loadStateVars()` + * `.loadSigned()` -> `.loadInt()` + * `.loadUnsigned()` -> `.loadUint()` + * `.storeSigned()` -> `.storeInt()` + * `.storeUnsigned()` -> `.storeUint()` + * Improved `try-catch` (experimental feature). + * Now `tx.storageFee` returns `uint120` (not `uint64`). + * Renamed `tx.timestamp` to `tx.logicaltime`. `tx.timestamp` is available and marked as deprecated. + ### 0.69.0 (2023-05-15) Breaking changes: -* `pragma AbiHeader time` is no longer allowed for use. Timestamp in header of external message is enabled by default and can be disabled with `pragma AbiHeader notime`. + * `pragma AbiHeader time` is no longer allowed for use. Timestamp in header of external message is enabled by default and can be disabled with `pragma AbiHeader notime`. Compiler features: * Supported generating code for The Open Network. Use command line option `--tvm-version ton`. We don't use `ZERO(SWAP|ROTR)IF[NOT][2]`, `COPYLEFT`, `INITCODEHASH`, `MYCODE`, `STORAGEFEE`, `LDCONT`, `STCONT` and another opcodes for ton. @@ -10,7 +79,7 @@ Optimizations: * Improved constant analyzer that compile more optimal code for `require`/`revert` functions and another cases. Bugfixes: - * Fixed segmentation fault that could occur in some cases of using `var[U]Int` types. + * Fixed segmentation fault that could occur in some cases of using `variable integer` types. * Fixed segmentation fault that occurred in constructions `optional(T1, T2) x = null;` and some another cases. ### 0.68.0 (2023-04-19) diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 0b17854a..67ade9bb 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.69.0") +set(PROJECT_VERSION "0.70.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/libsolidity/analysis/ControlFlowBuilder.cpp b/compiler/libsolidity/analysis/ControlFlowBuilder.cpp index f536922f..a8179e5b 100644 --- a/compiler/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/compiler/libsolidity/analysis/ControlFlowBuilder.cpp @@ -93,10 +93,9 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional) bool ControlFlowBuilder::visit(TryStatement const& _tryStatement) { - appendControlFlow(_tryStatement.body()); - - auto nodes = splitFlow(1); - nodes[0] = createFlow(nodes[0], _tryStatement.clause().block()); + auto nodes = splitFlow(2); + nodes[0] = createFlow(nodes[0], _tryStatement.body()); + nodes[1] = createFlow(nodes[1], _tryStatement.clause()); mergeFlow(nodes); return false; diff --git a/compiler/libsolidity/analysis/StaticAnalyzer.cpp b/compiler/libsolidity/analysis/StaticAnalyzer.cpp index a71e143a..daad4564 100644 --- a/compiler/libsolidity/analysis/StaticAnalyzer.cpp +++ b/compiler/libsolidity/analysis/StaticAnalyzer.cpp @@ -113,20 +113,23 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) return true; } -void StaticAnalyzer::endVisit(FunctionDefinition const&) +void StaticAnalyzer::endVisit(FunctionDefinition const& _funDefinition) { if (m_currentFunction && !m_currentFunction->body().statements().empty()) for (auto const& var: m_localVarUseCount) if (var.second == 0) { - if (var.first.second->isCallableOrCatchParameter()) - m_errorReporter.warning( - 5667_error, - var.first.second->location(), - "Unused " + - string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") + - " parameter. Remove or comment out the variable name to silence this warning." - ); + if (var.first.second->isCallableOrCatchParameter()) { + if (!_funDefinition.isInlineAssembly()) { + m_errorReporter.warning( + 5667_error, + var.first.second->location(), + "Unused " + + string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") + + " parameter. Remove or comment out the variable name to silence this warning." + ); + } + } else m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable."); } diff --git a/compiler/libsolidity/analysis/TypeChecker.cpp b/compiler/libsolidity/analysis/TypeChecker.cpp index c3c6bc52..d83a394b 100644 --- a/compiler/libsolidity/analysis/TypeChecker.cpp +++ b/compiler/libsolidity/analysis/TypeChecker.cpp @@ -524,6 +524,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio wrongType = contractType->isSuper(); else if ( typeCategory != Type::Category::Integer && + typeCategory != Type::Category::VarInteger && typeCategory != Type::Category::Enum ) wrongType = true; @@ -719,6 +720,14 @@ bool TypeChecker::isBadAbiType( case Type::Category::VarInteger: break; + case Type::Category::UserDefinedValueType: { + auto userDefType = dynamic_cast(curType); + if (isBadAbiType(origVarLoc, &userDefType->underlyingType(), curVarLoc, usedStructs, doPrintErr)) { + return true; + } + break; + } + default: { printError("ABI doesn't support " + curType->toString() + " type."); return true; @@ -757,8 +766,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!_function.modifiers().empty() && _function.isFree()) m_errorReporter.syntaxError(5811_error, _function.location(), "Free functions cannot have modifiers."); - if (_function.externalMsg() || _function.internalMsg()) { - if (_function.externalMsg() && _function.internalMsg()) { + 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.)"); } if (!_function.functionIsExternallyVisible()) { @@ -1152,9 +1161,9 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) auto printError = [&](SourceLocation const& loc){ m_errorReporter.typeError( - 228_error, - loc, - "Expected `catch (variant value, uint number) { ... }`."); + 228_error, + loc, + "Expected `catch (variant value, uint number) { ... }`."); }; if (errArgs.size() != 2) { printError(clause.location()); @@ -1163,7 +1172,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) if (*errArgs.at(0)->type() != *TypeProvider::variant()) { printError(errArgs.at(0)->location()); } - if (*errArgs.at(1)->type() != *TypeProvider::uint256()) { + if (*errArgs.at(1)->type() != *TypeProvider::uint(16)) { printError(errArgs.at(1)->location()); } } @@ -3350,11 +3359,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (const auto & arg : arguments) { Type::Category cat = arg->annotation().type->mobileType()->category(); - if (cat != Type::Category::Integer) { + if (cat != Type::Category::Integer && cat != Type::Category::VarInteger) { m_errorReporter.fatalTypeError( 228_error, arg->location(), - "Expected an integer type." + "Expected an integer or variable integer type." ); } } @@ -3376,11 +3385,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (const auto & arg : arguments) { Type::Category cat = arg->annotation().type->mobileType()->category(); - if (cat != Type::Category::Integer && cat != Type::Category::FixedPoint) { + if (cat != Type::Category::Integer && cat != Type::Category::FixedPoint && cat != Type::Category::VarInteger) { m_errorReporter.fatalTypeError( 228_error, arg->location(), - "Expected integer or fixed point type." + "Expected integer, variable integer or fixed point type." ); } } @@ -3491,13 +3500,15 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = checkSliceDecode(arguments); break; } - case FunctionType::Kind::TVMSliceDecode: + case FunctionType::Kind::TVMSliceLoad: + case FunctionType::Kind::TVMSlicePreload: { checkAtLeastOneArg(); returnTypes = checkSliceDecode(_functionCall.arguments()); break; } - case FunctionType::Kind::TVMSliceDecodeQ: + case FunctionType::Kind::TVMSliceLoadQ: + case FunctionType::Kind::TVMSlicePreloadQ: { checkAtLeastOneArg(); TypePointers members = checkSliceDecode(_functionCall.arguments()); // TODO make for Q @@ -3512,7 +3523,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = getReturnTypesForTVMConfig(_functionCall); break; } - case FunctionType::Kind::DecodeFunctionParams: + case FunctionType::Kind::TVMSliceLoadFunctionParams: { if (arguments.size() != 1) { m_errorReporter.fatalTypeError( @@ -3563,7 +3574,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } - case FunctionType::Kind::TVMSliceDecodeStateVars: + case FunctionType::Kind::TVMSliceLoadStateVars: { if (arguments.size() != 1) { m_errorReporter.fatalTypeError( diff --git a/compiler/libsolidity/analysis/ViewPureChecker.cpp b/compiler/libsolidity/analysis/ViewPureChecker.cpp index a918e5fb..7f5a7bde 100644 --- a/compiler/libsolidity/analysis/ViewPureChecker.cpp +++ b/compiler/libsolidity/analysis/ViewPureChecker.cpp @@ -53,7 +53,7 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) "Library functions must have default mutability. Delete keyword view or pure."); } } - if (_funDef.isFree() && _funDef.stateMutability() != StateMutability::NonPayable) { + if (_funDef.isFree() && !_funDef.isInlineAssembly() && _funDef.stateMutability() != StateMutability::NonPayable) { m_errorReporter.warning(228_error, _funDef.location(), "Free functions must have default mutability. Delete keyword view or pure."); } @@ -82,7 +82,8 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) !_funDef.isFree() && !_funDef.isFallback() && !_funDef.isReceive() && - !_funDef.virtualSemantics() + !_funDef.virtualSemantics() && + !_funDef.isInlineAssembly() ) m_errorReporter.warning( 2018_error, @@ -264,7 +265,7 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall) auto ident = dynamic_cast(&_functionCall.expression()); if (ident) { auto funcDef = dynamic_cast(ident->annotation().referencedDeclaration); - isFree = funcDef && funcDef->isFree(); + isFree = funcDef && funcDef->isFree() && !funcDef->isInlineAssembly(); } if (isFree) mutability = StateMutability::Pure; @@ -412,6 +413,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::TVM, "setGasLimit"}, {MagicType::Kind::TVM, "setcode"}, {MagicType::Kind::TVM, "stateInitHash"}, + {MagicType::Kind::Transaction, "logicaltime"}, {MagicType::Kind::Transaction, "storageFee"}, {MagicType::Kind::Transaction, "timestamp"}, }; diff --git a/compiler/libsolidity/ast/AST.cpp b/compiler/libsolidity/ast/AST.cpp index 12d6a658..32dbdf21 100644 --- a/compiler/libsolidity/ast/AST.cpp +++ b/compiler/libsolidity/ast/AST.cpp @@ -832,6 +832,11 @@ StatementAnnotation& Statement::annotation() const return initAnnotation(); } +StatementAnnotation& FreeInlineAssembly::annotation() const +{ + return initAnnotation(); +} + InlineAssemblyAnnotation& InlineAssembly::annotation() const { return initAnnotation(); diff --git a/compiler/libsolidity/ast/AST.h b/compiler/libsolidity/ast/AST.h index 4d217022..8d727af7 100644 --- a/compiler/libsolidity/ast/AST.h +++ b/compiler/libsolidity/ast/AST.h @@ -930,11 +930,12 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen std::vector> _modifiers, ASTPointer const& _returnParameters, ASTPointer const& _body, - std::optional _functionID = {}, - bool _isInline = false, - bool _responsible = false, - bool _externalMsg = false, - bool _internalMsg = false + std::optional _functionID, + bool _isInline, + bool _responsible, + bool _externalMsg, + bool _internalMsg, + bool _freeInlineAssembly ): CallableDeclaration(_id, _location, _name, std::move(_nameLocation), _visibility, _parameters, _isVirtual, _overrides, _returnParameters), StructurallyDocumented(_documentation), @@ -945,10 +946,11 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen m_functionModifiers(std::move(_modifiers)), m_body(_body), m_functionID(_functionID), - m_isInline(_isInline), + m_inline(_isInline), m_responsible{_responsible}, m_externalMsg{_externalMsg}, - m_internalMsg{_internalMsg} + m_internalMsg{_internalMsg}, + m_inlineAssembly{_freeInlineAssembly} { solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive || _kind == Token::onBounce || @@ -1015,10 +1017,11 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen } std::optional functionID() const { return m_functionID; } - bool isInline() const { return m_isInline; } + bool isInline() const { return m_inline; } bool isResponsible() const { return m_responsible; } - bool externalMsg() const { return m_externalMsg; } - bool internalMsg() const { return m_internalMsg; } + bool isExternalMsg() const { return m_externalMsg; } + bool isInternalMsg() const { return m_internalMsg; } + bool isInlineAssembly() const { return m_inlineAssembly; } FunctionDefinition const& resolveVirtual( ContractDefinition const& _mostDerivedContract, ContractDefinition const* _searchStart = nullptr @@ -1031,10 +1034,11 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen std::vector> m_functionModifiers; ASTPointer m_body; std::optional m_functionID; - bool m_isInline; + bool m_inline{}; bool m_responsible{}; bool m_externalMsg{}; bool m_internalMsg{}; + bool m_inlineAssembly{}; }; /** @@ -1558,6 +1562,23 @@ class Statement: public ASTNode, public Documented StatementAnnotation& annotation() const override; }; +class FreeInlineAssembly: public Statement +{ +public: + explicit FreeInlineAssembly( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _docString, + std::vector> _lines + ): Statement(_id, _location, _docString), m_lines{_lines} {} + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + StatementAnnotation& annotation() const override; + std::vector> const& lines() const { return m_lines; } +private: + std::vector> m_lines; +}; + /** * Inline assembly. */ diff --git a/compiler/libsolidity/ast/ASTJsonExporter.cpp b/compiler/libsolidity/ast/ASTJsonExporter.cpp index ab06b215..64ad29af 100644 --- a/compiler/libsolidity/ast/ASTJsonExporter.cpp +++ b/compiler/libsolidity/ast/ASTJsonExporter.cpp @@ -601,6 +601,14 @@ bool ASTJsonExporter::visit(ArrayTypeName const& _node) return false; } +bool ASTJsonExporter::visit(FreeInlineAssembly const& _node) +{ + setJsonNode(_node, "FreeInlineAssembly", { + make_pair("body", toJson(_node.lines())) + }); + return false; +} + bool ASTJsonExporter::visit(InlineAssembly const& /*_node*/) { return false; diff --git a/compiler/libsolidity/ast/ASTJsonExporter.h b/compiler/libsolidity/ast/ASTJsonExporter.h index 91786206..9b0f7085 100644 --- a/compiler/libsolidity/ast/ASTJsonExporter.h +++ b/compiler/libsolidity/ast/ASTJsonExporter.h @@ -98,6 +98,7 @@ class ASTJsonExporter: public ASTConstVisitor bool visit(Optional const& _node) override; bool visit(TvmVector const& _node) override; bool visit(ArrayTypeName const& _node) override; + bool visit(FreeInlineAssembly const& _node) override; bool visit(InlineAssembly const& _node) override; bool visit(Block const& _node) override; bool visit(PlaceholderStatement const& _node) override; diff --git a/compiler/libsolidity/ast/ASTJsonImporter.cpp b/compiler/libsolidity/ast/ASTJsonImporter.cpp index 19988777..f618005f 100644 --- a/compiler/libsolidity/ast/ASTJsonImporter.cpp +++ b/compiler/libsolidity/ast/ASTJsonImporter.cpp @@ -510,7 +510,13 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V createParameterList(member(_node, "parameters")), modifiers, createParameterList(member(_node, "returnParameters")), - memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr + memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr, + nullopt, + false, + false, + false, + false, + false ); } diff --git a/compiler/libsolidity/ast/ASTVisitor.h b/compiler/libsolidity/ast/ASTVisitor.h index 70afa137..8de5358c 100644 --- a/compiler/libsolidity/ast/ASTVisitor.h +++ b/compiler/libsolidity/ast/ASTVisitor.h @@ -80,6 +80,7 @@ class ASTVisitor virtual bool visit(Optional& _node) { return visitNode(_node); } virtual bool visit(TvmVector& _node) { return visitNode(_node); } virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); } + virtual bool visit(FreeInlineAssembly& _node) { return visitNode(_node); } virtual bool visit(InlineAssembly& _node) { return visitNode(_node); } virtual bool visit(Block& _node) { return visitNode(_node); } virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); } @@ -143,6 +144,7 @@ class ASTVisitor virtual void endVisit(Optional& _node) { endVisitNode(_node); } virtual void endVisit(TvmVector& _node) { endVisitNode(_node); } virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); } + virtual void endVisit(FreeInlineAssembly& _node) { endVisitNode(_node); } virtual void endVisit(InlineAssembly& _node) { endVisitNode(_node); } virtual void endVisit(Block& _node) { endVisitNode(_node); } virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); } @@ -237,6 +239,7 @@ class ASTConstVisitor virtual bool visit(ForStatement const& _node) { return visitNode(_node); } virtual bool visit(ForEachStatement const& _node) { return visitNode(_node); } virtual bool visit(Continue const& _node) { return visitNode(_node); } + virtual bool visit(FreeInlineAssembly const& _node) { return visitNode(_node); } virtual bool visit(InlineAssembly const& _node) { return visitNode(_node); } virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } @@ -300,6 +303,7 @@ class ASTConstVisitor virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ForEachStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Continue const& _node) { endVisitNode(_node); } + virtual void endVisit(FreeInlineAssembly const& _node) { endVisitNode(_node); } virtual void endVisit(InlineAssembly const& _node) { endVisitNode(_node); } virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } diff --git a/compiler/libsolidity/ast/AST_accept.h b/compiler/libsolidity/ast/AST_accept.h index 5da46703..ec0fd7e6 100644 --- a/compiler/libsolidity/ast/AST_accept.h +++ b/compiler/libsolidity/ast/AST_accept.h @@ -543,6 +543,18 @@ void ArrayTypeName::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void FreeInlineAssembly::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void FreeInlineAssembly::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void InlineAssembly::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/compiler/libsolidity/ast/TypeProvider.cpp b/compiler/libsolidity/ast/TypeProvider.cpp index 3d194843..0c786275 100644 --- a/compiler/libsolidity/ast/TypeProvider.cpp +++ b/compiler/libsolidity/ast/TypeProvider.cpp @@ -1040,6 +1040,7 @@ MagicType const* TypeProvider::meta(Type const* _type) _type && ( _type->category() == Type::Category::Contract || _type->category() == Type::Category::Integer || + _type->category() == Type::Category::VarInteger || _type->category() == Type::Category::Enum ), "Only enum, contracts or integer types supported for now." diff --git a/compiler/libsolidity/ast/TypeProvider.h b/compiler/libsolidity/ast/TypeProvider.h index a228c868..e5060039 100644 --- a/compiler/libsolidity/ast/TypeProvider.h +++ b/compiler/libsolidity/ast/TypeProvider.h @@ -104,6 +104,7 @@ class TypeProvider } static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); } + static IntegerType const* int_(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Signed); } static IntegerType const* uint256() { return uint(256); } static IntegerType const* int256() { return integer(256, IntegerType::Modifier::Signed); } diff --git a/compiler/libsolidity/ast/Types.cpp b/compiler/libsolidity/ast/Types.cpp index 26f8cc6c..dab3d911 100644 --- a/compiler/libsolidity/ast/Types.cpp +++ b/compiler/libsolidity/ast/Types.cpp @@ -1682,7 +1682,10 @@ BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (isImplicitlyConvertibleTo(_convertTo)) return true; - if (isByteArray() && (_convertTo.category() == Category::FixedBytes)) + if (isByteArrayOrString() && _convertTo.category() == Category::TvmSlice) + return true; + + if (isByteArray() && _convertTo.category() == Category::FixedBytes) return true; // allow conversion bytes <-> string and bytes -> bytesNN @@ -1880,14 +1883,24 @@ MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const if (isByteArrayOrString()) { - members.emplace_back("toSlice", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmslice()}, - strings{}, - strings{{}}, - FunctionType::Kind::ByteToSlice, - StateMutability::Pure - )); + if (isByteArray()) + members.emplace_back("toSlice", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{{}}, + FunctionType::Kind::ByteToSlice, + StateMutability::Pure + )); + else + members.emplace_back("toSlice", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{{}}, + FunctionType::Kind::StringToSlice, + StateMutability::Pure + )); members.emplace_back("dataSize", TypeProvider::function( {TypeProvider::uint256()}, {TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}, @@ -3117,16 +3130,30 @@ string FunctionType::richIdentifier() const case Kind::StringMethod: id += "stringmethod"; break; case Kind::StringSubstr: id += "stringsubstr"; break; case Kind::StringToLowerCase: id += "stringtolowercase"; break; + case Kind::StringToSlice: id += "stringtoslice"; break; case Kind::StringToUpperCase: id += "stringtouppercase"; break; - case Kind::DecodeFunctionParams: id += "tvmslicedecodefunctionparams"; break; case Kind::TVMSliceCompare: id += "tvmslicecompare"; break; case Kind::TVMSliceDataSize: id += "tvmslicedatasize"; break; - case Kind::TVMSliceDecode: id += "tvmslicedecode"; break; - case Kind::TVMSliceDecodeQ: id += "tvmslicedecodeq"; break; - case Kind::TVMSliceDecodeStateVars: id += "tvmslicedecodestatevars"; break; case Kind::TVMSliceEmpty: id += "tvmsliceempty"; break; case Kind::TVMSliceHas: id += "tvmslicehasxxx"; break; + case Kind::TVMSliceLoad: id += "tvmsliceload"; break; + case Kind::TVMSliceLoadFunctionParams: id += "tvmsliceloadfunctionparams"; break; + case Kind::TVMSliceLoadInt: id += "tvmsliceloadint"; break; + case Kind::TVMSliceLoadIntQ: id += "tvmsliceloadintq"; break; + case Kind::TVMSliceLoadLE: id += "tvmsliceloadle"; break; + case Kind::TVMSliceLoadQ: id += "tvmsliceloadq"; break; + case Kind::TVMSliceLoadStateVars: id += "tvmslicedecodestatevars"; break; + case Kind::TVMSliceLoadUint: id += "tvmsliceloaduint"; break; + case Kind::TVMSliceLoadUintQ: id += "tvmsliceloaduintq"; break; + case Kind::TVMSlicePreLoadInt: id += "tvmslicepreloadint"; break; + case Kind::TVMSlicePreLoadIntQ: id += "tvmslicepreloadintq"; break; + case Kind::TVMSlicePreLoadSlice: id += "tvmslicepreloadslice"; break; + case Kind::TVMSlicePreLoadUint: id += "tvmslicepreloaduint"; break; + case Kind::TVMSlicePreLoadUintQ: id += "tvmslicepreloaduintq"; break; + case Kind::TVMSlicePreload: id += "tvmslicepreload"; break; + case Kind::TVMSlicePreloadQ: id += "tvmslicepreloadq"; break; + case Kind::TVMSlicePreloadRef: id += "tvmslicepreloadref"; break; case Kind::TVMSliceSize: id += "tvmslicesize"; break; case Kind::TVMSliceSkip: id += "tvmsliceskip"; break; @@ -3146,6 +3173,8 @@ string FunctionType::richIdentifier() const case Kind::TVMBuilderMethods: id += "tvmbuildermethods"; break; case Kind::TVMBuilderStore: id += "tvmbuilderstore"; break; + case Kind::TVMBuilderStoreInt: id += "tvmbuilderstoreint"; break; + case Kind::TVMBuilderStoreUint: id += "tvmbuilderstoreuint"; break; case Kind::TVMTuplePush: id += "tvmtuplepush"; break; case Kind::TVMTuplePop: id += "tvmtuplepop"; break; @@ -3166,8 +3195,8 @@ string FunctionType::richIdentifier() const case Kind::TVMFunctionId: id += "tvmfunctionid"; break; case Kind::TVMHash: id += "tvmhash"; break; case Kind::TVMInitCodeHash: id += "tvminitcodehash"; break; - case Kind::TVMLoadRef: id += "tvmloadref"; break; - case Kind::TVMLoadSlice: id += "tvmloadslice"; break; + case Kind::TVMSliceLoadRef: id += "tvmloadref"; break; + case Kind::TVMSliceLoadSlice: id += "tvmloadslice"; break; case Kind::TVMPubkey: id += "tvmpubkey"; break; case Kind::TVMRawConfigParam: id += "tvmrawconfigparam"; break; case Kind::TVMReplayProtInterval: id += "tvmreplayprotinterval"; break; @@ -4798,7 +4827,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const )); members.emplace_back("sign", TypeProvider::function( TypePointers{TypeProvider::integer(256, IntegerType::Modifier::Signed)}, - TypePointers{TypeProvider::integer(8, IntegerType::Modifier::Signed)}, + TypePointers{TypeProvider::integer(2, IntegerType::Modifier::Signed)}, strings{string("value")}, strings{string("sign")}, FunctionType::Kind::MathSign, @@ -4808,9 +4837,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const } case Kind::Transaction: return MemberList::MemberMap({ - {"origin", TypeProvider::address()}, {"gasprice", TypeProvider::uint256()}, - {"storageFee", TypeProvider::uint(64)}, + {"logicaltime", TypeProvider::uint(64)}, + {"origin", TypeProvider::address()}, + {"storageFee", TypeProvider::uint(120)}, {"timestamp", TypeProvider::uint(64)}, }); case Kind::ABI: @@ -4970,6 +5000,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const m_typeArgument && ( m_typeArgument->category() == Type::Category::Contract || m_typeArgument->category() == Type::Category::Integer || + m_typeArgument->category() == Type::Category::VarInteger || m_typeArgument->category() == Type::Category::Enum ), "Only enums, contracts or integer types supported for now" @@ -4998,6 +5029,14 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"max", integerTypePointer}, }); } + else if (m_typeArgument->category() == Type::Category::VarInteger) + { + VarInteger const* varIntTypePointer = dynamic_cast(m_typeArgument); + return MemberList::MemberMap({ + {"min", varIntTypePointer}, + {"max", varIntTypePointer}, + }); + } else if (m_typeArgument->category() == Type::Category::Enum) { EnumType const* enumTypePointer = dynamic_cast(m_typeArgument); @@ -5106,6 +5145,19 @@ MemberList::MemberMap OptionalType::nativeMembers(ASTNode const*) const return members; } +BoolResult TvmSliceType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + + if (auto arr = dynamic_cast(&_convertTo)) { + if (arr->isByteArrayOrString()) { + return true; + } + } + + return false; +} + TypeResult TvmSliceType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) return TypeProvider::emptyTuple(); @@ -5115,8 +5167,7 @@ TypeResult TvmSliceType::unaryOperatorResult(Token _operator) const { MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { MemberList::MemberMap members = { { - "dataSize", - TypeProvider::function( + "dataSize", TypeProvider::function( {TypeProvider::uint256()}, {TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}, {{}}, @@ -5127,8 +5178,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { ) }, { - "dataSizeQ", - TypeProvider::function( + "dataSizeQ", TypeProvider::function( {TypeProvider::uint256()}, {TypeProvider::optional(TypeProvider::tuple({TypeProvider::uint256(), TypeProvider::uint256(), TypeProvider::uint256()}))}, {{}}, @@ -5139,8 +5189,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { ) }, { - "loadOnes", - TypeProvider::function( + "loadOnes", TypeProvider::function( {}, {TypeProvider::uint(10)}, {}, @@ -5150,8 +5199,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { ) }, { - "loadZeroes", - TypeProvider::function( + "loadZeroes", TypeProvider::function( {}, {TypeProvider::uint(10)}, {}, @@ -5161,8 +5209,7 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { ) }, { - "loadSame", - TypeProvider::function( + "loadSame", TypeProvider::function( {TypeProvider::uint(1)}, {TypeProvider::uint(10)}, {{}}, @@ -5171,281 +5218,709 @@ MemberList::MemberMap TvmSliceType::nativeMembers(ASTNode const *) const { StateMutability::Pure ) }, - }; - - members.emplace_back("decode", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::TVMSliceDecode, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - members.emplace_back("decodeQ", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::TVMSliceDecodeQ, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - - members.emplace_back("decodeFunctionParams", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::DecodeFunctionParams, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - - members.emplace_back("decodeStateVars", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::TVMSliceDecodeStateVars, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - - members.emplace_back("loadUnsigned", TypeProvider::function( - TypePointers{TypeProvider::uint(16)}, - TypePointers{TypeProvider::uint(8)}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMLoadRef, - StateMutability::Pure - )); - - members.emplace_back("loadSigned", TypeProvider::function( - TypePointers{TypeProvider::uint(16)}, - TypePointers{TypeProvider::integer(8, IntegerType::Modifier::Signed)}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMLoadRef, - StateMutability::Pure - )); - - members.emplace_back("hasNBits", TypeProvider::function( - TypePointers{TypeProvider::uint(16)}, - TypePointers{TypeProvider::boolean()}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMSliceHas, - StateMutability::Pure - )); - - members.emplace_back("hasNRefs", TypeProvider::function( - TypePointers{TypeProvider::uint(8)}, - TypePointers{TypeProvider::boolean()}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMSliceHas, - StateMutability::Pure - )); - - members.emplace_back("hasNBitsAndRefs", TypeProvider::function( - TypePointers{TypeProvider::uint(16), TypeProvider::uint(8)}, - TypePointers{TypeProvider::boolean()}, - strings{string(), string()}, - strings{string()}, - FunctionType::Kind::TVMSliceHas, - StateMutability::Pure - )); - - members.emplace_back("loadTons", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(128)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMLoadRef, - StateMutability::Pure - )); - - members.emplace_back("loadSlice", TypeProvider::function( - {TypeProvider::uint256()}, - {TypeProvider::tvmslice()}, - {{}}, - {{}}, - FunctionType::Kind::TVMLoadSlice, - StateMutability::Pure - )); - - members.emplace_back("loadSlice", TypeProvider::function( - {TypeProvider::uint256(), TypeProvider::uint256()}, - {TypeProvider::tvmslice()}, - {{},{}}, - {{}}, - FunctionType::Kind::TVMLoadSlice, - StateMutability::Pure - )); - - members.emplace_back("skip", TypeProvider::function( - strings{"uint"}, - strings{}, - FunctionType::Kind::TVMSliceSkip, - StateMutability::Pure - )); - - members.emplace_back("skip", TypeProvider::function( - strings{"uint", "uint"}, - strings{}, - FunctionType::Kind::TVMSliceSkip, - StateMutability::Pure - )); - - members.emplace_back("size", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16), TypeProvider::uint(8)}, - strings{}, - strings{string(), string()}, - FunctionType::Kind::TVMSliceSize, - StateMutability::Pure - )); - - members.emplace_back("empty", TypeProvider::function( - {}, - {TypeProvider::boolean()}, - {}, - {{}}, - FunctionType::Kind::TVMSliceEmpty, - StateMutability::Pure - )); - - members.emplace_back("bits", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMSliceSize, - StateMutability::Pure - )); - - members.emplace_back("refs", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(8)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMSliceSize, - StateMutability::Pure - )); - - members.emplace_back("depth", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMSliceSize, - StateMutability::Pure - )); - - members.emplace_back("loadRef", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmcell()}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMLoadRef, - StateMutability::Pure - )); - - members.emplace_back("loadRefAsSlice", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmslice()}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMLoadRef, - StateMutability::Pure - )); - - members.emplace_back("compare", TypeProvider::function( - TypePointers{TypeProvider::tvmslice()}, - TypePointers{TypeProvider::integer(8, IntegerType::Modifier::Signed)}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMSliceCompare, - StateMutability::Pure - )); - - return members; -} - -TypeResult TvmCellType::unaryOperatorResult(Token _operator) const { - if (_operator == Token::Delete) - return TypeProvider::emptyTuple(); - return nullptr; -} - -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 - )); - - - return members; -} - -MemberList::MemberMap Variant::nativeMembers(ASTNode const* /*_currentScope*/) const { - MemberList::MemberMap members; - members.emplace_back("isUint", TypeProvider::function( - {}, - {TypeProvider::boolean()}, - {}, - {{}}, - FunctionType::Kind::VariantIsUint, - StateMutability::Pure - )); - members.emplace_back("toUint", TypeProvider::function( - {}, - {TypeProvider::uint256()}, - {}, - {{}}, - FunctionType::Kind::VariantToUint, - StateMutability::Pure - )); - return members; -} - -TypeResult TvmVectorType::unaryOperatorResult(Token _operator) const { - if (_operator == Token::Delete) + { + "decode", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoad, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "load", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoad, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "preload", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSlicePreload, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "decodeQ", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadQ, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "loadQ", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadQ, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "preloadQ", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSlicePreloadQ, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "decodeFunctionParams", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadFunctionParams, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "loadFunctionParams", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadFunctionParams, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "decodeStateVars", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadStateVars, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "loadStateVars", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMSliceLoadStateVars, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "loadUnsigned", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::uint256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadUint, + StateMutability::Pure + ) + }, + { + "loadUint", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::uint256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadUint, + StateMutability::Pure + ) + }, + { + "loadUintQ", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::optional(TypeProvider::uint256())}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadUintQ, + StateMutability::Pure + ) + }, + { + "loadIntLE2", TypeProvider::function( + {}, + {TypeProvider::int_(16)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadIntLE4", TypeProvider::function( + {}, + {TypeProvider::int_(32)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadIntLE8", TypeProvider::function( + {}, + {TypeProvider::int_(64)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadUintLE2", TypeProvider::function( + {}, + {TypeProvider::uint(16)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadUintLE4", TypeProvider::function( + {}, + {TypeProvider::uint(32)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadUintLE8", TypeProvider::function( + {}, + {TypeProvider::uint(64)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadIntLE4", TypeProvider::function( + {}, + {TypeProvider::int_(32)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadIntLE8", TypeProvider::function( + {}, + {TypeProvider::int_(64)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadUintLE4", TypeProvider::function( + {}, + {TypeProvider::uint(32)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadUintLE8", TypeProvider::function( + {}, + {TypeProvider::uint(64)}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadIntLE4Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::int_(32))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadIntLE8Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::int_(64))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadUintLE4Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::uint(32))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadUintLE8Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::uint(64))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadIntLE4Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::int_(32))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadIntLE8Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::int_(64))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadUintLE4Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::uint(32))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "loadUintLE8Q", TypeProvider::function( + {}, + {TypeProvider::optional(TypeProvider::uint(64))}, + {}, + {{}}, + FunctionType::Kind::TVMSliceLoadLE, + StateMutability::Pure + ) + }, + { + "preloadUint", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::uint256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSlicePreLoadUint, + StateMutability::Pure + ) + }, + { + "preloadUintQ", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::optional(TypeProvider::uint256())}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSlicePreLoadUintQ, + StateMutability::Pure + ) + }, + { + "loadSigned", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::int256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadInt, + StateMutability::Pure + ) + }, + { + "loadInt", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::int256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadInt, + StateMutability::Pure + ) + }, + { + "loadIntQ", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::optional(TypeProvider::int256())}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadIntQ, + StateMutability::Pure + ) + }, + { + "preloadInt", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::int256()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSlicePreLoadInt, + StateMutability::Pure + ) + }, + { + "preloadIntQ", TypeProvider::function( + TypePointers{TypeProvider::uint(9)}, + TypePointers{TypeProvider::optional(TypeProvider::int256())}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSlicePreLoadIntQ, + StateMutability::Pure + ) + }, + { + "hasNBits", TypeProvider::function( + TypePointers{TypeProvider::uint(10)}, + TypePointers{TypeProvider::boolean()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceHas, + StateMutability::Pure + ) + }, + { + "hasNRefs", TypeProvider::function( + TypePointers{TypeProvider::uint(2)}, + TypePointers{TypeProvider::boolean()}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceHas, + StateMutability::Pure + ) + }, + { + "hasNBitsAndRefs", TypeProvider::function( + TypePointers{TypeProvider::uint(10), TypeProvider::uint(2)}, + TypePointers{TypeProvider::boolean()}, + strings{string(), string()}, + strings{string()}, + FunctionType::Kind::TVMSliceHas, + StateMutability::Pure + ) + }, + { + "loadTons", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(128)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadRef, + StateMutability::Pure + ) + }, + { + "loadSlice", TypeProvider::function( + {TypeProvider::uint(10)}, + {TypeProvider::tvmslice()}, + {{}}, + {{}}, + FunctionType::Kind::TVMSliceLoadSlice, + StateMutability::Pure + ) + }, + { + "loadSlice", TypeProvider::function( + {TypeProvider::uint(10), TypeProvider::uint(2)}, + {TypeProvider::tvmslice()}, + {{},{}}, + {{}}, + FunctionType::Kind::TVMSliceLoadSlice, + StateMutability::Pure + ) + }, + { + "loadSliceQ", TypeProvider::function( + {TypeProvider::uint(10)}, + {TypeProvider::optional(TypeProvider::tvmslice())}, + {{}}, + {{}}, + FunctionType::Kind::TVMSliceLoadSlice, + StateMutability::Pure + ) + }, + { + "loadSliceQ", TypeProvider::function( + {TypeProvider::uint(10), TypeProvider::uint(2)}, + {TypeProvider::optional(TypeProvider::tvmslice())}, + {{},{}}, + {{}}, + FunctionType::Kind::TVMSliceLoadSlice, + StateMutability::Pure + ) + }, + { + "preloadSlice", TypeProvider::function( + {TypeProvider::uint(10)}, + {TypeProvider::tvmslice()}, + {{}}, + {{}}, + FunctionType::Kind::TVMSlicePreLoadSlice, + StateMutability::Pure + ) + }, + { + "preloadSlice", TypeProvider::function( + {TypeProvider::uint(10), TypeProvider::uint(2)}, + {TypeProvider::tvmslice()}, + {{},{}}, + {{}}, + FunctionType::Kind::TVMSlicePreLoadSlice, + StateMutability::Pure + ) + }, + { + "preloadSliceQ", TypeProvider::function( + {TypeProvider::uint(10)}, + {TypeProvider::optional(TypeProvider::tvmslice())}, + {{}}, + {{}}, + FunctionType::Kind::TVMSlicePreLoadSlice, + StateMutability::Pure + ) + }, + { + "preloadSliceQ", TypeProvider::function( + {TypeProvider::uint(10), TypeProvider::uint(2)}, + {TypeProvider::optional(TypeProvider::tvmslice())}, + {{},{}}, + {{}}, + FunctionType::Kind::TVMSlicePreLoadSlice, + StateMutability::Pure + ) + }, + { + "skip", TypeProvider::function( + strings{"uint10"}, + strings{}, + FunctionType::Kind::TVMSliceSkip, + StateMutability::Pure + ) + }, + { + "skip", TypeProvider::function( + strings{"uint10", "uint2"}, + strings{}, + FunctionType::Kind::TVMSliceSkip, + StateMutability::Pure + ) + }, + { + "size", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10), TypeProvider::uint(2)}, + strings{}, + strings{string(), string()}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, + { + "empty", TypeProvider::function( + {}, + {TypeProvider::boolean()}, + {}, + {{}}, + FunctionType::Kind::TVMSliceEmpty, + StateMutability::Pure + ) + }, + { + "bits", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, + { + "refs", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(2)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, + { + "depth", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(16)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceSize, + StateMutability::Pure + ) + }, + { + "loadRef", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmcell()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadRef, + StateMutability::Pure + ) + }, + { + "preloadRef", TypeProvider::function( + {TypeProvider::uint(2)}, + {TypeProvider::tvmcell()}, + {{}}, + {{}}, + FunctionType::Kind::TVMSlicePreloadRef, + StateMutability::Pure + ) + }, + { + "preloadRef", TypeProvider::function( + {}, + {TypeProvider::tvmcell()}, + {}, + {{}}, + FunctionType::Kind::TVMSlicePreloadRef, + StateMutability::Pure + ) + }, + { + "loadRefAsSlice", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMSliceLoadRef, + StateMutability::Pure + ) + }, + { + "compare", TypeProvider::function( + TypePointers{TypeProvider::tvmslice()}, + TypePointers{TypeProvider::integer(2, IntegerType::Modifier::Signed)}, + strings{string()}, + strings{string()}, + FunctionType::Kind::TVMSliceCompare, + StateMutability::Pure + ) + } + }; + return members; +} + +TypeResult TvmCellType::unaryOperatorResult(Token _operator) const { + if (_operator == Token::Delete) + return TypeProvider::emptyTuple(); + return nullptr; +} + +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 + )); + + + return members; +} + +MemberList::MemberMap Variant::nativeMembers(ASTNode const* /*_currentScope*/) const { + MemberList::MemberMap members; + members.emplace_back("isUint", TypeProvider::function( + {}, + {TypeProvider::boolean()}, + {}, + {{}}, + FunctionType::Kind::VariantIsUint, + StateMutability::Pure + )); + members.emplace_back("toUint", TypeProvider::function( + {}, + {TypeProvider::uint256()}, + {}, + {{}}, + FunctionType::Kind::VariantToUint, + StateMutability::Pure + )); + return members; +} + +TypeResult TvmVectorType::unaryOperatorResult(Token _operator) const { + if (_operator == Token::Delete) return TypeProvider::emptyTuple(); return nullptr; } @@ -5518,172 +5993,277 @@ MemberList::MemberMap TvmBuilderType::nativeMembers(const ASTNode *) const { MemberList::MemberMap members = { { - "storeSame", - TypeProvider::function( - {TypeProvider::uint(10), TypeProvider::uint(1)}, + "storeSame", TypeProvider::function( + {TypeProvider::uint(10), TypeProvider::uint(1)}, + {}, + {{}, {}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "depth", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(16)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "bits", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "refs", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(2)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "size", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10), TypeProvider::uint(2)}, + strings{}, + strings{string(), string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "remBits", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "remRefs", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(2)}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "remBitsAndRefs", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::uint(10), TypeProvider::uint(2)}, + strings{}, + strings{string(), string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "toCell", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmcell()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "toSlice", TypeProvider::function( + TypePointers{}, + TypePointers{TypeProvider::tvmslice()}, + strings{}, + strings{string()}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeRef", TypeProvider::function( + TypePointers{TypeProvider::tvmbuilder()}, + TypePointers{}, + strings{string()}, + strings{}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeRef", TypeProvider::function( + {TypeProvider::tvmcell()}, {}, - {{}, {}}, + {{}}, {}, FunctionType::Kind::TVMBuilderMethods, StateMutability::Pure ) }, - }; - - members.emplace_back("depth", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("bits", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("refs", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(8)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("size", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16), TypeProvider::uint(8)}, - strings{}, - strings{string(), string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("remBits", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("remRefs", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(8)}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("remBitsAndRefs", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::uint(16), TypeProvider::uint(8)}, - strings{}, - strings{string(), string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("toCell", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmcell()}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("toSlice", TypeProvider::function( - TypePointers{}, - TypePointers{TypeProvider::tvmslice()}, - strings{}, - strings{string()}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("storeRef", TypeProvider::function( - TypePointers{TypeProvider::tvmbuilder()}, - TypePointers{}, - strings{string()}, - strings{}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("storeRef", TypeProvider::function( - {TypeProvider::tvmcell()}, - {}, - {{}}, - {}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("storeRef", TypeProvider::function( - {TypeProvider::tvmslice()}, - {}, - {{}}, - {}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - for (const std::string func : {"storeOnes", "storeZeroes"}) { - members.emplace_back(func.c_str(), TypeProvider::function( + { + "storeRef", TypeProvider::function( + {TypeProvider::tvmslice()}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeOnes", TypeProvider::function( {TypeProvider::uint(10)}, {}, {{}}, {}, FunctionType::Kind::TVMBuilderMethods, StateMutability::Pure - )); - } - - members.emplace_back("store", TypeProvider::function( - TypePointers{}, - TypePointers{}, - strings{}, - strings{}, - FunctionType::Kind::TVMBuilderStore, - StateMutability::Pure, - nullptr, FunctionType::Options::withArbitraryParameters() - )); - - members.emplace_back("storeSigned", TypeProvider::function( - TypePointers{TypeProvider::integer(256, IntegerType::Modifier::Signed), TypeProvider::uint(16)}, - TypePointers{}, - strings{string(), string()}, - strings{}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - - members.emplace_back("storeUnsigned", TypeProvider::function( - TypePointers{TypeProvider::uint(256), TypeProvider::uint(16)}, - TypePointers{}, - strings{string(), string()}, - strings{}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); - members.emplace_back("storeTons", TypeProvider::function( - TypePointers{TypeProvider::uint(128)}, - TypePointers{}, - strings{string()}, - strings{}, - FunctionType::Kind::TVMBuilderMethods, - StateMutability::Pure - )); + ) + }, + { + "storeZeroes", TypeProvider::function( + {TypeProvider::uint(10)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "store", TypeProvider::function( + TypePointers{}, + TypePointers{}, + strings{}, + strings{}, + FunctionType::Kind::TVMBuilderStore, + StateMutability::Pure, + nullptr, FunctionType::Options::withArbitraryParameters() + ) + }, + { + "storeSigned", TypeProvider::function( + TypePointers{TypeProvider::int256(), TypeProvider::uint(9)}, + TypePointers{}, + strings{string(), string()}, + strings{}, + FunctionType::Kind::TVMBuilderStoreInt, + StateMutability::Pure + ) + }, + { + "storeInt", TypeProvider::function( + TypePointers{TypeProvider::int256(), TypeProvider::uint(9)}, + TypePointers{}, + strings{string(), string()}, + strings{}, + FunctionType::Kind::TVMBuilderStoreInt, + StateMutability::Pure + ) + }, + { + "storeUnsigned", TypeProvider::function( + TypePointers{TypeProvider::uint256(), TypeProvider::uint(9)}, + TypePointers{}, + strings{string(), string()}, + strings{}, + FunctionType::Kind::TVMBuilderStoreUint, + StateMutability::Pure + ) + }, + { + "storeUint", TypeProvider::function( + TypePointers{TypeProvider::uint256(), TypeProvider::uint(9)}, + TypePointers{}, + strings{string(), string()}, + strings{}, + FunctionType::Kind::TVMBuilderStoreUint, + StateMutability::Pure + ) + }, + { + "storeTons", TypeProvider::function( + TypePointers{TypeProvider::uint(128)}, + TypePointers{}, + strings{string()}, + strings{}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeIntLE2", TypeProvider::function( + {TypeProvider::int_(16)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeIntLE4", TypeProvider::function( + {TypeProvider::integer(32, IntegerType::Modifier::Signed)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeIntLE8", TypeProvider::function( + {TypeProvider::integer(64, IntegerType::Modifier::Signed)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeUintLE2", TypeProvider::function( + {TypeProvider::uint(16)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeUintLE4", TypeProvider::function( + {TypeProvider::uint(32)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + { + "storeUintLE8", TypeProvider::function( + {TypeProvider::uint(64)}, + {}, + {{}}, + {}, + FunctionType::Kind::TVMBuilderMethods, + StateMutability::Pure + ) + }, + }; return members; } @@ -5726,6 +6306,14 @@ 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); } diff --git a/compiler/libsolidity/ast/Types.h b/compiler/libsolidity/ast/Types.h index 54a7e237..9c3330b4 100644 --- a/compiler/libsolidity/ast/Types.h +++ b/compiler/libsolidity/ast/Types.h @@ -176,10 +176,11 @@ class Type FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple, Mapping, TypeType, Modifier, Magic, Module, InaccessibleDynamic, TvmCell, TvmSlice, TvmBuilder, ExtraCurrencyCollection, TvmVector, Variant, - VarInteger, InitializerList, CallList, // <-- variables of that types can't be declared in solidity contract + VarInteger, + InitializerList, CallList, // <-- variables of that types can't be declared in solidity contract Optional, - Null, // null - EmpyMap // emptyMap + Null, + EmpyMap }; /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise @@ -762,12 +763,11 @@ class TvmSliceType: public Type Category category() const override { return Category::TvmSlice; } bool isValueType() const override { return true; } std::string richIdentifier() const override { return "t_tvmslice"; } + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; TypeResult unaryOperatorResult(Token _operator) const override; std::string toString(bool) const override { return "TvmSlice"; } - Type const* encodingType() const override { return this; } TypeResult interfaceType(bool) const override { return this; } - MemberList::MemberMap nativeMembers(ASTNode const*) const override; }; @@ -811,6 +811,8 @@ 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; @@ -1391,16 +1393,29 @@ class FunctionType: public Type TVMDataSize, ///< cell.dataSize() TVMDataSizeQ, ///< cell.dataSizeQ() - DecodeFunctionParams, ///< slice.decodeFunctionParams(function_name) - TVMLoadRef, ///< slice.loadRef() - TVMLoadSlice, ///< slice.loadSlice() TVMSliceCompare, ///< slice.compare() TVMSliceDataSize, ///< slice.dataSize() - TVMSliceDecode, ///< slice.decode(types) - TVMSliceDecodeQ, ///< slice.decodeQ(types) - TVMSliceDecodeStateVars, ///< slice.decodeStateVars(contract_name) TVMSliceEmpty, ///< slice.empty() TVMSliceHas, ///< slice.hasXXX() + TVMSliceLoad, ///< slice.load(types...) + TVMSliceLoadFunctionParams, ///< slice.loadFunctionParams(function_name) + TVMSliceLoadInt, ///< slice.loadInt() + TVMSliceLoadIntQ, ///< slice.loadIntQ() + TVMSliceLoadQ, ///< slice.loadQ(types...) + TVMSliceLoadRef, ///< slice.loadRef() + TVMSliceLoadSlice, ///< slice.loadSlice() + TVMSliceLoadStateVars, ///< slice.loadStateVars(contract_name) + TVMSliceLoadUint, ///< slice.loadUint() + TVMSliceLoadUintQ, ///< slice.loadUintQ() + TVMSliceLoadLE, ///< slice.load[U]intLE(4|8)[Q]() + TVMSlicePreLoadInt, ///< slice.preloadInt() + TVMSlicePreLoadIntQ, ///< slice.preloadIntQ() + TVMSlicePreLoadSlice, ///< slice.preloadSlice() + TVMSlicePreLoadUint, ///< slice.preloadUint() + TVMSlicePreLoadUintQ, ///< slice.preloadUintQ() + TVMSlicePreload, ///< slice.preload(types...) + TVMSlicePreloadQ, ///< slice.preloadQ(types...) + TVMSlicePreloadRef, ///< slice.preloadRef() TVMSliceSize, ///< slice.size() TVMSliceSkip, ///< slice.skip() @@ -1408,6 +1423,8 @@ class FunctionType: public Type TVMBuilderMethods, ///< builder.*() TVMBuilderStore, ///< builder.store(...) + TVMBuilderStoreInt, ///< builder.storeInt() + TVMBuilderStoreUint, ///< builder.storeUint() TVMTuplePush, ///< tuple.push(...) TVMTuplePop, ///< tuple.pop() @@ -1522,6 +1539,7 @@ class FunctionType: public Type StringMethod, ///< string methods StringSubstr, ///< string.substr() StringToLowerCase, ///< string.toLowerCase() + StringToSlice, ///< .toSlice() StringToUpperCase, ///< string.toUpperCase() BytesConcat, ///< .concat() on bytes (type type) diff --git a/compiler/libsolidity/codegen/DictOperations.cpp b/compiler/libsolidity/codegen/DictOperations.cpp index e68a3ea4..e77b653b 100644 --- a/compiler/libsolidity/codegen/DictOperations.cpp +++ b/compiler/libsolidity/codegen/DictOperations.cpp @@ -244,11 +244,11 @@ void DictSet::dictSet() { switch (operation) { case SetDictOperation::Set: - pusher.push(-4 + 1, opcode); + pusher << opcode; break; case SetDictOperation::Replace: case SetDictOperation::Add: - pusher.push(-4 + 2, opcode); + pusher << opcode; break; } } diff --git a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp index 68a82843..8dafa9b1 100644 --- a/compiler/libsolidity/codegen/PeepholeOptimizer.cpp +++ b/compiler/libsolidity/codegen/PeepholeOptimizer.cpp @@ -68,7 +68,7 @@ class PrivatePeepholeOptimizer { std::optional optimizeSlice(int idx1) const; static std::optional optimizeAt1(Pointer const& cmd1, bool m_withUnpackOpaque); static std::optional optimizeAt2(Pointer const& cmd1, Pointer const& cmd2) ; - static std::optional optimizeAt3(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, bool m_withUnpackOpaque); + static std::optional optimizeAt3(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, bool m_withUnpackOpaque); static std::optional optimizeAt4(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, Pointer const& cmd4); static std::optional optimizeAt5(Pointer const& cmd1, Pointer const& cmd2, Pointer const& cmd3, @@ -484,7 +484,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer return false; } return *instructions.at(0) == *gen(isZero ? "PUSHINT 0" : "NULL") && - (!isSwap || *instructions.at(1) == *makeXCH_S(1)); + (!isSwap || *instructions.at(1) == *makeXCH_S(1)); }; // PUSHCONT { @@ -510,7 +510,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAt1(Pointer } if (f(isZero, isSwap, curBranch->instructions())) { Pointer align = getZeroOrNullAlignment(isZero, !isSwap, - !trueBranch || cmd1IfElse->withNot()); + !trueBranch || cmd1IfElse->withNot()); solAssert(trueBranch ? true : !cmd1IfElse->withNot(), ""); auto emptyBlock = createNode(curBranch->type()); return Result{1, @@ -1258,23 +1258,23 @@ std::optional PrivatePeepholeOptimizer::optimizeAt3(Pointer } } - // ; a b - // SWAP ; b a - // gen(0, 1) ; b a c - // ROT ; a c b - // => - // gen(0,1) - // SWAP - if (isSWAP(cmd1)) { - std::optional> rot = isBLKSWAP(cmd3); - if (rot && *rot == std::make_pair(1, 2)) { - if (isPureGen01(*cmd2)) { - return Result{3, cmd2, makeXCH_S(1)}; - } - } - } - - // TODO delete, fix in stackOpt + // ; a b + // SWAP ; b a + // gen(0, 1) ; b a c + // ROT ; a c b + // => + // gen(0,1) + // SWAP + if (isSWAP(cmd1)) { + std::optional> rot = isBLKSWAP(cmd3); + if (rot && *rot == std::make_pair(1, 2)) { + if (isPureGen01(*cmd2)) { + return Result{3, cmd2, makeXCH_S(1)}; + } + } + } + + // TODO delete, fix in stackOpt // Note: breaking stack // DUP // IFREF { CALL $c7_to_c4$ / $upd_only_time_in_c4$ } @@ -1564,6 +1564,7 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { c2 = get(j); } + // TODO ADD LD[I|U]LE[4|8] if (is(c1, "STZERO")) { bitString += "0"; ++opcodeQty; @@ -1598,6 +1599,12 @@ std::optional PrivatePeepholeOptimizer::optimizeAtInf(int idx1) const { bitString += StrUtils::toBitString(hexSlice); opcodeQty += 2; i = nextCommandLine(j); + } else if (c2 && isPUSHINT(c1) && is(c2, "STGRAMS")) { + bigint arg = pushintValue(c1); + bitString += StrUtils::tonsToBinaryString(arg); + opcodeQty += 2; + i = nextCommandLine(j); + break; } else { break; } diff --git a/compiler/libsolidity/codegen/StackOptimizer.cpp b/compiler/libsolidity/codegen/StackOptimizer.cpp index 7accc77f..409bc203 100644 --- a/compiler/libsolidity/codegen/StackOptimizer.cpp +++ b/compiler/libsolidity/codegen/StackOptimizer.cpp @@ -144,16 +144,16 @@ bool StackOptimizer::visit(CodeBlock &_node) { if (successfullyUpdate(i, instructions)) { m_didSome = true; - //Printer p{std::cout}; - //std::cout << i << "\n"; - //std::cout << "<<<<<\n"; - //for (const auto& x : _node.instructions()) - // x->accept(p); - //std::cout << "=====\n"; - //for (const auto& x : instructions) - // x->accept(p); - //std::cout << ">>>>>\n"; - //_node.upd(instructions); + //Printer p{std::cout}; + //std::cout << i << "\n"; + //std::cout << "<<<<<\n"; + //for (const auto& x : _node.instructions()) + // x->accept(p); + //std::cout << "=====\n"; + //for (const auto& x : instructions) + // x->accept(p); + //std::cout << ">>>>>\n"; + //_node.upd(instructions); // do nothing } else { @@ -232,23 +232,23 @@ bool StackOptimizer::visit(TvmUntil &_node) { } bool StackOptimizer::visit(TryCatch &_node) { - const int savedStack = size(); - - // try body - startScope(); - _node.tryBody()->accept(*this); - endScope(); - solAssert(savedStack == size(), ""); - - // catch body - startScope(); - delta(2); // 2 error variables - _node.catchBody()->accept(*this); - solAssert(savedStack == size(), ""); - endScope(); - - solAssert(savedStack == size(), ""); - return false; + const int savedStack = size(); + + // try body + startScope(); + _node.tryBody()->accept(*this); + endScope(); + solAssert(savedStack == size(), ""); + + // catch body + startScope(); + delta(2); // 2 error variables + _node.catchBody()->accept(*this); + solAssert(savedStack <= size(), ""); + endScope(); + + solAssert(savedStack == size(), ""); + return false; } bool StackOptimizer::visit(While &_node) { @@ -358,9 +358,9 @@ bool StackOptimizer::successfullyUpdate(int index, std::vector - // DROP + // POP Si + // => + // DROP if (!ok && isPOP(op)) { int startStackSize = isPOP(op).value(); Simulator sim{instructions.begin() + index + 1, instructions.end(), startStackSize, 1}; diff --git a/compiler/libsolidity/codegen/TVM.cpp b/compiler/libsolidity/codegen/TVM.cpp index 552444a0..fe183377 100644 --- a/compiler/libsolidity/codegen/TVM.cpp +++ b/compiler/libsolidity/codegen/TVM.cpp @@ -70,14 +70,14 @@ void TVMCompilerProceedContract( const std::string& outputFolder, const std::string& filePrefix, bool doPrintFunctionIds, - bool doPrivateFunctionIds + bool doPrivateFunctionIds ) { std::string pathToFiles = getPathToFiles(solFileName, outputFolder, filePrefix); PragmaDirectiveHelper pragmaHelper{*pragmaDirectives}; if (doPrintFunctionIds) { - TVMContractCompiler::printFunctionIds(_contract, pragmaHelper); - } else if (doPrivateFunctionIds) { + TVMContractCompiler::printFunctionIds(_contract, pragmaHelper); + } else if (doPrivateFunctionIds) { TVMContractCompiler::printPrivateFunctionIds(_contract, _sourceUnits, pragmaHelper); } else { if (generateCode) { diff --git a/compiler/libsolidity/codegen/TVM.hpp b/compiler/libsolidity/codegen/TVM.hpp index 3f85c3c0..45441e73 100644 --- a/compiler/libsolidity/codegen/TVM.hpp +++ b/compiler/libsolidity/codegen/TVM.hpp @@ -27,9 +27,9 @@ class GlobalParams { public: - static solidity::langutil::ErrorReporter* g_errorReporter; - static solidity::langutil::CharStreamProvider* g_charStreamProvider; - static solidity::util::SetOnce g_tvmVersion; + static solidity::langutil::ErrorReporter* g_errorReporter; + static solidity::langutil::CharStreamProvider* g_charStreamProvider; + static solidity::util::SetOnce g_tvmVersion; }; std::string getPathToFiles( @@ -48,5 +48,5 @@ void TVMCompilerProceedContract( const std::string& outputFolder, const std::string& filePrefix, bool doPrintFunctionIds, - bool doPrivateFunctionIds + bool doPrivateFunctionIds ); diff --git a/compiler/libsolidity/codegen/TVMABI.cpp b/compiler/libsolidity/codegen/TVMABI.cpp index 4e9ed405..bb684115 100644 --- a/compiler/libsolidity/codegen/TVMABI.cpp +++ b/compiler/libsolidity/codegen/TVMABI.cpp @@ -502,6 +502,9 @@ Json::Value TVMABI::setupNameTypeComponents(const string &name, const Type *type components = obj["components"]; } } + } else if (auto userDefType = to(type)) { + Json::Value obj = setupNameTypeComponents("", &userDefType->underlyingType()); + typeName = obj["type"].asString(); } else { solUnimplemented(""); } @@ -580,6 +583,9 @@ void DecodePositionAbiV2::initTypes(Type const* type) { for (const auto &m : members) { initTypes(m->type()); } + } else if (type->category() == Type::Category::UserDefinedValueType) { + auto userDefType = to(type); + initTypes(&userDefType->underlyingType()); } else { m_types.push_back(type); } @@ -624,17 +630,18 @@ void ChainDataDecoder::decodePublicFunctionParameters( ) { if (isInternal) { DecodePositionAbiV2 position{minBits(isResponsible), 0, types}; - decodeParameters(types, position, true); + decodeParameters(types, position); } else { DecodePositionAbiV2 position{maxBits(isResponsible), 0, types}; - decodeParameters(types, position, true); + decodeParameters(types, position); } + *pusher << "ENDS"; } void ChainDataDecoder::decodeFunctionParameters(const std::vector& types, bool isResponsible) { pusher->startOpaque(); pusher->pushS(1); - pusher->push(-1, ""); // fix stack + pusher->fixStack(-1); // fix stack pusher->startContinuation(); decodePublicFunctionParameters(types, isResponsible, false); @@ -650,13 +657,13 @@ void ChainDataDecoder::decodeFunctionParameters(const std::vector& void ChainDataDecoder::decodeData(int offset, int usedRefs, const std::vector& types) { DecodePositionAbiV2 position{offset, usedRefs, types}; - decodeParameters(types, position, true); + decodeParameters(types, position); + *pusher << "ENDS"; } void ChainDataDecoder::decodeParameters( const std::vector& types, - DecodePosition& position, - const bool doDropSlice + DecodePosition& position ) { // slice are on stack solAssert(pusher->stackSize() >= 1, ""); @@ -666,9 +673,6 @@ void ChainDataDecoder::decodeParameters( decodeParameter(type, &position); pusher->ensureSize(savedStackSize + 1, "decodeParameter-2"); } - if (doDropSlice) { - pusher->push(-1, "ENDS"); // only ENDS - } if (!pusher->hasLock()) solAssert(static_cast(types.size()) <= pusher->stackSize(), ""); @@ -702,9 +706,9 @@ void ChainDataDecoder::decodeParametersQ( } void ChainDataDecoder::loadNextSlice() { - pusher->push(-1 + 2, "LDREF"); - pusher->push(-1, "ENDS"); // only ENDS - pusher->push(-1 + 1, "CTOS"); + *pusher << "LDREF"; + *pusher << "ENDS"; // only ENDS + *pusher << "CTOS"; } void ChainDataDecoder::loadNextSliceIfNeed(bool doLoadNextSlice) { @@ -733,7 +737,7 @@ void ChainDataDecoder::decodeParameter(Type const* type, DecodePosition* positio if (auto enumType = to(type)) { pusher->pushS(1); pusher->pushInt(enumType->enumDefinition().members().size()); - pusher->push(-1, "GEQ"); + *pusher << "GEQ"; pusher->_throw("THROWIF " + toString(TvmConst::RuntimeException::WrongValueOfEnum)); } } else if ( @@ -747,6 +751,8 @@ void ChainDataDecoder::decodeParameter(Type const* type, DecodePosition* positio ) { loadNextSliceIfNeed(position->loadNextCell(type)); pusher->load(type, false); + } else if (auto userDefType = to(type)) { + decodeParameter(&userDefType->underlyingType(), position); } else { solUnimplemented("Unsupported parameter type for decoding: " + type->toString()); } @@ -783,7 +789,7 @@ void ChainDataDecoder::decodeParameterQ(Type const* type, DecodePosition* positi // val slice -1 pusher->pushS(1); pusher->pushInt(enumType->enumDefinition().members().size()); - pusher->push(-1, "GEQ"); + *pusher << "GEQ"; pusher->_throw("THROWIF " + toString(TvmConst::RuntimeException::WrongValueOfEnum)); } } @@ -796,12 +802,12 @@ void ChainDataEncoder::createDefaultConstructorMsgBodyAndAppendToBuilder(const i if (bitSizeBuilder < (1023 - 32 - 1)) { pusher->stzeroes(1); - pusher->push(0, "STSLICECONST " + ss.str()); + *pusher << "STSLICECONST " + ss.str(); } else { pusher->stones(1); - pusher->push(+1, "NEWC"); - pusher->push(0, "STSLICECONST " + ss.str()); - pusher->push(-1, "STBREFR"); + *pusher << "NEWC"; + *pusher << "STSLICECONST " + ss.str(); + *pusher << "STBREFR"; } } @@ -810,7 +816,7 @@ void ChainDataEncoder::createDefaultConstructorMessage2() uint32_t funcID = calculateConstructorFunctionID(); std::stringstream ss; ss << "x" << std::hex << std::setfill('0') << std::setw(8) << funcID; - pusher->push(0, "STSLICECONST " + ss.str()); + *pusher << "STSLICECONST " + ss.str(); } uint32_t ChainDataEncoder::calculateConstructorFunctionID() { @@ -850,17 +856,17 @@ std::pair ChainDataEncoder::calculateFunctionID(const CallableDe } uint32_t ChainDataEncoder::toHash256(std::string const& str) { - 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; + 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; } uint32_t ChainDataEncoder::calculateFunctionID( @@ -944,20 +950,20 @@ uint32_t ChainDataEncoder::calculateFunctionIDWithReason( } bool isManuallyOverridden = functionId.has_value(); uint32_t funcID{}; - if (isManuallyOverridden) { - funcID = functionId.value(); - } else { - funcID = calculateFunctionID(name, inputs, outputs); - switch (reason) { - case ReasonOfOutboundMessage::FunctionReturnExternal: - funcID |= 0x80000000; - break; - case ReasonOfOutboundMessage::EmitEventExternal: - case ReasonOfOutboundMessage::RemoteCallInternal: - funcID &= 0x7FFFFFFFu; - break; - } - } + if (isManuallyOverridden) { + funcID = functionId.value(); + } else { + funcID = calculateFunctionID(name, inputs, outputs); + switch (reason) { + case ReasonOfOutboundMessage::FunctionReturnExternal: + funcID |= 0x80000000; + break; + case ReasonOfOutboundMessage::EmitEventExternal: + case ReasonOfOutboundMessage::RemoteCallInternal: + funcID &= 0x7FFFFFFFu; + break; + } + } return funcID; } @@ -985,7 +991,7 @@ void ChainDataEncoder::createMsgBodyAndAppendToBuilder( if (!doAppend) { position = std::make_unique(32 + callbackLength, 0, types); pusher->blockSwap(params.size(), 1); // msgBuilder, arg[n-1], ..., arg[1], arg[0] - pusher->push(+1, "NEWC"); // msgBuilder, arg[n-1], ..., arg[1], arg[0], builder + *pusher << "NEWC"; // msgBuilder, arg[n-1], ..., arg[1], arg[0], builder } // arg[n-1], ..., arg[1], arg[0], msgBuilder @@ -993,7 +999,7 @@ void ChainDataEncoder::createMsgBodyAndAppendToBuilder( if (!doAppend) { // msgBuilder, builder - pusher->push(-1, "STBREFR"); + *pusher << "STBREFR"; } if (!pusher->hasLock()) @@ -1015,16 +1021,16 @@ void ChainDataEncoder::createMsgBody( if (functionId.index() == 0) { std::stringstream ss; ss << "x" << std::hex << std::setfill('0') << std::setw(8) << std::get<0>(functionId); - pusher->push(0, "STSLICECONST " + ss.str()); + *pusher << "STSLICECONST " + ss.str(); } else { std::get<1>(functionId)(); - pusher->push(-1, "STUR 32"); + *pusher << "STUR 32"; } if (callbackFunctionId.has_value()) { std::stringstream ss; ss << "x" << std::hex << std::setfill('0') << std::setw(8) << callbackFunctionId.value(); - pusher->push(0, "STSLICECONST " + ss.str()); + *pusher << "STSLICECONST " + ss.str(); } encodeParameters(types, position); @@ -1055,13 +1061,13 @@ void ChainDataEncoder::encodeParameters( if (position.loadNextCell(type)) { // arg[n-1], ..., arg[1], arg[0], builder pusher->blockSwap(argQty, 1); - pusher->push(+1, "NEWC"); + *pusher << "NEWC"; } pusher->store(type, false); } } for (int idx = 0; idx < position.countOfCreatedBuilders(); idx++) { - pusher->push(-1, "STBREFR"); + *pusher << "STBREFR"; } } diff --git a/compiler/libsolidity/codegen/TVMABI.hpp b/compiler/libsolidity/codegen/TVMABI.hpp index 6de0cffa..62283ff9 100644 --- a/compiler/libsolidity/codegen/TVMABI.hpp +++ b/compiler/libsolidity/codegen/TVMABI.hpp @@ -99,8 +99,7 @@ class ChainDataDecoder : private boost::noncopyable { void decodeData(int offset, int usedRefs, const std::vector& types); void decodeParameters( const std::vector& types, - DecodePosition& position, - bool doDropSlice + DecodePosition& position ); void decodeParametersQ( const std::vector& types, diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.cpp b/compiler/libsolidity/codegen/TVMAnalyzer.cpp index d0e45f66..6ca796f2 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.cpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.cpp @@ -221,11 +221,6 @@ bool ContactsUsageScanner::visit(const FunctionDefinition &fd) { return true; } -bool ContactsUsageScanner::visit(TryStatement const& ) { - m_hasTryCatch = true; - return true; -} - bool withPrelocatedRetValues(const FunctionDefinition *f) { LocationReturn locationReturn = ::notNeedsPushContWhenInlining(f->body()); if (!f->returnParameters().empty() && isIn(locationReturn, LocationReturn::noReturn, LocationReturn::Anywhere)) { diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.hpp b/compiler/libsolidity/codegen/TVMAnalyzer.hpp index 3ffe0b8e..9314a408 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.hpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.hpp @@ -64,13 +64,11 @@ class ContactsUsageScanner: public ASTConstVisitor bool visit(FunctionCall const& _functionCall) override; bool visit(MemberAccess const &_node) override; bool visit(FunctionDefinition const& fd) override; - bool visit(TryStatement const& fd) override; bool hasMsgPubkey() const { return m_hasMsgPubkey; } bool hasMsgSender() const { return m_hasMsgSender; } bool hasResponsibleFunction() const { return m_hasResponsibleFunction; } bool hasAwaitCall() const { return m_hasAwaitCall; } - bool hasTryCatch() const { return m_hasTryCatch; } std::set const& awaitFunctions() const { return m_awaitFunctions; } private: @@ -78,7 +76,6 @@ class ContactsUsageScanner: public ASTConstVisitor bool m_hasMsgSender{}; bool m_hasResponsibleFunction{}; bool m_hasAwaitCall{}; - bool m_hasTryCatch{}; std::set m_usedFunctions; std::set m_awaitFunctions; }; @@ -103,7 +100,8 @@ static bool doesAlways(const Statement* st) { to(st) || to(st) || to(st) || - to(st) + to(st) || + to(st) ) return false; if (auto block = to(st)) { @@ -116,7 +114,7 @@ static bool doesAlways(const Statement* st) { return false; return rec(&ifStatement->trueStatement()) && rec(ifStatement->falseStatement()); } - solUnimplemented( std::string("Unsupported statement type: ") + typeid(*st).name()); + solUnimplemented(std::string("Unsupported statement type: ") + typeid(*st).name()); } class CFAnalyzer: public ASTConstVisitor @@ -136,9 +134,11 @@ class CFAnalyzer: public ASTConstVisitor bool visit(ForEachStatement const&) override; bool visit(WhileStatement const&) override; bool visit(ForStatement const&) override; + void endVisit(ForEachStatement const&) override; void endVisit(WhileStatement const&) override; void endVisit(ForStatement const&) override; + void endVisit(Return const&) override; void endVisit(Break const&) override; void endVisit(Continue const&) override; diff --git a/compiler/libsolidity/codegen/TVMCommons.cpp b/compiler/libsolidity/codegen/TVMCommons.cpp index 6f3371d4..d5701f57 100644 --- a/compiler/libsolidity/codegen/TVMCommons.cpp +++ b/compiler/libsolidity/codegen/TVMCommons.cpp @@ -166,17 +166,17 @@ int dictKeyLength(Type const *key) { return 256; // hash of tree of cells } - auto structType = to(key); + auto structType = to(key); if (structType) { - int bitLength = 0; - StructDefinition const &structDefinition = structType->structDefinition(); - for (const auto &member : structDefinition.members()) { - TypeInfo ti2{member->type()}; - solAssert(ti2.isNumeric, ""); - bitLength += ti2.numBits; - } - return bitLength; - } + int bitLength = 0; + StructDefinition const &structDefinition = structType->structDefinition(); + for (const auto &member : structDefinition.members()) { + TypeInfo ti2{member->type()}; + solAssert(ti2.isNumeric, ""); + bitLength += ti2.numBits; + } + return bitLength; + } solUnimplemented(""); } @@ -409,7 +409,11 @@ std::set getAllBaseFunctions(CallableDeclaration con } -ABITypeSize::ABITypeSize(const Type *type) { +ABITypeSize::ABITypeSize(Type const* _type) { + init(_type); +} + +void ABITypeSize::init(Type const* type) { if (isAddressOrContractType(type)){ maxBits = AddressInfo::maxBitLength(); maxRefs = 0; @@ -466,6 +470,8 @@ ABITypeSize::ABITypeSize(const Type *type) { } else if (to(type) || to(type)) { maxBits = 1023; maxRefs = 3; + } else if (auto userDefType = to(type)) { + init(&userDefType->underlyingType()); } else { solUnimplemented("Undefined type: " + type->toString()); } @@ -603,7 +609,7 @@ std::string StrUtils::tonsToBinaryString(bigint value) { s += value % 2 == 0? "0" : "1"; value /= 2; } - solAssert(len < 120, "Ton value should fit 120 bit"); + solAssert(len <= 120, "coins value must fit into 120 bit"); while (len % 8 != 0) { s += "0"; len++; @@ -667,7 +673,9 @@ std::optional ExprUtils::constValue(const Expression &_e) { if (auto ident = to(&_e)) { IdentifierAnnotation &identifierAnnotation = ident->annotation(); const auto *variable = to(identifierAnnotation.referencedDeclaration); - return constValue(*variable->value()); + if (variable) { + return constValue(*variable->value()); + } } } diff --git a/compiler/libsolidity/codegen/TVMCommons.hpp b/compiler/libsolidity/codegen/TVMCommons.hpp index 1e601836..e0de43e9 100644 --- a/compiler/libsolidity/codegen/TVMCommons.hpp +++ b/compiler/libsolidity/codegen/TVMCommons.hpp @@ -224,11 +224,11 @@ class PragmaDirectiveHelper { return {}; } - bool hasUpgradeFunc() const { + bool hasUpgradeFunc() const { return std::any_of(pragmaDirectives.begin(), pragmaDirectives.end(), [](PragmaDirective const *pd){ return pd->literals().size() == 2 && pd->literals()[0] == "upgrade" && pd->literals()[1] == "func"; }); - } + } bool hasUpgradeOldSol() const { // TODO test that it's impossible to use func and oldsol at same time @@ -246,7 +246,8 @@ struct ABITypeSize { int maxBits = -1; int maxRefs = -1; - explicit ABITypeSize(Type const* type); + explicit ABITypeSize(Type const* _type); + void init(Type const* _type); }; inline std::pair, std::vector> @@ -284,10 +285,28 @@ enum class LocationReturn { Anywhere }; -struct ControlFlowInfo { - int stackSize {-1}; - bool doAnalyzeFlag {false}; - bool isLoop {false}; +class ControlFlowInfo { +public: + ControlFlowInfo() = default; + ControlFlowInfo(int stackSize, bool hasAnalyzeFlag, bool isLoop) : m_stackSize(stackSize), + m_hasAnalyzeFlag(hasAnalyzeFlag), m_isLoop(isLoop) {} + + int stackSize() const { + return m_stackSize; + } + + bool hasAnalyzeFlag() const { + return m_hasAnalyzeFlag; + } + + bool isLoop() const { + return m_isLoop; + } + +private: + int m_stackSize {-1}; + bool m_hasAnalyzeFlag {false}; + bool m_isLoop {false}; }; LocationReturn notNeedsPushContWhenInlining(Block const& _block); diff --git a/compiler/libsolidity/codegen/TVMConstants.hpp b/compiler/libsolidity/codegen/TVMConstants.hpp index 346a9d9f..acefe532 100644 --- a/compiler/libsolidity/codegen/TVMConstants.hpp +++ b/compiler/libsolidity/codegen/TVMConstants.hpp @@ -127,6 +127,5 @@ namespace TvmConst { const int TvmTupleLen = 255; static constexpr int CONTINUE_FLAG = 1; - static constexpr int BREAK_FLAG = 2; static constexpr int RETURN_FLAG = 4; } diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.cpp b/compiler/libsolidity/codegen/TVMContractCompiler.cpp index a60ba990..9229666d 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.cpp @@ -83,8 +83,8 @@ Pointer TVMConstructorCompiler::generateConstructors() { m_pusher.ctx().addPublicFunction(functionId, "constructor"); } - m_pusher.push(+1, ""); // push encoded params of constructor - m_pusher.push(+1, ""); // functionID + m_pusher.fixStack(+1); // push encoded params of constructor + m_pusher.fixStack(+1); // functionID m_pusher.drop(); c4ToC7WithMemoryInitAndConstructorProtection(); @@ -98,7 +98,7 @@ Pointer TVMConstructorCompiler::generateConstructors() { FunctionDefinition const* constructor = linearizedBaseContracts[0]->constructor(); int take{}; if (constructor == nullptr) { - m_pusher.push(-1, "ENDS"); + m_pusher << "ENDS"; } else { take = constructor->parameters().size(); vector types = getParams(constructor->parameters()).first; @@ -141,12 +141,12 @@ Pointer TVMConstructorCompiler::generateConstructors() { pusher.clear(); pusher.takeLast(take2); TVMFunctionCompiler::generateFunctionWithModifiers(pusher, c->constructor(), false); - m_pusher.push(-take2, ""); // fix stack + m_pusher.fixStack(-take2); // fix stack m_pusher.add(pusher); } if (!haveConstructor) { - m_pusher.push(0, "ACCEPT"); + m_pusher << "ACCEPT"; } // solAssert(m_pusher.stackSize() == 0, ""); @@ -164,7 +164,7 @@ Pointer TVMConstructorCompiler::generateConstructors() { void TVMConstructorCompiler::c4ToC7WithMemoryInitAndConstructorProtection() { // copy c4 to c7 m_pusher.was_c4_to_c7_called(); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); m_pusher.pushCall(0, 0, "c4_to_c7_with_init_storage"); @@ -219,15 +219,15 @@ void TVMContractCompiler::generateCodeAndSaveToFile( ) { Pointer codeContract = generateContractCode(&contract, _sourceUnits, pragmaHelper); - ofstream ofile; - ofile.open(fileName); - if (!ofile) { + ofstream ofile; + ofile.open(fileName); + if (!ofile) { fatal_error("Failed to open the output file: " + fileName); } Printer p{ofile}; codeContract->accept(p); - ofile.close(); - cout << "Code was generated and saved to file " << fileName << endl; + ofile.close(); + cout << "Code was generated and saved to file " << fileName << endl; } Pointer @@ -245,9 +245,9 @@ TVMContractCompiler::generateContractCode( pragmas.emplace_back(std::string{} + ".version sol " + solidity::frontend::VersionNumber); } - if (pragmaHelper.hasUpgradeFunc()) { - pragmas.emplace_back(".pragma selector-func-solidity"); - } + if (pragmaHelper.hasUpgradeFunc()) { + pragmas.emplace_back(".pragma selector-func-solidity"); + } if (pragmaHelper.hasUpgradeOldSol()) { pragmas.emplace_back(".pragma selector-old-sol"); } @@ -292,7 +292,8 @@ TVMContractCompiler::generateContractCode( } else if (isMacro(_function->name())) { functions.push_back(TVMFunctionCompiler::generateMacro(ctx, _function)); } else if (_function->name() == "onCodeUpgrade") { - functions.push_back(TVMFunctionCompiler::generateOnCodeUpgrade(ctx, _function)); + if (!ctx.isBaseFunction(_function)) + functions.push_back(TVMFunctionCompiler::generateOnCodeUpgrade(ctx, _function)); } else { if (_function->isPublic()) { bool isBaseMethod = _function != getContractFunctions(contract, _function->name()).back(); @@ -398,7 +399,7 @@ TVMContractCompiler::generateContractCode( for (std::shared_ptr source: _sourceUnits) { for (ASTPointer const &node: source->nodes()) { if (auto function = dynamic_cast(node.get())) { - if (function->isFree()) { + if (function->isFree() && !function->isInlineAssembly()) { ctx.setCurrentFunction(function); if (!function->modifiers().empty()) { @@ -443,9 +444,9 @@ TVMContractCompiler::generateContractCode( functions.emplace_back(TVMFunctionCompiler::generatePublicFunctionSelector(ctx, contract)); } - if (ctx.getPragmaSaveAllFunctions()) { - pragmas.emplace_back(".pragma save-all-private-functions"); - } + if (ctx.getPragmaSaveAllFunctions()) { + pragmas.emplace_back(".pragma save-all-private-functions"); + } Pointer c = createNode(pragmas, functions); diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp index 55bd52c3..969925ff 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp @@ -93,7 +93,7 @@ bool TVMExpressionCompiler::acceptExpr(const Expression *expr) { --m_expressionDepth; solAssert(savedExpressionDepth == m_expressionDepth, - "Internal error: depth exp " + toString(savedExpressionDepth) + " got " + toString(m_expressionDepth)); + "Internal error: depth exp " + toString(savedExpressionDepth) + " got " + toString(m_expressionDepth)); return doDropResultIfNeeded; } @@ -111,20 +111,20 @@ bool TVMExpressionCompiler::isCurrentResultNeeded() const { void TVMExpressionCompiler::visitStringLiteralAbiV2(Literal const &_node) { const std::string &str = _node.value(); - m_pusher.pushString(str, false); + m_pusher.pushString(str, false); } void TVMExpressionCompiler::visit2(Literal const &_node) { const auto* type = getType(&_node); switch (_node.annotation().type->category()) { case Type::Category::Bool: - m_pusher.push(+1, _node.token() == Token::TrueLiteral? "TRUE" : "FALSE"); + m_pusher << (_node.token() == Token::TrueLiteral? "TRUE" : "FALSE"); break; case Type::Category::Null: m_pusher.pushNull(); break; case Type::Category::EmpyMap: - m_pusher.push(+1, "NEWDICT"); + m_pusher << "NEWDICT"; break; case Type::Category::StringLiteral: visitStringLiteralAbiV2(_node); @@ -177,7 +177,7 @@ void TVMExpressionCompiler::visitHonest(TupleExpression const& _tupleExpression, // values... m_pusher.pushInt(0); // values... index - m_pusher.push(+1, "NEWDICT"); + m_pusher << "NEWDICT"; // values... index dict m_pusher.pushInt(n); // values... index dict totalSize @@ -191,7 +191,7 @@ void TVMExpressionCompiler::visitHonest(TupleExpression const& _tupleExpression, // values... index dict valueI' m_pusher.pushS(2); // values... index dict valueI' index - m_pusher.push(0, "INC"); + m_pusher << "INC"; // values... index dict valueI' index++ m_pusher.exchange(3); // values... index++ dict valueI' index @@ -207,7 +207,7 @@ void TVMExpressionCompiler::visitHonest(TupleExpression const& _tupleExpression, if (onlyDict) m_pusher.dropUnder(1, 1); else - m_pusher.push(-2 + 1, "TUPLE 2"); + m_pusher << "TUPLE 2"; solAssert(stackSize + 1 == m_pusher.stackSize(), ""); } @@ -242,10 +242,10 @@ void TVMExpressionCompiler::visit2(Identifier const &_identifier) { if (pushLocalOrStateVariable(_identifier)) { } else if (name == "now") { // Getting value of `now` variable - m_pusher.push(+1, "NOW"); + m_pusher << "NOW"; } else if (name == "this") { // calling this.function() creates an internal message that should be sent to the address of current contract - m_pusher.push(+1, "MYADDR"); + m_pusher << "MYADDR"; } else if (auto funDecl = to(_identifier.annotation().referencedDeclaration)) { m_pusher.pushPrivateFunctionId(*funDecl); } else { @@ -256,7 +256,7 @@ void TVMExpressionCompiler::visit2(Identifier const &_identifier) { void TVMExpressionCompiler::compileUnaryOperation( UnaryOperation const &_node, const std::string &tvmUnaryOperation, - const bool isPrefixOperation + const bool isPrefixOperation ) { const int saveStackSize = m_pusher.stackSize(); Type const* resType = _node.annotation().type; @@ -266,14 +266,14 @@ void TVMExpressionCompiler::compileUnaryOperation( const int expandedLValueSize = m_pusher.stackSize() - saveStackSize - 1; solAssert(expandedLValueSize >= 0, ""); if (isPrefixOperation) { - m_pusher.push(0, tvmUnaryOperation); + m_pusher << tvmUnaryOperation; m_pusher.pushS(0); // expanded.. value value if (expandedLValueSize != 0) { m_pusher.blockSwap(expandedLValueSize + 1, 1); // value expanded.. value } } else { m_pusher.pushS(0); // expanded.. value value - m_pusher.push(0, tvmUnaryOperation); // expanded.. value newValue + m_pusher << tvmUnaryOperation; // expanded.. value newValue if (expandedLValueSize != 0) { m_pusher.exchange(1); // expanded.. newValue value m_pusher.blockSwap(expandedLValueSize + 1, 1); // value expanded.. newValue @@ -281,7 +281,7 @@ void TVMExpressionCompiler::compileUnaryOperation( } } else { lValueInfo = expandLValue(&_node.subExpression(), true); - m_pusher.push(0, tvmUnaryOperation); + m_pusher << tvmUnaryOperation; } if (!isCheckFitUseless(resType, _node.getOperator()) && !m_pusher.ctx().ignoreIntegerOverflow()) { @@ -312,10 +312,10 @@ void TVMExpressionCompiler::compileUnaryDelete(UnaryOperation const &node) { collectLValue(lValueInfo, true, false); } else { // mapping m_pusher.pushS(1); // ... index dict index - Type const* dictKey = StackPusher::parseIndexType(indexAccess->baseExpression().annotation().type); + Type const* dictKey = StackPusher::parseIndexType(indexAccess->baseExpression().annotation().type); m_pusher.exchange(1); // ... index index' dict m_pusher.pushInt(dictKeyLength(dictKey)); // ..index index dict nbits - m_pusher.push(-3 + 2, "DICT" + typeToDictChar(dictKey) + "DEL"); // ... index dict' {-1,0} + m_pusher << "DICT" + typeToDictChar(dictKey) + "DEL"; // ... index dict' {-1,0} m_pusher.drop(); // ... index dict' collectLValue(lValueInfo, false, false); // lValueInfo.isValueBuilder is ignored } @@ -332,10 +332,10 @@ void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { compileUnaryOperation(_node, "DEC", _node.isPrefixOperation()); } else if (op == Token::Not) { compileNewExpr(&_node.subExpression()); - m_pusher.push(0, "NOT"); + m_pusher << "NOT"; } else if (op == Token::Sub) { compileNewExpr(&_node.subExpression()); - m_pusher.push(0, "NEGATE"); + m_pusher << "NEGATE"; } else if (op == Token::BitNot) { compileNewExpr(&_node.subExpression()); int numBits = 0; @@ -349,10 +349,10 @@ void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { cast_error(_node, "~ operation is supported only for numbers."); } if (isSigned) - m_pusher.push(0, "BITNOT"); + m_pusher << "BITNOT"; else { - m_pusher.push(+1,"PUSHPOW2DEC " + to_string(numBits)); - m_pusher.push(-2+1, "SUBR"); + m_pusher << "PUSHPOW2DEC " + to_string(numBits); + m_pusher << "SUBR"; } } else if (op == Token::Delete) { compileUnaryDelete(_node); @@ -364,27 +364,27 @@ void TVMExpressionCompiler::visit2(UnaryOperation const &_node) { void TVMExpressionCompiler::compareSlices(Token op) { switch(op) { case Token::GreaterThan: - m_pusher.push(-2 + 1, "SDLEXCMP"); - m_pusher.push(0, "ISPOS"); + m_pusher << "SDLEXCMP"; + m_pusher << "ISPOS"; break; case Token::GreaterThanOrEqual: - m_pusher.push(-2 + 1, "SDLEXCMP"); - m_pusher.push(0, "ISNNEG"); + m_pusher << "SDLEXCMP"; + m_pusher << "ISNNEG"; break; case Token::LessThan: - m_pusher.push(-2 + 1, "SDLEXCMP"); - m_pusher.push(0, "ISNEG"); + m_pusher << "SDLEXCMP"; + m_pusher << "ISNEG"; break; case Token::LessThanOrEqual: - m_pusher.push(-2 + 1, "SDLEXCMP"); - m_pusher.push(0, "ISNPOS"); + m_pusher << "SDLEXCMP"; + m_pusher << "ISNPOS"; break; case Token::Equal: - m_pusher.push(-2 + 1, "SDEQ"); + m_pusher << "SDEQ"; break; case Token::NotEqual: - m_pusher.push(-2 + 1, "SDEQ"); - m_pusher.push(0, "NOT"); + m_pusher << "SDEQ"; + m_pusher << "NOT"; break; default: solUnimplemented("Wrong compare operation"); @@ -395,23 +395,23 @@ void TVMExpressionCompiler::compareStrings(Token op) { m_pusher.pushMacroCallInCallRef(2, 1, "compareLongStrings_macro"); switch(op) { case Token::GreaterThan: - m_pusher.push(0, "ISPOS"); + m_pusher << "ISPOS"; break; case Token::GreaterThanOrEqual: - m_pusher.push(0, "ISNNEG"); + m_pusher << "ISNNEG"; break; case Token::LessThan: - m_pusher.push(0, "ISNEG"); + m_pusher << "ISNEG"; break; case Token::LessThanOrEqual: - m_pusher.push(0, "ISNPOS"); + m_pusher << "ISNPOS"; break; case Token::Equal: - m_pusher.push(0, "ISZERO"); + m_pusher << "ISZERO"; break; case Token::NotEqual: - m_pusher.push(0, "ISZERO"); - m_pusher.push(0, "NOT"); + m_pusher << "ISZERO"; + m_pusher << "NOT"; break; default: solUnimplemented("Wrong compare operation"); @@ -464,13 +464,13 @@ void TVMExpressionCompiler::visitBinaryOperationForTvmCell( ) { if (op == Token::Equal || op == Token::NotEqual) { pushLeft(); - m_pusher.push(-1 + 1, "HASHCU"); + m_pusher << "HASHCU"; pushRight(); - m_pusher.push(-1 + 1, "HASHCU"); + m_pusher << "HASHCU"; if (op == Token::Equal) - m_pusher.push(-2 + 1, "EQUAL"); + m_pusher << "EQUAL"; else - m_pusher.push(-2 + 1, "NEQ"); + m_pusher << "NEQ"; } else { solUnimplemented("Unsupported binary operation"); } @@ -483,7 +483,7 @@ void TVMExpressionCompiler::visitLogicalShortCircuiting(BinaryOperation const &_ compileNewExpr(order[0]); for (int i = 1; i < static_cast(order.size()); ++i) { m_pusher.pushS(0); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); m_pusher.drop(); compileNewExpr(order[i]); @@ -576,24 +576,24 @@ void TVMExpressionCompiler::visitMathBinaryOperation( if (rightValue.has_value() && (*rightValue == 2 || *rightValue == 3 || *rightValue == 4)) { if (*rightValue == 2) { m_pusher.pushS(0); - m_pusher.push(-2 + 1, "MUL"); + m_pusher << "MUL"; } else if (*rightValue == 3) { m_pusher.pushS(0); m_pusher.pushS(0); - m_pusher.push(-2 + 1, "MUL"); - m_pusher.push(-2 + 1, "MUL"); + m_pusher << "MUL"; + m_pusher << "MUL"; } else if (*rightValue == 4) { m_pusher.pushS(0); - m_pusher.push(-2 + 1, "MUL"); + m_pusher << "MUL"; m_pusher.pushS(0); - m_pusher.push(-2 + 1, "MUL"); + m_pusher << "MUL"; } else { solUnimplemented(""); } } else { pushRight(); m_pusher.dup2(); - m_pusher.push(-2 + 1, "OR"); + m_pusher << "OR"; m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::Exponent00)); m_pusher.pushMacroCallInCallRef(2, 1, "__exp_macro"); } @@ -603,46 +603,46 @@ void TVMExpressionCompiler::visitMathBinaryOperation( pushRight(); } if (op == Token::Add) { - m_pusher.push(-1, "ADD"); + m_pusher << "ADD"; checkOverflow = true; } else if (op == Token::Mul) { if (commonType->category() == Type::Category::FixedPoint) { int power = to(commonType)->fractionalDigits(); m_pusher.pushInt(StackPusher::pow10(power)); - m_pusher.push(-3 + 1, "MULDIV"); + m_pusher << "MULDIV"; } else { - m_pusher.push(-2 + 1, "MUL"); + m_pusher << "MUL"; } checkOverflow = true; } else if (op == Token::Sub) { - m_pusher.push(-1, "SUB"); + m_pusher << "SUB"; checkOverflow = true; } - else if (op == Token::Mod) { m_pusher.push(-1, "MOD"); } + else if (op == Token::Mod) { m_pusher << "MOD"; } 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.exchange(1); - m_pusher.push(-3 + 1, "MULDIV"); + m_pusher << "MULDIV"; } else { - m_pusher.push(-1, "DIV"); + m_pusher << "DIV"; } } - else if (op == Token::GreaterThan) m_pusher.push(-1, "GREATER"); - else if (op == Token::GreaterThanOrEqual) m_pusher.push(-1, "GEQ"); - else if (op == Token::LessThan) m_pusher.push(-1, "LESS"); - else if (op == Token::LessThanOrEqual) m_pusher.push(-1, "LEQ"); - else if (op == Token::Equal) m_pusher.push(-1, "EQUAL"); - else if (op == Token::NotEqual) m_pusher.push(-1, "NEQ"); - else if (op == Token::BitAnd) m_pusher.push(-1, "AND"); - else if (op == Token::BitOr) m_pusher.push(-1, "OR"); + else if (op == Token::GreaterThan) m_pusher << "GREATER"; + else if (op == Token::GreaterThanOrEqual) m_pusher << "GEQ"; + else if (op == Token::LessThan) m_pusher << "LESS"; + else if (op == Token::LessThanOrEqual) m_pusher << "LEQ"; + else if (op == Token::Equal) m_pusher << "EQUAL"; + else if (op == Token::NotEqual) m_pusher << "NEQ"; + else if (op == Token::BitAnd) m_pusher << "AND"; + else if (op == Token::BitOr) m_pusher << "OR"; else if (op == Token::SHL) { - m_pusher.push(-1, "LSHIFT"); + m_pusher << "LSHIFT"; checkOverflow = true; } - else if (op == Token::SAR) m_pusher.push(-1, "RSHIFT"); - else if (op == Token::BitXor) m_pusher.push(-1, "XOR"); + else if (op == Token::SAR) m_pusher << "RSHIFT"; + else if (op == Token::BitXor) m_pusher << "XOR"; else { solUnimplemented("Unsupported binary operation"); } @@ -682,11 +682,11 @@ void TVMExpressionCompiler::visitMsgMagic(MemberAccess const &_node) { "PICK", }, 0, 1, true)); if (_node.memberName() == "isInternal") { - m_pusher.push(-1 + 1, "EQINT 0"); + m_pusher << "EQINT 0"; } else if (_node.memberName() == "isExternal") { - m_pusher.push(-1 + 1, "EQINT -1"); + m_pusher << "EQINT -1"; } else if (_node.memberName() == "isTickTock") { - m_pusher.push(-1 + 1, "EQINT -2"); + m_pusher << "EQINT -2"; } else { solUnimplemented(""); } @@ -792,34 +792,59 @@ void TVMExpressionCompiler::visitMsgMagic(MemberAccess const &_node) { } } -void TVMExpressionCompiler::visitMagic(MemberAccess const &_node) { - auto unsupportedMagic = [&](){ - cast_error(_node, "Unsupported magic"); +void TVMExpressionCompiler::visitMagic(MemberAccess const &_memberAccess) { + auto unsupportedMagic = [&]() { + cast_error(_memberAccess, "Unsupported magic"); }; - auto identifier = to(&_node.expression()); - if (identifier == nullptr) { - unsupportedMagic(); - } - if (identifier->name() == "msg") { - visitMsgMagic(_node); - } else if (identifier->name() == "tx") { - if (_node.memberName() == "timestamp") { + Type const* exprType = _memberAccess.expression().annotation().type; + auto magicType = dynamic_cast(exprType); + ASTString const& member = _memberAccess.memberName(); + + switch (magicType->kind()) { + case MagicType::Kind::Message: + visitMsgMagic(_memberAccess); + break; + case MagicType::Kind::Transaction: { + if (isIn(member, "timestamp", "logicaltime")) { m_pusher << "LTIME"; - } else if (_node.memberName() == "storageFee") { + } else if (member == "storageFee") { m_pusher << "STORAGEFEE"; } else { unsupportedMagic(); } - } else if (identifier->name() == "block") { - if (_node.memberName() == "timestamp") { + break; + } + case MagicType::Kind::Block: { + if (member == "timestamp") { m_pusher << "NOW"; - } else if (_node.memberName() == "logicaltime") { + } else if (member == "logicaltime") { m_pusher << "BLOCKLT"; } else { unsupportedMagic(); } - } else { + break; + } + case MagicType::Kind::MetaType: { + if (member == "min" || member == "max") { + auto const* arg = dynamic_cast(_memberAccess.expression().annotation().type); + string opcode = "PUSHINT "; + 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* enumType = dynamic_cast(argType)) + opcode += toString(member == "min" ? enumType->minValue() : enumType->maxValue()); + else + solAssert(false, "min/max not available for the given type."); + m_pusher << opcode; + } else { + unsupportedMagic(); + } + break; + } + default: unsupportedMagic(); } } @@ -855,19 +880,19 @@ void TVMExpressionCompiler::visit2(MemberAccess const &_node) { auto typeType = to(_node.expression().annotation().type); if (auto enumType = dynamic_cast(typeType->actualType())) { unsigned int value = enumType->memberValue(_node.memberName()); - m_pusher.push(+1, "PUSHINT " + toString(value)); + m_pusher << "PUSHINT " + toString(value); return; } - auto conType = to(typeType->actualType()); - if (conType->contractDefinition().isLibrary()) { - auto funType = to(_node.annotation().type); - if (funType) { - auto funDef = to(&funType->declaration()); - m_pusher.pushPrivateFunctionId(*funDef); - return ; - } - } + auto conType = to(typeType->actualType()); + if (conType->contractDefinition().isLibrary()) { + auto funType = to(_node.annotation().type); + if (funType) { + auto funDef = to(&funType->declaration()); + m_pusher.pushPrivateFunctionId(*funDef); + return ; + } + } if (fold_constants(&_node)) { return; @@ -888,7 +913,7 @@ bool TVMExpressionCompiler::checkForAddressMemberAccess(MemberAccess const &_nod if (!isAddressThis(to(&_node.expression()))) { cast_error(_node.expression(), "Only 'address(this).balance' is supported for member balance"); } - m_pusher.push(+1, "GETPARAM 7"); + m_pusher << "GETPARAM 7"; m_pusher.indexNoexcep(0); return true; } @@ -896,21 +921,21 @@ bool TVMExpressionCompiler::checkForAddressMemberAccess(MemberAccess const &_nod if (!isAddressThis(to(&_node.expression()))) { cast_error(_node.expression(), "Only 'address(this).currencies' is supported for member currencies"); } - m_pusher.push(+1, "GETPARAM 7"); + m_pusher << "GETPARAM 7"; m_pusher.indexNoexcep(1); return true; } if (_node.memberName() == "wid") { compileNewExpr(&_node.expression()); - m_pusher.push(-1 + 1, "PARSEMSGADDR"); + m_pusher << "PARSEMSGADDR"; m_pusher.indexWithExcep(2); return true; } if (_node.memberName() == "value") { compileNewExpr(&_node.expression()); - m_pusher.push(-1 + 1, "PARSEMSGADDR"); + m_pusher << "PARSEMSGADDR"; m_pusher.indexWithExcep(3); - m_pusher.push(0, "PLDU 256"); + m_pusher << "PLDU 256"; return true; } return false; @@ -960,10 +985,10 @@ void TVMExpressionCompiler::visit2(IndexRangeAccess const &indexRangeAccess) { } if (indexRangeAccess.endExpression()) { compileNewExpr(indexRangeAccess.endExpression()); - m_pusher.push(+1, "FALSE"); + m_pusher << "FALSE"; } else { m_pusher.pushInt(0); - m_pusher.push(+1, "TRUE"); + m_pusher << "TRUE"; } m_pusher.pushMacroCallInCallRef(4, 1, "bytes_substr_macro"); @@ -979,23 +1004,23 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { auto baseArrayType = to(baseType); if (baseArrayType->isByteArrayOrString()) { acceptExpr(&indexAccess.baseExpression()); // bytes - m_pusher.push(-1 + 1, "CTOS"); + m_pusher << "CTOS"; compileNewExpr(indexAccess.indexExpression()); // slice index m_pusher.startContinuation(); m_pusher.pushInt(127); - m_pusher.push(-2 + 2, "DIVMOD"); // slice cntRef rest + m_pusher << "DIVMOD"; // slice cntRef rest m_pusher.rotRev(); // rest slice cntRef m_pusher.startContinuation(); - m_pusher.push(0, "PLDREF"); - m_pusher.push(0, "CTOS"); + m_pusher << "PLDREF"; + m_pusher << "CTOS"; m_pusher.endContinuation(); m_pusher.repeat(false); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack // rest slice m_pusher.exchange(1); // slice rest - m_pusher.push(-1 + 1, "MULCONST 8"); - m_pusher.push(-2 + 1, "SDSKIPFIRST"); - m_pusher.push(-1 + 1, "PLDU 8"); + m_pusher << "MULCONST 8"; + m_pusher << "SDSKIPFIRST"; + m_pusher << "PLDU 8"; m_pusher.pushRefContAndCallX(2, 1, false); return; } else { @@ -1012,14 +1037,14 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { } else { acceptExpr(indexAccess.indexExpression()); // vector index m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher.push(-2+2, "DIVMOD"); + m_pusher << "DIVMOD"; // vector tuple_num rest m_pusher.rotRev(); // rest vector tuple_num - m_pusher.push(-2 + 1, "INDEXVAR"); + m_pusher << "INDEXVAR"; // rest tuple m_pusher.exchange(1); - m_pusher.push(-2 + 1, "INDEXVAR"); + m_pusher << "INDEXVAR"; } return; } else if (baseType->category() == Type::Category::FixedBytes) { @@ -1027,10 +1052,10 @@ void TVMExpressionCompiler::visit2(IndexAccess const &indexAccess) { auto fbt = to(getType(&indexAccess.baseExpression())); m_pusher.pushInt(static_cast(fbt->storageBytes()) - 1); acceptExpr(indexAccess.indexExpression()); // integer byte_len byte_index - m_pusher.push(-1, "SUB"); - m_pusher.push(0, "MULCONST 8"); - m_pusher.push(-1, "RSHIFT"); - m_pusher.push(0, "MODPOW2 8"); + m_pusher << "SUB"; + m_pusher << "MULCONST 8"; + m_pusher << "RSHIFT"; + m_pusher << "MODPOW2 8"; return; } else { pushIndexAndConvert(indexAccess); // index @@ -1056,17 +1081,17 @@ void TVMExpressionCompiler::visit2(Conditional const &_conditional) { const int paramQty = returnParamQty(_conditional.trueExpression()); compileNewExpr(&_conditional.condition()); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); compileNewExpr(&_conditional.trueExpression()); m_pusher.endContinuation(); - m_pusher.push(-paramQty, ""); // fix stack + m_pusher.fixStack(-paramQty); // fix stack m_pusher.startContinuation(); compileNewExpr(&_conditional.falseExpression()); m_pusher.endContinuation(); - m_pusher.push(-paramQty, ""); // fix stack + m_pusher.fixStack(-paramQty); // fix stack m_pusher.pushConditional(paramQty); @@ -1134,7 +1159,7 @@ TVMExpressionCompiler::expandLValue( if (isIn(index->baseExpression().annotation().type->category(), Type::Category::Mapping, Type::Category::ExtraCurrencyCollection)) { // dict1 pushIndexAndConvert(*index); // dict1 index - m_pusher.prepareKeyForDictOperations(index->indexExpression()->annotation().type, false); + m_pusher.prepareKeyForDictOperations(index->indexExpression()->annotation().type, false); // dict1 index m_pusher.exchange(1); // index dict1 @@ -1143,23 +1168,23 @@ TVMExpressionCompiler::expandLValue( // index dict1 index dict1 m_pusher.getDict(*StackPusher::parseIndexType(index->baseExpression().annotation().type), - *StackPusher::parseValueType(*index), - GetDictOperation::GetFromMapping); + *StackPusher::parseValueType(*index), + GetDictOperation::GetFromMapping); // index dict1 dict2 } else if (index->baseExpression().annotation().type->category() == Type::Category::Array) { // array - m_pusher.push(-1 + 2, "UNTUPLE 2"); // size dict + m_pusher << "UNTUPLE 2"; // size dict compileNewExpr(index->indexExpression()); // size dict index m_pusher.exchange(1); // size index dict m_pusher.pushS2(1, 2); // size index dict index size - m_pusher.push(-2 + 1, "LESS"); // size index dict indexbaseExpression().annotation().type), - *index->annotation().type, GetDictOperation::GetFromArray); + *index->annotation().type, GetDictOperation::GetFromArray); // size index dict value } else { solUnimplemented(""); @@ -1239,7 +1264,7 @@ TVMExpressionCompiler::collectLValue( m_pusher.setDict(*keyType, *valueDictType, dataType); // size dict' } - m_pusher.push(-2 + 1, "TUPLE 2"); + m_pusher << "TUPLE 2"; } else { solUnimplemented(""); } @@ -1351,7 +1376,7 @@ void TVMExpressionCompiler::visit2(Assignment const &_assignment) { cast_error(_assignment, "Unsupported operation."); } if (tryAssignTuple(_assignment) || - tryAssignLValue(_assignment)) { + tryAssignLValue(_assignment)) { return; } cast_error(_assignment, "Unsupported assignment."); @@ -1361,7 +1386,7 @@ void TVMExpressionCompiler::visit2(Assignment const &_assignment) { bool TVMExpressionCompiler::fold_constants(const Expression *expr) { const auto& val = ExprUtils::constValue(*expr); if (val.has_value()) { - m_pusher.push(+1, "PUSHINT " + val.value().str()); + m_pusher << "PUSHINT " + val.value().str(); return true; } diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.cpp b/compiler/libsolidity/codegen/TVMFunctionCall.cpp index f390732b..bd769541 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.cpp @@ -61,20 +61,20 @@ void FunctionCallCompiler::structConstructorCall() { } void FunctionCallCompiler::compile() { - auto ma = to(&m_functionCall.expression()); + auto _memberAccess = to(&m_functionCall.expression()); auto reportError = [&](){ cast_error(m_functionCall, "Unsupported function call"); }; if (checkRemoteMethodCall(m_functionCall) || - (ma != nullptr && libraryCall(*ma)) || + (_memberAccess != nullptr && libraryCall(*_memberAccess)) || checkForMappingOrCurrenciesMethods() || checkNewExpression() || checkAddressThis() || checkSolidityUnits() || checkLocalFunctionOrLibCallOrFuncVarCall()) { // do nothing - } else if (ma != nullptr && getType(&ma->expression())->category() == Type::Category::Struct) { + } else if (_memberAccess != nullptr && getType(&_memberAccess->expression())->category() == Type::Category::Struct) { if (!structMethodCall()) { reportError(); } @@ -83,18 +83,18 @@ void FunctionCallCompiler::compile() { } else if (*m_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { typeConversion(); } else { - if (ma != nullptr) { - auto category = getType(&ma->expression())->category(); - auto ident = to(&ma->expression()); + if (_memberAccess != nullptr) { + auto category = getType(&_memberAccess->expression())->category(); + auto ident = to(&_memberAccess->expression()); if (category == Type::Category::Array) { - arrayMethods(*ma); + arrayMethods(*_memberAccess); } else if (category == Type::Category::TvmSlice) { - sliceMethods(*ma); + sliceMethods(*_memberAccess); } else if (category == Type::Category::TvmBuilder) { - builderMethods(*ma); + builderMethods(*_memberAccess); } else if ( - checkForTvmVectorMethods(*ma, category) || - checkForOptionalMethods(*ma)) + checkForTvmVectorMethods(*_memberAccess, category) || + checkForOptionalMethods(*_memberAccess)) { // nothing } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "tvm") { @@ -105,38 +105,43 @@ void FunctionCallCompiler::compile() { } else if (m_funcType->kind() == FunctionType::Kind::TVMBuildExtMsg) { tvmBuildMsgMethod(); } else if ( - checkForTvmSendFunction(*ma) || - checkForTvmConfigParamFunction(*ma) || - checkForTvmFunction(*ma) || - checkForTvmDeployMethods(*ma, category) + checkForTvmSendFunction(*_memberAccess) || + checkForTvmConfigParamFunction(*_memberAccess) || + checkForTvmFunction(*_memberAccess) || + checkForTvmDeployMethods(*_memberAccess, category) ) { // do nothing } else { reportError(); } } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "rnd") { - rndFunction(*ma); + rndFunction(*_memberAccess); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "gosh") { goshFunction(); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "msg") { - msgFunction(*ma); + msgFunction(*_memberAccess); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "abi") { abiFunction(); } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "math") { - mathFunction(*ma); + mathFunction(*_memberAccess); } else if (category == Type::Category::Address) { addressMethod(); } else if (category == Type::Category::TvmCell) { - cellMethods(*ma); - } else if (category == Type::Category::Variant) { - variantMethods(*ma); - } else if (isSuper(&ma->expression())) { - superFunctionCall(*ma); + cellMethods(*_memberAccess); + } else if (category == Type::Category::Variant) { + variantMethods(*_memberAccess); + } else if (isSuper(&_memberAccess->expression())) { + superFunctionCall(*_memberAccess); } else if (category == Type::Category::TypeType) { - if (checkBaseContractCall(*ma, category)) { + Type const* actualType = to(_memberAccess->expression().annotation().type)->actualType(); + if (checkBaseContractCall(*_memberAccess, category)) { // nothing + } else if (to(actualType)) { + userDefinedValueMethods(*_memberAccess); + } else if (to(actualType)) { + addressMethods(*_memberAccess); } else { - typeTypeMethods(*ma); + reportError(); } } else { reportError(); @@ -150,12 +155,12 @@ void FunctionCallCompiler::compile() { void FunctionCallCompiler::arrayPush(StackPusher& pusher, Type const* arrayBaseType, DataType dataType) { // arr value pusher.exchange(1); // value' arr - pusher.push(-1 + 2, "UNTUPLE 2"); // value' size dict + pusher << "UNTUPLE 2"; // value' size dict pusher.pushS(1); // value' size dict size - pusher.push(0, "INC"); // value' size dict newSize + pusher << "INC"; // value' size dict newSize pusher.blockSwap(3, 1); // newSize value' size dict pusher.setDict(getArrayKeyType(), *arrayBaseType, dataType); // newSize dict' - pusher.push(-2 + 1, "TUPLE 2"); // arr + pusher << "TUPLE 2"; // arr } bool FunctionCallCompiler::checkForMappingOrCurrenciesMethods() { @@ -308,7 +313,7 @@ void FunctionCallCompiler::mappingKeysOrValues(bool areKeys) { m_pusher.pushS(0); m_pusher << "ISNULL"; m_pusher << "NOT"; - m_pusher.push(-1, ""); + m_pusher.fixStack(-1); m_pusher.endContinuation(); m_pusher.startContinuation(); @@ -355,7 +360,7 @@ void FunctionCallCompiler::mappingEmpty() { auto expr = &m_functionCall.expression(); auto ma = to(expr); acceptExpr(&ma->expression()); - m_pusher.push(0, "DICTEMPTY"); + m_pusher << "DICTEMPTY"; } void FunctionCallCompiler::superFunctionCall(MemberAccess const &_node) { @@ -371,7 +376,15 @@ void FunctionCallCompiler::superFunctionCall(MemberAccess const &_node) { m_pusher.pushCallOrCallRef(functionName, m_funcType); } -void FunctionCallCompiler::typeTypeMethods(MemberAccess const &_node) { +void FunctionCallCompiler::userDefinedValueMethods(MemberAccess const &_memberAccess) { + if (isIn(_memberAccess.memberName(), "wrap", "unwrap")) { + pushArgs(); + } else { + solUnimplemented(""); + } +} + +void FunctionCallCompiler::addressMethods(MemberAccess const &_node) { if (_node.memberName() == "makeAddrExtern") { // addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; const auto& num = ExprUtils::constValue(*m_arguments.at(0)); @@ -384,13 +397,13 @@ void FunctionCallCompiler::typeTypeMethods(MemberAccess const &_node) { } else { pushArgs(); m_pusher.pushS(0); // numb cntBit cntBit - m_pusher.push(+1, "NEWC"); // numb cntBit cntBit builder - m_pusher.push(-1 + 1, "STSLICECONST x6_"); - m_pusher.push(-1, "STU 9"); // numb cntBit builder'' + m_pusher << "NEWC"; // numb cntBit cntBit builder + m_pusher << "STSLICECONST x6_"; + m_pusher << "STU 9"; // numb cntBit builder'' m_pusher.exchange(1); // numb builder'' cntBit - m_pusher.push(-3 + 1, "STUX"); // builder''' - m_pusher.push(0, "ENDC"); - m_pusher.push(0, "CTOS"); // extAddress + m_pusher << "STUX"; // builder''' + m_pusher << "ENDC"; + m_pusher << "CTOS"; // extAddress } } else if (_node.memberName() == "makeAddrNone") { m_pusher.pushSlice("x2_"); @@ -404,12 +417,12 @@ void FunctionCallCompiler::typeTypeMethods(MemberAccess const &_node) { m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(addr)); } else { pushArgs(true); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1 + 1, "STSLICECONST x9_"); - m_pusher.push(-1, "STI 8"); - m_pusher.push(-1, "STU 256"); - m_pusher.push(-1 + 1, "ENDC"); - m_pusher.push(-1 + 1, "CTOS"); + m_pusher << "NEWC"; + m_pusher << "STSLICECONST x9_"; + m_pusher << "STI 8"; + m_pusher << "STU 256"; + m_pusher << "ENDC"; + m_pusher << "CTOS"; } } else { solUnimplemented(""); @@ -460,7 +473,7 @@ std::function FunctionCallCompiler::generateDataSection( ) { return [pushKey, this, vars, ct]() { // creat dict with variable values - m_pusher.push(+1, "NEWDICT"); + m_pusher << "NEWDICT"; // stake: builder dict IntegerType keyType = getKeyTypeOfC4(); @@ -499,9 +512,9 @@ std::function FunctionCallCompiler::generateDataSection( // stack: dict' } } - m_pusher.push(+1, "NEWC"); - m_pusher.push(-2 + 1, "STDICT"); - m_pusher.push(-1 + 1, "ENDC"); + m_pusher << "NEWC"; + m_pusher << "STDICT"; + m_pusher << "ENDC"; }; } @@ -659,14 +672,14 @@ bool FunctionCallCompiler::checkRemoteMethodCall(FunctionCall const &_functionCa "SWAP", }, 0, 0, false)); m_pusher.getGlob(TvmConst::C7::AwaitAnswerId); - m_pusher.push(-2 + 1, "EQUAL"); + m_pusher << "EQUAL"; m_pusher._throw("THROWIFNOT " + to_string(TvmConst::RuntimeException::WrongAwaitFuncId)); - m_pusher.push(+2, ""); // fix stack + m_pusher.fixStack(+2); // fix stack // decode returned vars ChainDataDecoder decoder{&m_pusher}; vector types = getParams(functionDefinition->returnParameters()).first; decoder.decodePublicFunctionParameters(types, false, true); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.pushRefContAndCallX(0, 0, false); m_pusher.endOpaque(1, types.size()); // take one parameter - it's dest address } @@ -783,7 +796,7 @@ void FunctionCallCompiler::generateExtInboundMsg( acceptExpr(arg.get()); } - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; builderSize += TvmConst::Abi::MaxOptionalSignLength; if (addSignature) { m_pusher.stones(1); @@ -807,8 +820,8 @@ void FunctionCallCompiler::generateExtInboundMsg( if (addSignature) { m_pusher.pushS(0); m_pusher.startOpaque(); - m_pusher.push(0, "ISNULL"); - m_pusher.push(-1, ""); + m_pusher << "ISNULL"; + m_pusher.fixStack(-1); m_pusher.startContinuation(); m_pusher.drop(1); @@ -818,14 +831,14 @@ void FunctionCallCompiler::generateExtInboundMsg( m_pusher.startContinuation(); m_pusher.exchange(1); m_pusher.stones(1); - m_pusher.push(+1, ""); // fix stack - m_pusher.push(-1, "STU 256"); + m_pusher.fixStack(+1); // fix stack + m_pusher << "STU 256"; m_pusher.endContinuation(); m_pusher.ifElse(); m_pusher.endOpaque(3, 1); } else { - m_pusher.push(-1 + 1, "ISNULL"); + m_pusher << "ISNULL"; m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::MsgWithKeyButNoSign)); m_pusher.stzeroes(1); } @@ -835,13 +848,13 @@ void FunctionCallCompiler::generateExtInboundMsg( if (time != nullptr) { builderSize += 64; acceptExpr(time); - m_pusher.push(-1, "STUR 64"); + m_pusher << "STUR 64"; } if (expire != nullptr) { builderSize += 32; acceptExpr(expire); - m_pusher.push(-1, "STUR 32"); + m_pusher << "STUR 32"; } // function call body @@ -870,7 +883,7 @@ void FunctionCallCompiler::generateExtInboundMsg( ChainDataEncoder{&m_pusher}.createMsgBody(params, functionId, callbackFunctionId, position); - m_pusher.push(-1, "STBREFR"); + m_pusher << "STBREFR"; }; // store dest address @@ -890,63 +903,63 @@ void FunctionCallCompiler::generateExtInboundMsg( acceptExprOrPushFunctionId(abiVer); acceptExprOrPushFunctionId(onerrorid); acceptExprOrPushFunctionId(callbackid); - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; // stack: flags [signBoxHandle] abiVer onerrorid callbackid builder - m_pusher.push(0, "STSLICECONST x6_"); // header 01 + m_pusher << "STSLICECONST x6_"; // header 01 if (signBoxHandle == nullptr) - m_pusher.push(0, "STSLICECONST x2A4_"); // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) + m_pusher << "STSLICECONST x2A4_"; // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) else { m_pusher.pushS(4); m_pusher.startOpaque(); - m_pusher.push(0, "ISNULL"); + m_pusher << "ISNULL"; m_pusher.startContinuation(); - m_pusher.push(0, "STSLICECONST x2A4_"); // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) + m_pusher << "STSLICECONST x2A4_"; // const length 84 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 1 opt signBox + 8 flags) m_pusher.endContinuation(); m_pusher.startContinuation(); - m_pusher.push(0, "STSLICECONST x3A4_"); // const length 116 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 33 opt signBox + 8 flags) + m_pusher << "STSLICECONST x3A4_"; // const length 116 (32 callback + 32 onerror + 8 abiVer + 3 header mask + 33 opt signBox + 8 flags) m_pusher.endContinuation(); m_pusher.ifElse(); - m_pusher.push(-1, ""); + m_pusher.fixStack(-1); m_pusher.endOpaque(2, 1); } // stack: flags [signBoxHandle] abiVer onerrorid callbackid builder - m_pusher.push(-1, "STU 32"); // stack: flags [signBoxHandle] abiVer onerrorid builder - m_pusher.push(-1, "STU 32"); // stack: flags [signBoxHandle] abiVer builder - m_pusher.push(-1, "STU 8"); // stack: flags [signBoxHandle] builder + m_pusher << "STU 32"; // stack: flags [signBoxHandle] abiVer onerrorid builder + m_pusher << "STU 32"; // stack: flags [signBoxHandle] abiVer builder + m_pusher << "STU 8"; // stack: flags [signBoxHandle] builder if (time != nullptr) - m_pusher.push(0, "STONE"); + m_pusher << "STONE"; else - m_pusher.push(0, "STZERO"); + m_pusher << "STZERO"; if (expire != nullptr) - m_pusher.push(0, "STONE"); + m_pusher << "STONE"; else - m_pusher.push(0, "STZERO"); + m_pusher << "STZERO"; if (pubkey != nullptr) - m_pusher.push(0, "STONE"); + m_pusher << "STONE"; else - m_pusher.push(0, "STZERO"); + m_pusher << "STZERO"; if (signBoxHandle == nullptr) - m_pusher.push(0, "STZERO"); + m_pusher << "STZERO"; else { // stack: flags [signBoxHandle] builder m_pusher.pushS(1); m_pusher.startOpaque(); - m_pusher.push(0, "ISNULL"); + m_pusher << "ISNULL"; m_pusher.startContinuation(); m_pusher.dropUnder(1, 1); m_pusher.stzeroes(1); m_pusher.endContinuation(); m_pusher.startContinuation(); m_pusher.stones(1); - m_pusher.push(+1, ""); // fix stack - m_pusher.push(-1, "STU 32"); + m_pusher.fixStack(+1); // fix stack + m_pusher << "STU 32"; m_pusher.endContinuation(); m_pusher.ifElse(); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.endOpaque(3, 1); } // stack: flags builder - m_pusher.push(-1, "STU 8"); + m_pusher << "STU 8"; std::function appendStateInit = nullptr; if (stateInit != nullptr) @@ -955,7 +968,7 @@ void FunctionCallCompiler::generateExtInboundMsg( acceptExpr(stateInit); m_pusher.pushS(0); checkStateInit(); - m_pusher.push(-1, "STREFR"); + m_pusher << "STREFR"; }; m_pusher.prepareMsg({TvmConst::ext_msg_info::src, TvmConst::ext_msg_info::dest}, {}, appendBody, appendStateInit, StackPusher::MsgType::ExternalIn); @@ -1052,11 +1065,11 @@ void FunctionCallCompiler::tvmBuildIntMsg() { if (stateInit != -1) { appendStateInit = [&](){ // Either StateInit ^StateInit - m_pusher.push(-1 + 1, "STONE"); // ^StateInit + m_pusher << "STONE"; // ^StateInit pushArgAndConvert(stateInit, "stateInit"); m_pusher.pushS(0); checkStateInit(); - m_pusher.push(-2 + 1, "STREFR"); + m_pusher << "STREFR"; }; } @@ -1305,59 +1318,119 @@ bool FunctionCallCompiler::checkForTvmDeployMethods(MemberAccess const &_node, T } void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { + auto returnTypes = [&](bool fromOptional){ + Type const* type{}; + if (fromOptional) { + solAssert(to(m_retType), ""); + type= to(m_retType)->valueType(); + } else { + type = m_retType; + } + + TypePointers returnTypes; + if (auto const *targetTupleType = to(type)) + returnTypes = targetTupleType->components(); + else + returnTypes = TypePointers{type}; + return returnTypes; + }; + + const auto& value = m_arguments.empty() ? nullopt : ExprUtils::constValue(*m_arguments[0]); ASTString const& memberName = _node.memberName(); if (memberName == "empty") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "SEMPTY"); + m_pusher << "SEMPTY"; } else if (memberName == "dataSize") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2 + 3, "SDATASIZE"); + m_pusher << "SDATASIZE"; } else if (memberName == "dataSizeQ") { acceptExpr(&_node.expression()); pushArgAndConvert(0); cellBitRefQty(false); } else if (memberName == "size") { acceptExpr(&_node.expression()); - m_pusher.push(-1+2, "SBITREFS"); + m_pusher << "SBITREFS"; } else if (memberName == "bits") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "SBITS"); + m_pusher << "SBITS"; } else if (memberName == "compare") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2+1, "SDLEXCMP"); + m_pusher << "SDLEXCMP"; } else if (memberName == "hasNBits") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2+1, "SCHKBITSQ"); + m_pusher << "SCHKBITSQ"; } else if (memberName == "hasNRefs") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2+1, "SCHKREFSQ"); + m_pusher << "SCHKREFSQ"; } else if (memberName == "hasNBitsAndRefs") { acceptExpr(&_node.expression()); pushArgAndConvert(0); pushArgAndConvert(1); - m_pusher.push(-3+1, "SCHKBITREFSQ"); + m_pusher << "SCHKBITREFSQ"; } else if (memberName == "refs") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "SREFS"); + m_pusher << "SREFS"; } else if (memberName == "depth") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "SDEPTH"); + m_pusher << "SDEPTH"; } else if (memberName == "skip") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); if (m_arguments.size() == 1) { pushArgAndConvert(0); - m_pusher.push(-2+1, "SDSKIPFIRST"); + m_pusher << "SDSKIPFIRST"; } else { pushArgAndConvert(0); pushArgAndConvert(1); - m_pusher.push(-3+1, "SSKIPFIRST"); + m_pusher << "SSKIPFIRST"; } m_exprCompiler.collectLValue(lValueInfo, true, false); - } else if (isIn(memberName, "decode", "decodeQ") || boost::starts_with(memberName, "load")) { + } else if (isIn(memberName, "loadFunctionParams", "decodeFunctionParams", "loadStateVars", "decodeStateVars")) { + const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); + int paramQty = -1; + if (isIn(memberName, "loadFunctionParams", "decodeFunctionParams")) { + CallableDeclaration const *functionDefinition = getFunctionDeclarationOrConstructor( + m_arguments.at(0).get()); + if (functionDefinition) { + + // lvalue.. slice + auto fd = to(functionDefinition); + bool isResponsible = fd->isResponsible(); + if (isResponsible) { + m_pusher << "LDU 32"; + } + // lvalue.. callback slice + ChainDataDecoder decoder{&m_pusher}; + vector types = getParams(functionDefinition->parameters()).first; + decoder.decodePublicFunctionParameters(types, isResponsible, true); + + paramQty = functionDefinition->parameters().size() + (isResponsible ? 1 : 0); + } + } else if (isIn(memberName, "loadStateVars", "decodeStateVars")) { + std::vector stateVarTypes; + auto retTuple = to(m_retType); + for (Type const* type : retTuple->components()) { + stateVarTypes.push_back(type); + } + + // lvalue.. slice + ChainDataDecoder decoder{&m_pusher}; + decoder.decodeData(0, 0, stateVarTypes); + // lvalue.. stateVars... + + paramQty = stateVarTypes.size(); + } else { + solUnimplemented(""); + } + if (paramQty != -1) { + m_pusher.blockSwap(lValueInfo.stackSizeDiff - 1, paramQty); + m_pusher.pushSlice("x8_"); + } + m_exprCompiler.collectLValue(lValueInfo, true, false); + } else if (isIn(memberName, "load", "decode", "loadQ", "decodeQ") || boost::starts_with(memberName, "load")) { int stackDelta = 0; const int stackSize = m_pusher.stackSize(); bool isLValue = *_node.expression().annotation().isLValue; @@ -1367,75 +1440,146 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { } else { acceptExpr(&_node.expression()); } - if (memberName == "decode") { - TypePointers targetTypes; - if (auto const *targetTupleType = dynamic_cast(m_retType)) - targetTypes = targetTupleType->components(); - else - targetTypes = TypePointers{m_retType}; - + if (isIn(memberName, "load", "decode")) { + TypePointers types = returnTypes(false); ChainDataDecoder decode{&m_pusher}; DecodePositionFromOneSlice pos; - decode.decodeParameters(targetTypes, pos, false); - stackDelta = targetTypes.size(); - } else if (memberName == "decodeQ") { - auto optType = to(m_retType); - TypePointers targetTypes; - if (auto const *targetTupleType = dynamic_cast(optType->valueType())) - targetTypes = targetTupleType->components(); - else - targetTypes = TypePointers{optType->valueType()}; - + decode.decodeParameters(types, pos); + stackDelta = types.size(); + } else if (isIn(memberName, "loadQ", "decodeQ")) { ChainDataDecoder decode{&m_pusher}; DecodePositionFromOneSlice pos; - decode.decodeParametersQ(targetTypes, pos); + decode.decodeParametersQ(returnTypes(true), pos); stackDelta = 1; } else if (boost::starts_with(memberName, "load")) { stackDelta = 1; + std::optional opcode; if (memberName == "loadRefAsSlice") { - m_pusher.push(-1 + 2, "LDREFRTOS"); + m_pusher << "LDREFRTOS"; m_pusher.exchange(1); } else if (memberName == "loadRef") { - m_pusher.push(-1 + 2, "LDREF"); - } else if (memberName == "loadUnsigned" || memberName == "loadSigned") { + opcode = "LDREF"; + } else if (isIn(memberName, "loadUint", "loadUnsigned", "loadInt", "loadSigned")) { std::string cmd = "LD"; - cmd += (memberName == "loadSigned" ? "I" : "U"); - const auto& val = ExprUtils::constValue(*m_arguments[0]); - if (val.has_value()) { - if (val < 1 || val > 256) { - cast_error(*m_arguments[0], "The value must be in the range 1 - 256."); - } - m_pusher.push(-1 + 2, cmd + " " + val.value().str()); + cmd += isIn(memberName, "loadInt", "loadSigned") ? "I" : "U"; + if (value.has_value() && value != 0) { + m_pusher << cmd + " " + value->str(); } else { pushArgAndConvert(0); - m_pusher.push(-2 + 2, cmd + "X"); + m_pusher << cmd + "X"; + } + } else if (isIn(memberName, "loadUintQ", "loadIntQ")) { + std::string cmd = "LD"; + cmd += memberName == "loadIntQ" ? "I" : "U"; + int take{}; + if (value.has_value() && value != 0) { + cmd += "Q " + value->str(); + take = 1; + } else { + pushArgs(); + cmd += "XQ"; + take = 2; } + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, false, true)); + m_pusher.drop(); + m_pusher.endOpaque(take, 2); + } else if (isIn(memberName, "loadIntLE4Q", "loadIntLE8Q", "loadUintLE4Q", "loadUintLE8Q")) { + std::string cmd = "LD"; + cmd += boost::starts_with(memberName, "loadIntLE") ? "I" : "U"; + cmd += "LE"; + cmd += boost::ends_with(memberName, "4Q") ? "4" : "8"; + cmd += "Q"; + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, false, true)); + m_pusher.drop(); + m_pusher.endOpaque(1, 2); } else if (memberName == "loadTons") { - m_pusher.push(-1 + 2, "LDGRAMS"); + opcode = "LDGRAMS"; } else if (memberName == "loadSlice") { if (m_arguments.size() == 1) { - const auto& value = ExprUtils::constValue(*m_arguments[0].get()); - if (value.has_value()) { - m_pusher.push(+1, "LDSLICE " + value.value().str()); + if (value.has_value() && 0 < value && value <= 256) { + m_pusher << "LDSLICE " + value->str(); } else { pushArgAndConvert(0); - m_pusher.push(-2+2, "LDSLICEX"); + m_pusher << "LDSLICEX"; } } else { pushArgAndConvert(0); pushArgAndConvert(1); - m_pusher.push(-3+2, "SPLIT"); + m_pusher << "SPLIT"; + } + } else if (memberName == "loadSliceQ") { + string cmd; + int take{}; + if (m_arguments.size() == 1) { + if (value.has_value() && 0 < value && value <= 256) { + take = 1; + cmd = "LDSLICEQ " + value->str(); + } else { + take = 2; + pushArgs(); + cmd = "LDSLICEXQ"; + } + } else { + take = 3; + pushArgs(); + cmd = "SPLITQ"; } + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, false, true)); + m_pusher.drop(); + m_pusher.endOpaque(take, 2); } else if (memberName == "loadOnes") { - m_pusher.push(-1 + 2, "LDONES"); + opcode = "LDONES"; } else if (memberName == "loadZeroes") { - m_pusher.push(-1 + 2, "LDZEROES"); + opcode = "LDZEROES"; } else if (memberName == "loadSame") { - pushArgs(); - m_pusher.push(-2 + 2, "LDSAME"); + opcode = "LDSAME"; + } else if (memberName == "loadIntLE2") { + m_pusher << "LDU 8" + << "LDU 8"; + m_pusher.blockSwap(2, 1); + m_pusher << "LSHIFT 8" + << "ADD"; + m_pusher.pushS(0); // s v v + m_pusher.pushInt((1<<15) - 1); // s v v 32767 + m_pusher << "GREATER"; // s v v>32767 + m_pusher.fixStack(-1); // fix stack + m_pusher.startContinuation(); + // s v + m_pusher.pushInt(1 << 16); + m_pusher << "SUB"; + m_pusher.endContinuation(); + m_pusher._if(); + m_pusher.blockSwap(1, 1); + } else if (memberName == "loadIntLE4") { + opcode = "LDILE4"; + } else if (memberName == "loadIntLE8") { + opcode = "LDILE8"; + } else if (memberName == "loadUintLE2") { + m_pusher << "LDU 8" + << "LDU 8"; + m_pusher.blockSwap(2, 1); + m_pusher << "LSHIFT 8" + << "ADD"; + m_pusher.blockSwap(1, 1); + } else if (memberName == "loadUintLE4") { + opcode = "LDULE4"; + } else if (memberName == "loadUintLE8") { + opcode = "LDULE8"; } else { solUnimplemented(""); } + + if (opcode.has_value()) { + pushArgs(); + m_pusher << opcode.value(); + } + } else { solUnimplemented(""); } @@ -1452,48 +1596,121 @@ void FunctionCallCompiler::sliceMethods(MemberAccess const &_node) { // decodedValues... } solAssert(stackSize + stackDelta == m_pusher.stackSize(), ""); - } else if (memberName == "decodeFunctionParams" || memberName == "decodeStateVars") { - const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); - int paramQty = -1; - if (memberName == "decodeFunctionParams") { - CallableDeclaration const *functionDefinition = getFunctionDeclarationOrConstructor( - m_arguments.at(0).get()); - if (functionDefinition) { + } else if (memberName == "preload") { + acceptExpr(&_node.expression()); - // lvalue.. slice - auto fd = to(functionDefinition); - bool isResponsible = fd->isResponsible(); - if (isResponsible) { - m_pusher.push(-1 + 2, "LDU 32"); - } - // lvalue.. callback slice - ChainDataDecoder decoder{&m_pusher}; - vector types = getParams(functionDefinition->parameters()).first; - decoder.decodePublicFunctionParameters(types, isResponsible, true); + ChainDataDecoder decode{&m_pusher}; + DecodePositionFromOneSlice pos; + decode.decodeParameters(returnTypes(false), pos); + m_pusher.drop(); + } else if (memberName == "preloadQ") { + acceptExpr(&_node.expression()); - paramQty = functionDefinition->parameters().size() + (isResponsible ? 1 : 0); + ChainDataDecoder decode{&m_pusher}; + DecodePositionFromOneSlice pos; + decode.decodeParametersQ(returnTypes(true), pos); + m_pusher.drop(); + } else if (memberName == "preloadRef") { + acceptExpr(&_node.expression()); + if (m_arguments.empty()) { + m_pusher << "PLDREF"; + } else { + if (value.has_value()) { + m_pusher << "PLDREFIDX " + toString(value.value()); + } else { + pushArgs(); + m_pusher << "PLDREFVAR"; } - } else if (memberName == "decodeStateVars") { - std::vector stateVarTypes; - auto retTuple = to(m_retType); - for (Type const* type : retTuple->components()) { - stateVarTypes.push_back(type); + } + } else if (isIn(memberName, "preloadInt", "preloadUint")) { + acceptExpr(&_node.expression()); + string cmd = string{} + "PLD" + (memberName == "preloadInt" ? "I" : "U"); + if (value.has_value() && value != 0) { + cmd += " " + value->str(); + } else { + pushArgs(); + cmd += "X"; + } + m_pusher << cmd; + } else if (isIn(memberName, "preloadIntLE4", "preloadIntLE8", "preloadUintLE4", "preloadUintLE8")) { + acceptExpr(&_node.expression()); + string cmd = "PLD"; + cmd += boost::starts_with(memberName, "preloadInt") ? "I" : "U"; + cmd += "LE"; + cmd += boost::ends_with(memberName, "4") ? "4" : "8"; + m_pusher << cmd; + } else if (isIn(memberName, "preloadIntQ", "preloadUintQ")) { + acceptExpr(&_node.expression()); + string cmd = string{} + "PLD" + (memberName == "preloadIntQ" ? "I" : "U"); + int take{}; + if (value.has_value() && value != 0) { + take = 1; + cmd += "Q " + toString(value.value()); + } else { + pushArgs(); + take = 2; + cmd += "XQ"; + } + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, true, true)); + m_pusher.drop(); + m_pusher.endOpaque(take, 1); + } else if (isIn(memberName, "preloadIntLE4Q", "preloadIntLE8Q", "preloadUintLE4Q", "preloadUintLE8Q")) { + acceptExpr(&_node.expression()); + string cmd = "PLD"; + cmd += boost::starts_with(memberName, "preloadInt") ? "I" : "U"; + cmd += "LE"; + cmd += boost::ends_with(memberName, "4Q") ? "4" : "8"; + cmd += "Q"; + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, true, true)); + m_pusher.drop(); + m_pusher.endOpaque(1, 1); + } else if (memberName == "preloadSlice") { + acceptExpr(&_node.expression()); + if (m_arguments.size() == 1) { + if (value.has_value() && 0 < value && value <= 256) { + m_pusher << "PLDSLICE " + value->str(); + } else { + pushArgs(); + m_pusher << "PLDSLICEX"; } - - // lvalue.. slice - ChainDataDecoder decoder{&m_pusher}; - decoder.decodeData(0, 0, stateVarTypes); - // lvalue.. stateVars... - - paramQty = stateVarTypes.size(); } else { - solUnimplemented(""); + pushArgs(); + m_pusher << "SCUTFIRST"; } - if (paramQty != -1) { - m_pusher.blockSwap(lValueInfo.stackSizeDiff - 1, paramQty); - m_pusher.pushSlice("x8_"); + } else if (memberName == "preloadSliceQ") { + acceptExpr(&_node.expression()); + string cmd; + int take{}; + int ret{}; + if (m_arguments.size() == 1) { + if (value.has_value() && 0 < value && value <= 256) { + cmd += "PLDSLICEQ " + value->str(); + take = 1; + ret = 1; + } else { + pushArgs(); + cmd += "PLDSLICEXQ"; + take = 2; + ret = 1; + } + } else { + pushArgs(); + cmd += "SPLITQ"; + take = 3; + ret = 2; + } + m_pusher.startOpaque(); + m_pusher.pushAsym(cmd); + m_pusher.pushAsym(getZeroOrNullAlignment(false, true, true)); + m_pusher.drop(); + m_pusher.endOpaque(take, ret); + if (ret == 2) { + m_pusher.drop(); } - m_exprCompiler.collectLValue(lValueInfo, true, false); } else { solUnimplemented(""); } @@ -1515,21 +1732,21 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T m_pusher.exchange(1); // new_el vector m_pusher.pushS(0); - m_pusher.push(0, "TLEN"); + m_pusher << "TLEN"; //new_el vector vector_len // if vector is not empty get last tuple m_pusher.startContinuation(); - m_pusher.push(+1, "TPOP"); + m_pusher << "TPOP"; // new_el vector' last_tuple m_pusher.pushS(0); - m_pusher.push(0, "TLEN"); + m_pusher << "TLEN"; m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher.push(-1, "SUB"); + m_pusher << "SUB"; // if last tuple is full (his length is equal to 255) push it back to vector and create new tuple m_pusher.startContinuation(); - m_pusher.push(-1, "TPUSH"); + m_pusher << "TPUSH"; m_pusher.tuple(0); m_pusher.endContinuation(); m_pusher.ifNot(); @@ -1546,9 +1763,9 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // new_el vector' tuple m_pusher.rot(); // vector' tuple new_el - m_pusher.push(-1, "TPUSH"); + m_pusher << "TPUSH"; // vector' tuple' - m_pusher.push(-1, "TPUSH"); + m_pusher << "TPUSH"; // vector'' m_pusher.endOpaque(2, 1); @@ -1569,9 +1786,9 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // vector m_pusher.startOpaque(); - m_pusher.push(+1, "TPOP"); + m_pusher << "TPOP"; // vector' last_tuple - m_pusher.push(+1, "TPOP"); + m_pusher << "TPOP"; // [vector_expand] vector' last_tuple' last_el // put last el before vector and it's expand components @@ -1580,12 +1797,12 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // if the last tuple is empty delete it m_pusher.pushS(0); - m_pusher.push(0, "TLEN"); + m_pusher << "TLEN"; // last_el [vector_expand] vector' last_tuple' last_tuple_len // if last_tuple is not empty push it back to the vector m_pusher.startContinuation(); - m_pusher.push(-1, "TPUSH"); + m_pusher << "TPUSH"; m_pusher.endContinuation(); // if last_tuple is empty drop it @@ -1615,7 +1832,7 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T m_pusher.startOpaque(); m_pusher.pushS(0); - m_pusher.push(-1 + 1, "TLEN"); + m_pusher << "TLEN"; // vector len m_pusher.pushS(0); // vector len len @@ -1623,17 +1840,17 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T // if len is not zero count length of full tuples and add length of the last tuple m_pusher.startContinuation(); // vector len - m_pusher.push(0, "DEC"); + m_pusher << "DEC"; // vector len-- m_pusher.pushInt(TvmConst::TvmTupleLen); - m_pusher.push(-1, "MUL"); + m_pusher << "MUL"; // vector full_tuples_len m_pusher.exchange(1); - m_pusher.push(-1+1, "LAST"); + m_pusher << "LAST"; // full_tuples_len last_tuple - m_pusher.push(+1-1, "TLEN"); + m_pusher << "TLEN"; // full_tuples_len last_tuple_len - m_pusher.push(-1, "ADD"); + m_pusher << "ADD"; m_pusher.endContinuation(); // if length is zero just leave it on stack @@ -1652,8 +1869,8 @@ bool FunctionCallCompiler::checkForTvmVectorMethods(MemberAccess const &_node, T if (_node.memberName() == "empty") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "TLEN"); - m_pusher.push(-1 + 1, "ISZERO"); + m_pusher << "TLEN"; + m_pusher << "ISZERO"; return true; } @@ -1665,26 +1882,26 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { if (boost::starts_with(memberName, "store")) { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); + std::optional opcode; + bool doSwap = false; if (memberName == "storeOnes") { - pushArgs(); - m_pusher.push(-2 + 1, "STONES"); + opcode = "STONES"; } else if (memberName == "storeZeroes") { - pushArgs(); - m_pusher.push(-2 + 1, "STZEROES"); + opcode = "STZEROES"; } else if (memberName == "storeRef") { pushArgAndConvert(0); Type::Category cat = m_arguments.at(0)->annotation().type->category(); switch (cat) { case Type::Category::TvmBuilder: - m_pusher.push(-1, "STBREFR"); + m_pusher << "STBREFR"; break; case Type::Category::TvmCell: - m_pusher.push(-1, "STREFR"); + m_pusher << "STREFR"; break; case Type::Category::TvmSlice: - m_pusher.push(+1, "NEWC"); - m_pusher.push(-2 + 1, "STSLICE"); - m_pusher.push(-1, "STBREFR"); + m_pusher << "NEWC"; + m_pusher << "STSLICE"; + m_pusher << "STBREFR"; break; default: solUnimplemented(""); @@ -1710,59 +1927,99 @@ void FunctionCallCompiler::builderMethods(MemberAccess const &_node) { m_pusher.store(argument->annotation().type->mobileType(), false); } } - } else if (memberName == "storeSigned" || memberName == "storeUnsigned") { + } else if (isIn(memberName, "storeSigned", "storeInt", "storeUnsigned", "storeUint")) { std::string cmd = "ST"; - cmd += (memberName == "storeSigned" ? "I" : "U"); + cmd += isIn(memberName, "storeSigned", "storeInt") ? "I" : "U"; pushArgAndConvert(0); const auto& val = ExprUtils::constValue(*m_arguments[1]); if (val.has_value()) { if (val < 1 || val > 256) { + // TODO val == 0 -> ok cast_error(*m_arguments[1], "The value must be in the range 1 - 256."); } - m_pusher.push(-1, cmd + "R " + val.value().str()); + m_pusher << cmd + "R " + val.value().str(); } else { pushArgAndConvert(1); - m_pusher.push(-2, cmd + "XR"); + m_pusher << cmd + "XR"; } } else if (memberName == "storeTons") { - pushArgAndConvert(0); - m_pusher.push(-1, "STGRAMS"); + opcode = "STGRAMS"; } else if (memberName == "storeSame") { + opcode = "STSAME"; + } else if (memberName == "storeIntLE2") { + pushArgs(); + m_pusher.pushS(0); + m_pusher << "ISNEG"; + m_pusher.fixStack(-1); // fix stack + m_pusher.startContinuation(); + m_pusher.pushInt(1 << 16); + m_pusher << "ADD"; + m_pusher.endContinuation(); + m_pusher._if(); + m_pusher.pushInt(1 << 8); + m_pusher << "DIVMOD"; // s a1 a0 + m_pusher.blockSwap(1, 2); // a1 a0 s + m_pusher << "STU 8" + << "STU 8"; + } else if (memberName == "storeIntLE4") { + opcode = "STILE4"; + doSwap = true; + } else if (memberName == "storeIntLE8") { + opcode = "STILE8"; + doSwap = true; + } else if (memberName == "storeUintLE2") { pushArgs(); - m_pusher << "STSAME"; + m_pusher.pushInt(1 << 8); + m_pusher << "DIVMOD"; // s a1 a0 + m_pusher.blockSwap(1, 2); // a1 a0 s + m_pusher << "STU 8" + << "STU 8"; + } else if (memberName == "storeUintLE4") { + opcode = "STULE4"; + doSwap = true; + } else if (memberName == "storeUintLE8") { + opcode = "STULE8"; + doSwap = true; } else { solUnimplemented(""); } + if (opcode.has_value()) { + pushArgs(); + if (doSwap) + m_pusher.blockSwap(1, 1); + m_pusher << *opcode; + } + m_exprCompiler.collectLValue(lValueInfo, true, false); } else if (memberName == "bits") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "BBITS"); + m_pusher << "BBITS"; } else if (memberName == "refs") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "BREFS"); + m_pusher << "BREFS"; } else if (memberName == "size") { acceptExpr(&_node.expression()); - m_pusher.push(-1+2, "BBITREFS"); + m_pusher << "BBITREFS"; } else if (memberName == "remBits") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "BREMBITS"); + m_pusher << "BREMBITS"; } else if (memberName == "remRefs") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "BREMREFS"); + m_pusher << "BREMREFS"; } else if (memberName == "remBitsAndRefs") { acceptExpr(&_node.expression()); - m_pusher.push(-1+2, "BREMBITREFS"); + m_pusher << "BREMBITREFS"; } else if (memberName == "toCell") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "ENDC"); + m_pusher << "ENDC"; } else if (memberName == "toSlice") { acceptExpr(&_node.expression()); - m_pusher.push(-1+1, "ENDC"); - m_pusher.push(-1+1, "CTOS"); + m_pusher << "ENDC"; + m_pusher << "CTOS"; } else if (memberName == "depth") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "BDEPTH"); + m_pusher << "BDEPTH"; } else { solUnimplemented(""); } @@ -1774,10 +2031,10 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { acceptExpr(&_node.expression()); if (isUsualArray(type)) { m_pusher.indexNoexcep(0); - m_pusher.push(-1 + 1, "EQINT 0"); + m_pusher << "EQINT 0"; } else { - m_pusher.push(-1 + 1, "CTOS"); - m_pusher.push(-1 + 1, "SEMPTY"); + m_pusher << "CTOS"; + m_pusher << "SEMPTY"; } } else if (_node.memberName() == "substr") { acceptExpr(&_node.expression()); @@ -1811,10 +2068,10 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { } else if (_node.memberName() == "dataSize") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2 + 3, "CDATASIZE"); + m_pusher << "CDATASIZE"; } else if (_node.memberName() == "toSlice") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "CTOS"); + m_pusher << "CTOS"; } else if (_node.memberName() == "dataSizeQ") { acceptExpr(&_node.expression()); pushArgAndConvert(0); @@ -1836,17 +2093,17 @@ void FunctionCallCompiler::arrayMethods(MemberAccess const &_node) { } else if (_node.memberName() == "pop") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); // arr - m_pusher.push(-1 + 2, "UNTUPLE 2"); // size dict + m_pusher << "UNTUPLE 2"; // size dict m_pusher.pushS(1); // size dict size m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::PopFromEmptyArray)); // size dict m_pusher.exchange(1); // dict size - m_pusher.push(0, "DEC"); // dict newSize + m_pusher << "DEC"; // dict newSize m_pusher.pushS(0); // dict newSize newSize m_pusher.rot(); // newSize newSize dict m_pusher.pushInt(TvmConst::ArrayKeyLength); // newSize newSize dict 32 - m_pusher.push(-3 + 2, "DICTUDEL"); // newSize dict ? + m_pusher << "DICTUDEL"; // newSize dict ? m_pusher.drop(1); // newSize dict - m_pusher.push(-2 + 1, "TUPLE 2"); // arr + m_pusher << "TUPLE 2"; // arr m_exprCompiler.collectLValue(lValueInfo, true, false); } else if (_node.memberName() == "append") { const LValueInfo lValueInfo = m_exprCompiler.expandLValue(&_node.expression(), true); @@ -1865,8 +2122,8 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { if (_node.memberName() == "hasValue") { acceptExpr(&_node.expression()); - m_pusher.push(+1 - 1, "ISNULL"); - m_pusher.push(0, "NOT"); + m_pusher << "ISNULL"; + m_pusher << "NOT"; return true; } @@ -1913,14 +2170,14 @@ bool FunctionCallCompiler::checkForOptionalMethods(MemberAccess const &_node) { void FunctionCallCompiler::cellMethods(MemberAccess const &_node) { if (_node.memberName() == "toSlice") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "CTOS"); + m_pusher << "CTOS"; } else if (_node.memberName() == "depth") { acceptExpr(&_node.expression()); - m_pusher.push(-1 + 1, "CDEPTH"); + m_pusher << "CDEPTH"; } else if (_node.memberName() == "dataSize") { acceptExpr(&_node.expression()); pushArgAndConvert(0); - m_pusher.push(-2 + 3, "CDATASIZE"); + m_pusher << "CDATASIZE"; } else if (_node.memberName() == "dataSizeQ") { acceptExpr(&_node.expression()); pushArgAndConvert(0); @@ -1932,35 +2189,35 @@ void FunctionCallCompiler::cellMethods(MemberAccess const &_node) { void FunctionCallCompiler::variantMethods(MemberAccess const& _node) { - auto isUint = [&](){ - m_pusher.push(createNode(std::vector{ - "PUSHCONT {", - " UFITS 256", - " TRUE", - "}", - "PUSHCONT {", - " FALSE", - "}", - "TRYARGS 1, 1" - }, 1, 1, true)); - }; - - switch (m_funcType->kind()) { - case FunctionType::Kind::VariantToUint: { - acceptExpr(&_node.expression()); - m_pusher.pushS(0); - isUint(); - m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::BadVariant)); - break; - } - case FunctionType::Kind::VariantIsUint: { - acceptExpr(&_node.expression()); - isUint(); - break; - } - default: - solUnimplemented(""); - } + auto isUint = [&](){ + m_pusher.push(createNode(std::vector{ + "PUSHCONT {", + " UFITS 256", + " TRUE", + "}", + "PUSHCONT {", + " FALSE", + "}", + "TRYARGS 1, 1" + }, 1, 1, true)); + }; + + switch (m_funcType->kind()) { + case FunctionType::Kind::VariantToUint: { + acceptExpr(&_node.expression()); + m_pusher.pushS(0); + isUint(); + m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::BadVariant)); + break; + } + case FunctionType::Kind::VariantIsUint: { + acceptExpr(&_node.expression()); + isUint(); + break; + } + default: + solUnimplemented(""); + } } void FunctionCallCompiler::addressMethod() { @@ -1994,11 +2251,11 @@ void FunctionCallCompiler::addressMethod() { auto setAppendStateInit = [&](Expression const* expr) { appendStateInit = [expr, this](){ // Either StateInit ^StateInit - m_pusher.push(-1 + 1, "STONE"); // ^StateInit + m_pusher << "STONE"; // ^StateInit acceptExpr(expr); m_pusher.pushS(0); checkStateInit(); - m_pusher.push(-2 + 1, "STREFR"); + m_pusher << "STREFR"; }; }; @@ -2024,7 +2281,7 @@ void FunctionCallCompiler::addressMethod() { appendBody = [e = m_arguments[arg], this](int /*size*/){ m_pusher.stones(1); acceptExpr(e.get()); - m_pusher.push(-1, "STREFR"); + m_pusher << "STREFR"; return false; }; break; @@ -2053,7 +2310,7 @@ void FunctionCallCompiler::addressMethod() { appendBody = [&](int /*size*/) { m_pusher.stones(1); pushArgAndConvert(3); - m_pusher.push(-1, "STREFR"); + m_pusher << "STREFR"; return false; }; } @@ -2068,28 +2325,28 @@ void FunctionCallCompiler::addressMethod() { } else if (_node->memberName() == "isStdZero") { acceptExpr(&_node->expression()); m_pusher.pushZeroAddress(); - m_pusher.push(-2 + 1, "SDEQ"); + m_pusher << "SDEQ"; } else if (_node->memberName() == "isExternZero") { acceptExpr(&_node->expression()); m_pusher.pushSlice("x401_"); - m_pusher.push(-2 + 1, "SDEQ"); + m_pusher << "SDEQ"; } else if (_node->memberName() == "isNone") { acceptExpr(&_node->expression()); m_pusher.pushSlice("x2_"); - m_pusher.push(-2 + 1, "SDEQ"); + m_pusher << "SDEQ"; } else if (_node->memberName() == "unpack") { acceptExpr(&_node->expression()); - m_pusher.push(-1 + 2, "REWRITESTDADDR"); + m_pusher << "REWRITESTDADDR"; } else if (_node->memberName() == "getType") { acceptExpr(&_node->expression()); - m_pusher.push(+1 - 1, "PLDU 2"); + m_pusher << "PLDU 2"; } else if (_node->memberName() == "isStdAddrWithoutAnyCast") { acceptExpr(&_node->expression()); // t = (2, u, x, s); check t[0] == 2 and t[1] is null - m_pusher.push(-1 + 1, "PARSEMSGADDR"); + m_pusher << "PARSEMSGADDR"; m_pusher.pushS(0); - m_pusher.push(-1 + 1, "INDEX_NOEXCEP 0"); - m_pusher.push(-1 + 1, "EQINT 2"); // t tag==2 + m_pusher << "INDEX_NOEXCEP 0"; + m_pusher << "EQINT 2"; // t tag==2 m_pusher.push(createNode(std::vector{ "PUSHCONT {", @@ -2131,15 +2388,15 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n m_pusher.pushAsym("CONFIGPARAM"); m_pusher.startContinuation(); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "LDU 256"); - m_pusher.push(-1, "ENDS"); - m_pusher.push(+1, "TRUE"); + m_pusher << "CTOS"; + m_pusher << "LDU 256"; + m_pusher << "ENDS"; + m_pusher << "TRUE"; m_pusher.endContinuation(); m_pusher.startContinuation(); m_pusher.pushInt(0); - m_pusher.push(+1, "FALSE"); + m_pusher << "FALSE"; m_pusher.endContinuation(); m_pusher.ifElse(); @@ -2160,13 +2417,13 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n m_pusher.pushAsym("CONFIGPARAM"); m_pusher.startContinuation(); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "LDU 32"); - m_pusher.push(+1, "LDU 32"); - m_pusher.push(+1, "LDU 32"); - m_pusher.push(+1, "LDU 32"); - m_pusher.push(-1, "ENDS"); - m_pusher.push(+1, "TRUE"); + m_pusher << "CTOS"; + m_pusher << "LDU 32"; + m_pusher << "LDU 32"; + m_pusher << "LDU 32"; + m_pusher << "LDU 32"; + m_pusher << "ENDS"; + m_pusher << "TRUE"; m_pusher.endContinuation(); m_pusher.startContinuation(); @@ -2190,13 +2447,13 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n m_pusher.pushAsym("CONFIGPARAM"); m_pusher.startContinuation(); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "LDGRAMS"); - m_pusher.push(+1, "LDGRAMS"); - m_pusher.push(+1, "LDGRAMS"); - m_pusher.push(+1, "LDU 32"); - m_pusher.push(-1, "ENDS"); - m_pusher.push(+1, "TRUE"); + m_pusher << "CTOS"; + m_pusher << "LDGRAMS"; + m_pusher << "LDGRAMS"; + m_pusher << "LDGRAMS"; + m_pusher << "LDU 32"; + m_pusher << "ENDS"; + m_pusher << "TRUE"; m_pusher.endContinuation(); m_pusher.startContinuation(); @@ -2240,27 +2497,27 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n m_pusher.pushAsym("CONFIGPARAM"); m_pusher.startContinuation(); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "LDU 8"); // constructor - m_pusher.push(+1, "LDU 32"); // utime_since - m_pusher.push(+1, "LDU 32"); // utime_until - m_pusher.push(+1, "LDU 16"); // total - m_pusher.push(+1, "LDU 16"); // main - m_pusher.push(+1, "LDU 64"); // total_weight - m_pusher.push(+1, "LDDICT"); // ValidatorDescr - m_pusher.push(-1, "ENDS"); - m_pusher.push(+1, "TRUE"); + m_pusher << "CTOS"; + m_pusher << "LDU 8"; // constructor + m_pusher << "LDU 32"; // utime_since + m_pusher << "LDU 32"; // utime_until + m_pusher << "LDU 16"; // total + m_pusher << "LDU 16"; // main + m_pusher << "LDU 64"; // total_weight + m_pusher << "LDDICT"; // ValidatorDescr + m_pusher << "ENDS"; + m_pusher << "TRUE"; m_pusher.endContinuation(); m_pusher.startContinuation(); - m_pusher.push(+1, "PUSHINT 0"); // constructor - m_pusher.push(+1, "PUSHINT 0"); // utime_since - m_pusher.push(+1, "PUSHINT 0"); // utime_until - m_pusher.push(+1, "PUSHINT 0"); // total - m_pusher.push(+1, "PUSHINT 0"); // main - m_pusher.push(+1, "PUSHINT 0"); // total_weight - m_pusher.push(+1, "NEWDICT"); // ValidatorDescr - m_pusher.push(+1, "FALSE"); // + m_pusher << "PUSHINT 0"; // constructor + m_pusher << "PUSHINT 0"; // utime_since + m_pusher << "PUSHINT 0"; // utime_until + m_pusher << "PUSHINT 0"; // total + m_pusher << "PUSHINT 0"; // main + m_pusher << "PUSHINT 0"; // total_weight + m_pusher << "NEWDICT"; // ValidatorDescr + m_pusher << "FALSE"; // m_pusher.endContinuation(); m_pusher.ifElse(); @@ -2275,7 +2532,7 @@ bool FunctionCallCompiler::checkForTvmConfigParamFunction(MemberAccess const &_n bool FunctionCallCompiler::checkForTvmSendFunction(MemberAccess const &_node) { if (_node.memberName() == "sendrawmsg") { // tvm.sendrawmsg pushArgs(); - m_pusher.push(-2, "SENDRAWMSG"); + m_pusher << "SENDRAWMSG"; } else { return false; } @@ -2287,7 +2544,7 @@ void FunctionCallCompiler::msgFunction(MemberAccess const &_node) { m_pusher.getGlob(TvmConst::C7::MsgPubkey); m_pusher.startOpaque(); m_pusher.pushS(0); - m_pusher.push(-1 + 1, "ISNULL"); + m_pusher << "ISNULL"; m_pusher.startContinuation(); m_pusher.drop(); m_pusher.pushInt(0); @@ -2304,30 +2561,30 @@ void FunctionCallCompiler::rndFunction(MemberAccess const &_node) { case FunctionType::Kind::RndNext: pushArgs(); if (m_arguments.empty()) { - m_pusher.push(+1, "RANDU256"); + m_pusher << "RANDU256"; } else { - m_pusher.push(-1 + 1, "RAND"); + m_pusher << "RAND"; } break; case FunctionType::Kind::RndSetSeed: { pushArgAndConvert(0); - m_pusher.push(-1, "SETRAND"); + m_pusher << "SETRAND"; break; } case FunctionType::Kind::RndGetSeed: { - m_pusher.push(+1, "RANDSEED"); + m_pusher << "RANDSEED"; break; } case FunctionType::Kind::RndShuffle: { if (m_arguments.empty()) { - m_pusher.push(+1, "LTIME"); + m_pusher << "LTIME"; } else { pushArgs(); } - m_pusher.push(-1, "ADDRAND"); + m_pusher << "ADDRAND"; break; } default: @@ -2336,40 +2593,40 @@ void FunctionCallCompiler::rndFunction(MemberAccess const &_node) { } void FunctionCallCompiler::goshFunction() { - auto typeToOpcode = [&]() -> std::string { - switch (m_funcType->kind()) { - case FunctionType::Kind::GoshDiff: - return "DIFF"; - case FunctionType::Kind::GoshApplyPatch: - return "DIFF_PATCH"; - case FunctionType::Kind::GoshZip: - return "ZIP"; - case FunctionType::Kind::GoshUnzip: - return "UNZIP"; - case FunctionType::Kind::GoshZipDiff: - return "DIFF_ZIP"; - case FunctionType::Kind::GoshApplyZipPatch: - return "DIFF_PATCH_ZIP"; - case FunctionType::Kind::GoshApplyPatchQ: - return "DIFF_PATCHQ"; - case FunctionType::Kind::GoshApplyZipPatchQ: - return "DIFF_PATCH_ZIPQ"; - case FunctionType::Kind::GoshApplyBinPatch: - return "DIFF_PATCH_BINARY"; - case FunctionType::Kind::GoshApplyBinPatchQ: - return "DIFF_PATCH_BINARYQ"; - case FunctionType::Kind::GoshApplyZipBinPatch: - return "DIFF_PATCH_BINARY_ZIP"; - case FunctionType::Kind::GoshApplyZipBinPatchQ: - return "DIFF_PATCH_BINARY_ZIPQ"; - default: - solUnimplemented("Unsupported function call"); - } - }; - - pushArgs(); - string opcode = typeToOpcode(); - m_pusher << opcode; + auto typeToOpcode = [&]() -> std::string { + switch (m_funcType->kind()) { + case FunctionType::Kind::GoshDiff: + return "DIFF"; + case FunctionType::Kind::GoshApplyPatch: + return "DIFF_PATCH"; + case FunctionType::Kind::GoshZip: + return "ZIP"; + case FunctionType::Kind::GoshUnzip: + return "UNZIP"; + case FunctionType::Kind::GoshZipDiff: + return "DIFF_ZIP"; + case FunctionType::Kind::GoshApplyZipPatch: + return "DIFF_PATCH_ZIP"; + case FunctionType::Kind::GoshApplyPatchQ: + return "DIFF_PATCHQ"; + case FunctionType::Kind::GoshApplyZipPatchQ: + return "DIFF_PATCH_ZIPQ"; + case FunctionType::Kind::GoshApplyBinPatch: + return "DIFF_PATCH_BINARY"; + case FunctionType::Kind::GoshApplyBinPatchQ: + return "DIFF_PATCH_BINARYQ"; + case FunctionType::Kind::GoshApplyZipBinPatch: + return "DIFF_PATCH_BINARY_ZIP"; + case FunctionType::Kind::GoshApplyZipBinPatchQ: + return "DIFF_PATCH_BINARY_ZIPQ"; + default: + solUnimplemented("Unsupported function call"); + } + }; + + pushArgs(); + string opcode = typeToOpcode(); + m_pusher << opcode; } bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { @@ -2379,17 +2636,17 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { pushArgs(); m_pusher.setGlob(TvmConst::C7::TvmPubkey); } else if (_node.memberName() == "accept") { // tvm.accept - m_pusher.push(0, "ACCEPT"); + m_pusher << "ACCEPT"; } else if (_node.memberName() == "hash") { // tvm.hash pushArgs(); switch (m_arguments.at(0)->annotation().type->category()) { case Type::Category::TvmCell: case Type::Category::Array: case Type::Category::StringLiteral: - m_pusher.push(0, "HASHCU"); + m_pusher << "HASHCU"; break; case Type::Category::TvmSlice: - m_pusher.push(0, "HASHSU"); + m_pusher << "HASHSU"; break; default: solUnimplemented(""); @@ -2398,58 +2655,58 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { size_t cnt = m_arguments.size(); if (getType(m_arguments[0].get())->category() == Type::Category::TvmSlice) { pushArgs(); - m_pusher.push(-3+1, "CHKSIGNS"); + m_pusher << "CHKSIGNS"; } else { pushArgAndConvert(0); if (cnt == 4) { pushArgAndConvert(2); pushArgAndConvert(1); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1, "STU 256"); - m_pusher.push(-1, "STU 256"); - m_pusher.push(0, "ENDC"); - m_pusher.push(0, "CTOS"); + m_pusher << "NEWC"; + m_pusher << "STU 256"; + m_pusher << "STU 256"; + m_pusher << "ENDC"; + m_pusher << "CTOS"; } else { pushArgAndConvert(1); } pushArgAndConvert(cnt - 1); - m_pusher.push(-3+1, "CHKSIGNU"); + m_pusher << "CHKSIGNU"; } } else if (_node.memberName() == "setcode") { // tvm.setcode pushArgs(); - m_pusher.push(-1, "SETCODE"); + m_pusher << "SETCODE"; } else if (_node.memberName() == "bindump") { // tvm.bindump pushArgs(); if (getType(m_arguments[0].get())->category() == Type::Category::TvmCell) - m_pusher.push(-1+1, "CTOS"); - m_pusher.push(0, "BINDUMP"); + m_pusher << "CTOS"; + m_pusher << "BINDUMP"; m_pusher.drop(); } else if (_node.memberName() == "hexdump") { // tvm.hexdump pushArgs(); if (getType(m_arguments[0].get())->category() == Type::Category::TvmCell) - m_pusher.push(-1+1, "CTOS"); - m_pusher.push(0, "HEXDUMP"); + m_pusher << "CTOS"; + m_pusher << "HEXDUMP"; m_pusher.drop(); } else if (_node.memberName() == "setCurrentCode") { // tvm.setCurrentCode const int stackSize = m_pusher.stackSize(); pushArgs(); - m_pusher.push(0, "CTOS"); + m_pusher << "CTOS"; m_pusher.pushS(0); m_pusher.pushSlice("x" + TvmConst::Selector::RootCodeCell()); - m_pusher.push(-2 + 1, "SDEQ"); + m_pusher << "SDEQ"; m_pusher.startOpaque(); m_pusher.startContinuation(); - m_pusher.push(0, "PLDREFIDX 1"); - m_pusher.push(0, "CTOS"); + m_pusher << "PLDREFIDX 1"; + m_pusher << "CTOS"; m_pusher.endContinuation(); m_pusher._if(); m_pusher.endOpaque(2, 1); - m_pusher.push(0, "PLDREF"); - m_pusher.push(0, "CTOS"); - m_pusher.push(0, "BLESS"); + m_pusher << "PLDREF"; + m_pusher << "CTOS"; + m_pusher << "BLESS"; m_pusher.popC3(); solAssert(stackSize == m_pusher.stackSize(), ""); } else if (_node.memberName() == "getData") { // tvm.getData @@ -2458,10 +2715,10 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { pushArgs(); m_pusher.popRoot(); } else if (_node.memberName() == "rawCommit") { // tvm.rawCommit - m_pusher.push(0, "COMMIT"); + m_pusher << "COMMIT"; } else if (_node.memberName() == "commit") { // tvm.commit m_pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); - m_pusher.push(0, "COMMIT"); + m_pusher << "COMMIT"; } else if (_node.memberName() == "log") { // tvm.log compileLog(); } else if (_node.memberName() == "resetStorage") { //tvm.resetStorage @@ -2483,7 +2740,7 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { } else if (_node.memberName() == "encodeBody") { // tvm.encodeBody CallableDeclaration const* callDef = getFunctionDeclarationOrConstructor(m_arguments.at(0).get()); if (callDef == nullptr) { // if no constructor (default constructor) - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; ChainDataEncoder{&m_pusher}.createDefaultConstructorMessage2(); } else { auto funcDef = to(callDef); @@ -2500,7 +2757,7 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { for (int i = m_arguments.size() - 1; i >= 1 + shift; --i) { acceptExpr(m_arguments.at(i).get()); } - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; ChainDataEncoder{&m_pusher}.createMsgBody( convertArray(parameters), ChainDataEncoder{&m_pusher}.calculateFunctionIDWithReason(callDef, ReasonOfOutboundMessage::RemoteCallInternal), @@ -2508,15 +2765,15 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node) { position ); } - m_pusher.push(-1 + 1, "ENDC"); + m_pusher << "ENDC"; } else if (_node.memberName() == "rawReserve") { pushArgs(); int n = m_arguments.size(); solAssert(isIn(n, 2, 3), ""); - m_pusher.push(-n, n == 2? "RAWRESERVE" : "RAWRESERVEX"); + m_pusher << (n == 2 ? "RAWRESERVE" : "RAWRESERVEX"); } else if (isIn(_node.memberName(), "exit", "exit1")) { m_pusher.was_c4_to_c7_called(); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); m_pusher.pushCall(0, 0, "c7_to_c4"); @@ -2586,7 +2843,7 @@ CALLREF { m_pusher.push(createNode(codeLines, 1, 1, false)); } else if (_node.memberName() == "setCodeSalt") { pushArgAndConvert(0); - m_pusher.push(-1 + 1, "CTOS"); // sliceCode + m_pusher << "CTOS"; // sliceCode pushArgAndConvert(1); // sliceCode salt string insertSaltInUsualSelector = R"( LDREFRTOS ; selfCallCode salt restUsualSelector intSelector @@ -2607,11 +2864,11 @@ CALLREF { SWAP } IFELSE - ; selfCallCode salt restUsualSelector dict version intSelector + ; selfCallCode salt restUsualSelector dict version intSelector PUSHSLICE xPrivateOpcode1 SDBEGINSX DROP - ; selfCallCode salt restUsualSelector dict version + ; selfCallCode salt restUsualSelector dict version SWAP ; selfCallCode salt restUsualSelector version dict NEWC ; selfCallCode salt restUsualSelector version dict builder STSLICECONST xPrivateOpcode0 ; DICTPUSHCONST @@ -2674,12 +2931,12 @@ CALLREF { m_pusher.pushInt(TvmConst::Message::ReplayProtection::Interval); } else if (_node.memberName() == "setGasLimit") { pushArgs(); - m_pusher.push(-1, "SETGASLIMIT"); + m_pusher << "SETGASLIMIT"; } else if (_node.memberName() == "initCodeHash") { m_pusher << "INITCODEHASH"; } else if (_node.memberName() == "buyGas") { pushArgs(); - m_pusher.push(-1, "BUYGAS"); + m_pusher << "BUYGAS"; } else { return false; } @@ -2735,36 +2992,36 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { if (_node.memberName() == "max") { pushArgs(); for (int i = 0; i + 1 < static_cast(m_arguments.size()); ++i) - m_pusher.push(-2 + 1, "MAX"); + m_pusher << "MAX"; } else if (_node.memberName() == "min") { pushArgs(); for (int i = 0; i + 1 < static_cast(m_arguments.size()); ++i) - m_pusher.push(-2 + 1, "MIN"); + m_pusher << "MIN"; } else if (_node.memberName() == "minmax") { pushArgs(); - m_pusher.push(-2 + 2, "MINMAX"); + m_pusher << "MINMAX"; } else if (isIn(_node.memberName(), "divr", "divc")) { 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.exchange(1); - m_pusher.push(-3 + 1, "MUL" + boost::to_upper_copy(_node.memberName())); + m_pusher << "MUL" + boost::to_upper_copy(_node.memberName()); } else { - m_pusher.push(-2 + 1, boost::to_upper_copy(_node.memberName())); + m_pusher << boost::to_upper_copy(_node.memberName()); } } else if (isIn(_node.memberName(), "muldiv", "muldivr", "muldivc")) { pushArgs(); - m_pusher.push(-3 + 1, boost::to_upper_copy(_node.memberName())); + m_pusher << boost::to_upper_copy(_node.memberName()); if (!m_pusher.ctx().ignoreIntegerOverflow()) { m_pusher.checkFit(m_retType); } } else if (_node.memberName() == "divmod") { pushArgs(); - m_pusher.push(-2 + 2, "DIVMOD"); + m_pusher << "DIVMOD"; } else if (_node.memberName() == "muldivmod") { pushArgs(); - m_pusher.push(-3 + 2, "MULDIVMOD"); + m_pusher << "MULDIVMOD"; if (!m_pusher.ctx().ignoreIntegerOverflow()) { m_pusher.exchange(1); m_pusher.checkFit(retTuple->components().at(0)); @@ -2772,7 +3029,7 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { } } else if (_node.memberName() == "abs") { pushArgs(); - m_pusher.push(-1 + 1, "ABS"); + m_pusher << "ABS"; if (!m_pusher.ctx().ignoreIntegerOverflow()) { m_pusher.checkFit(m_retType); } @@ -2784,13 +3041,13 @@ void FunctionCallCompiler::mathFunction(const MemberAccess &_node) { if (value < 0 || value >= 256) { cast_error(m_functionCall, "Second argument must be in the range 1 - 255."); } - m_pusher.push(-1 + 1, "MODPOW2 " + value.value().str()); + m_pusher << "MODPOW2 " + value->str(); } else { cast_error(m_functionCall, "Second argument must be a constant integer."); } } else if (_node.memberName() == "sign") { pushArgs(); - m_pusher.push(-1 + 1, "SGN"); + m_pusher << "SGN"; } else { cast_error(m_functionCall, "Unsupported function call"); } @@ -2799,13 +3056,13 @@ 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())) { - // 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); - return true; + if (to(&_node.expression()) && m_funcType->hasDeclaration()) { + // 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); + return true; } return false; } @@ -2813,7 +3070,7 @@ bool FunctionCallCompiler::checkBaseContractCall(MemberAccess const &_node, Type bool FunctionCallCompiler::checkAddressThis() { // compile "address(this)" if (isAddressThis(&m_functionCall)) { - m_pusher.push(+1, "MYADDR"); + m_pusher << "MYADDR"; return true; } return false; @@ -2838,11 +3095,11 @@ void FunctionCallCompiler::typeConversion() { m_pusher.pushSlice("x" + StrUtils::binaryStringToSlice(StrUtils::literalToSliceAddress(literal))); } else { acceptExpr(m_arguments.at(0).get()); // it's correct - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1 + 1, "STSLICECONST x801_"); // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 // 10 0 00000000 1 = 801 - m_pusher.push(-1, "STU 256"); // address:bits256 - m_pusher.push(-1 + 1, "ENDC"); - m_pusher.push(-1 + 1, "CTOS"); + 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"; } break; } @@ -2867,20 +3124,20 @@ void FunctionCallCompiler::typeConversion() { } else if (auto enumDef = to(identifier->annotation().referencedDeclaration)) { const auto& value = ExprUtils::constValue(*m_arguments[0]); if (value.has_value()) { - m_pusher.push(+1, "PUSHINT " + value.value().str()); + 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.push(-1, "GEQ"); + 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.push(0, "UFITS 256"); // checks whether value >= 0 + m_pusher << "UFITS 256"; // checks whether value >= 0 } return; @@ -2904,16 +3161,26 @@ bool FunctionCallCompiler::checkLocalFunctionOrLibCall(const Identifier *identif int ret = m_funcType->returnParameterTypes().size(); m_pusher.pushInlineFunction(body, take, ret); } else { - Declaration const& funDecl = m_funcType->declaration(); - ContractDefinition const* contractDecl = funDecl.annotation().contract; - std::string name; - auto fd = to(&funDecl); + ContractDefinition const* contractDecl = functionDefinition->annotation().contract; if (contractDecl && contractDecl->isLibrary()) { - name = TVMCompilerContext::getLibFunctionName(fd, false); + std::string name = TVMCompilerContext::getLibFunctionName(functionDefinition, false); + m_pusher.pushCallOrCallRef(name, m_funcType); + } else if (functionDefinition->isInlineAssembly()) { + int take = functionDefinition->parameters().size(); + int ret = functionDefinition->returnParameters().size(); + std::vector lines; + for(ASTPointer const& s : functionDefinition->body().statements()) { + auto assembly = to(s.get()); + for (ASTPointer const& line : assembly->lines()) { + auto str = to(line.get()); + lines.push_back(str->value()); + } + } + m_pusher.push(createNode(lines, take, ret, false)); } else { - name = m_pusher.ctx().getFunctionInternalName(functionDefinition, false); + std::string name = m_pusher.ctx().getFunctionInternalName(functionDefinition, false); + m_pusher.pushCallOrCallRef(name, m_funcType); } - m_pusher.pushCallOrCallRef(name, m_funcType); } return true; } @@ -2944,12 +3211,12 @@ bool FunctionCallCompiler::checkSolidityUnits() { } case FunctionType::Kind::BitSize: { pushArgs(); - m_pusher.push(-1 + 1, "BITSIZE"); + m_pusher << "BITSIZE"; return true; } case FunctionType::Kind::UBitSize: { pushArgs(); - m_pusher.push(-1 + 1, "UBITSIZE"); + m_pusher << "UBITSIZE"; return true; } @@ -2958,9 +3225,9 @@ bool FunctionCallCompiler::checkSolidityUnits() { Type const* arg = m_arguments.at(0)->annotation().type; auto arrType = to(arg); if (arrType && arrType->isByteArrayOrString()) { - m_pusher.push(0, "CTOS"); + m_pusher << "CTOS"; } - m_pusher.push(0, "SHA256U"); + m_pusher << "SHA256U"; return true; } @@ -2974,7 +3241,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { {{TvmConst::int_msg_info::dest, m_arguments[0].get()}}, constParams, nullptr, - [&]() { m_pusher.push(+1, "PUSHINT " + toString(TvmConst::SENDRAWMSG::SelfDestruct)); }, + [&]() { m_pusher << "PUSHINT " + toString(TvmConst::SENDRAWMSG::SelfDestruct); }, false, 0, nullptr); return true; } @@ -3000,7 +3267,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { pushArgAndConvert(1); if (!exceptionCode.has_value()) { m_pusher.pushInt(2); - m_pusher.push(-2 + 1, "MAX"); + m_pusher << "MAX"; } pushArgAndConvert(0); if (m_arguments.size() == 3) @@ -3034,7 +3301,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { pushArgAndConvert(0); if (!exceptionCode.has_value()) { m_pusher.pushInt(2); - m_pusher.push(-2 + 1, "MAX"); + m_pusher << "MAX"; } m_pusher._throw(withArg ? "THROWARGANY" : "THROWANY"); } @@ -3070,7 +3337,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { // create new vector(TvmBuilder) m_pusher.pushDefaultValue(TypeProvider::tvmtuple(TypeProvider::tvmbuilder())); // create new builder to store data in it - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; auto pushConstStr = [&](const string& constStr) { if (!constStr.empty()) { @@ -3109,20 +3376,20 @@ bool FunctionCallCompiler::checkSolidityUnits() { auto isInt = dynamic_cast(mt); acceptExpr(m_arguments[it + 1].get()); if (isInt->isSigned()) - m_pusher.push(0, "ABS"); + m_pusher << "ABS"; m_pusher.pushInt(width); - m_pusher.push(+1, leadingZeroes ? "TRUE" : "FALSE"); + m_pusher << (leadingZeroes ? "TRUE" : "FALSE"); if (isHex) { if (isLower) - m_pusher.push(+1, "TRUE"); + m_pusher << "TRUE"; else - m_pusher.push(+1, "FALSE"); + m_pusher << "FALSE"; } if (isInt->isSigned()) { acceptExpr(m_arguments[it + 1].get()); - m_pusher.push(0, "ISNEG"); + m_pusher << "ISNEG"; } else { - m_pusher.push(+1, "FALSE"); + m_pusher << "FALSE"; } // stack: vector(TvmBuilder) builder abs(number) width leadingZeroes addMinus if (isHex) { @@ -3146,7 +3413,7 @@ bool FunctionCallCompiler::checkSolidityUnits() { // stack: vector(TvmBuilder) builder acceptExpr(m_arguments[it + 1].get()); // stack: vector(TvmBuilder) builder string(cell) - m_pusher.push(0, "CTOS"); + m_pusher << "CTOS"; // stack: vector(TvmBuilder) builder string(slice) m_pusher.pushMacroCallInCallRef(3, 2, "storeStringInBuilders_macro"); // stack: vector(TvmBuilder) builder @@ -3195,7 +3462,7 @@ bool FunctionCallCompiler::checkLocalFunctionOrLibCallOrFuncVarCall() { // Local variable of functional type acceptExpr(expr); - int returnCnt = m_funcType->returnParameterTypes().size(); + int returnCnt = m_funcType->returnParameterTypes().size(); int paramCnt = m_funcType->parameterTypes().size(); m_pusher.pushC3(); m_pusher.execute(paramCnt + 2, returnCnt); @@ -3348,25 +3615,25 @@ void FunctionCallCompiler::deployNewContract( // stack: stateInit m_pusher.pushS(0); - m_pusher.push(-1 + 1, "HASHCU"); // stack: stateInit hash + m_pusher << "HASHCU"; // stack: stateInit hash if (wid.index() == 0) { int8_t w = std::get<0>(wid); std::string binWID = "100"; binWID += StrUtils::toBitString(u256(w), 8); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1 + 1, "STSLICECONST x" + StrUtils::binaryStringToSlice(binWID)); + m_pusher << "NEWC"; + m_pusher << "STSLICECONST x" + StrUtils::binaryStringToSlice(binWID); } else { std::get<1>(wid)(); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1 + 1, "STSLICECONST x9_"); // addr_std$10 anycast:(Maybe Anycast) // 10 0 1 = 9 - m_pusher.push(-1, "STI 8"); // workchain_id:int8 + m_pusher << "NEWC"; + m_pusher << "STSLICECONST x9_"; // addr_std$10 anycast:(Maybe Anycast) // 10 0 1 = 9 + m_pusher << "STI 8"; // workchain_id:int8 } - m_pusher.push(-1, "STU 256"); // address:bits256 + m_pusher << "STU 256"; // address:bits256 bool isDestBuilder = !m_isCurrentResultNeeded; if (!isDestBuilder) { - m_pusher.push(-1 + 1, "ENDC"); - m_pusher.push(-1 + 1, "CTOS"); + m_pusher << "ENDC"; + m_pusher << "CTOS"; } // stack: arg[n-1], ..., arg[1], arg[0], stateInit, destAddress @@ -3392,7 +3659,7 @@ void FunctionCallCompiler::deployNewContract( m_pusher.stones(1); // stateInit builder m_pusher.pushS(1); checkStateInit(); - m_pusher.push(-1, "STREF"); + m_pusher << "STREF"; }; std::set isParamOnStack; @@ -3419,35 +3686,35 @@ void FunctionCallCompiler::checkStateInit() { // library:(HashmapE 256 SimpleLib) = StateInit; m_pusher.startContinuation(); - m_pusher.push(0, "CTOS"); + m_pusher << "CTOS"; // split_depth:(Maybe (## 5)) - m_pusher.push(+1, "LDI 1"); + m_pusher << "LDI 1"; m_pusher.exchange(1); - m_pusher.push(-1, ""); // fix stack: drop condition + m_pusher.fixStack(-1); // fix stack: drop condition m_pusher.startContinuation(); - m_pusher.push(+1, "LDI 5"); + m_pusher << "LDI 5"; m_pusher.dropUnder(1, 1); m_pusher.endContinuation(); m_pusher._if(); // special:(Maybe TickTock) - m_pusher.push(+1, "LDI 1"); + m_pusher << "LDI 1"; m_pusher.exchange(1); - m_pusher.push(-1, ""); // fix stack: drop condition + m_pusher.fixStack(-1); // fix stack: drop condition m_pusher.startContinuation(); // tick_tock$_ tick:Bool tock:Bool = TickTock; - m_pusher.push(+1, "LDI 2"); + m_pusher << "LDI 2"; m_pusher.dropUnder(1, 1); m_pusher.endContinuation(); m_pusher._if(); // code:(Maybe ^Cell) data:(Maybe ^Cell) // library:(HashmapE 256 SimpleLib) - m_pusher.push(+1, "LDOPTREF"); - m_pusher.push(+1, "LDOPTREF"); - m_pusher.push(+1, "LDDICT"); - m_pusher.push(-1, "ENDS"); + m_pusher << "LDOPTREF"; + m_pusher << "LDOPTREF"; + m_pusher << "LDDICT"; + m_pusher << "ENDS"; m_pusher.drop(3); m_pusher.pushRefContAndCallX(1, 0, false); @@ -3506,7 +3773,7 @@ void FunctionCallCompiler::honestArrayCreation(bool onlyDict) { m_pusher.pushS(3); // N value iter dict N solAssert(stackSize + 5 == m_pusher.stackSize(), ""); - m_pusher.push(-1, ""); // fix stack: drop replay iterator + m_pusher.fixStack(-1); // fix stack: drop replay iterator solAssert(stackSize + 4 == m_pusher.stackSize(), ""); { // N value iter dict @@ -3564,21 +3831,21 @@ void FunctionCallCompiler::buildStateInit(std::map 0) { exprs.at(StateInitMembers::SplitDepth)(); - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; m_pusher.stones(1); - m_pusher.push(-2 + 1, "STU 5"); + m_pusher << "STU 5"; m_pusher.stzeroes(1); } else { - m_pusher.push(+1, "NEWC"); + m_pusher << "NEWC"; // stake: data code builder - m_pusher.push(-1 + 1, "STSLICECONST x2_"); // no split_depth and no special // 0 0 + m_pusher << "STSLICECONST x2_"; // no split_depth and no special // 0 0 } // stake: data code builder - m_pusher.push(-2 + 1, "STOPTREF"); // store code - m_pusher.push(-2 + 1, "STOPTREF"); // store data - m_pusher.push(0, "STZERO"); // store library - m_pusher.push(0, "ENDC"); + m_pusher << "STOPTREF"; // store code + m_pusher << "STOPTREF"; // store data + m_pusher << "STZERO"; // store library + m_pusher << "ENDC"; // stack: stateInit solAssert(ss + 1 == m_pusher.stackSize(), ""); } @@ -3651,7 +3918,7 @@ void FunctionCallCompiler::compileLog() auto literal = to(logstr); if (literal && literal->value().size() < 16) { std::string hexStr = stringToBytes(literal->value()); - m_pusher.push(0, "PRINTSTR x" + hexStr); + m_pusher << "PRINTSTR x" + hexStr; } else { pushArgs(); m_pusher.pushLog(); @@ -3679,7 +3946,7 @@ void FunctionCallCompiler::cellBitRefQty(bool forCell) { m_pusher.pushAsym("SDATASIZEQ"); m_pusher.startContinuation(); - m_pusher.push(-3 + 1, "TUPLE 3"); + m_pusher << "TUPLE 3"; m_pusher.endContinuation(); m_pusher.startContinuation(); diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.hpp b/compiler/libsolidity/codegen/TVMFunctionCall.hpp index f6172927..c29fd05c 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.hpp @@ -46,7 +46,8 @@ class FunctionCallCompiler { void mappingEmpty(); bool structMethodCall(); void superFunctionCall(MemberAccess const& _node); - void typeTypeMethods(MemberAccess const& _node); + void userDefinedValueMethods(MemberAccess const& _node); + void addressMethods(MemberAccess const& _node); bool libraryCall(MemberAccess const& ma); bool checkForTvmDeployMethods(MemberAccess const& _node, Type::Category category); void tvmBuildIntMsg(); @@ -118,7 +119,7 @@ class FunctionCallCompiler { const Expression *callbackid, const Expression *onerrorid, const Expression *stateInit, - const Expression *signBoxHandle, + const Expression *signBoxHandle, const Expression *abiVer, const Expression *flags, const CallableDeclaration *functionDefinition, diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp index f5f52fcd..27ea4b12 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp @@ -75,25 +75,25 @@ ast_vec TVMFunctionCompiler::functionModifiers() { } void TVMFunctionCompiler::endContinuation2(const bool doDrop) { - int delta = m_pusher.stackSize() - m_controlFlowInfo.back().stackSize; + int delta = m_pusher.stackSize() - m_controlFlowInfo.back().stackSize(); if (doDrop) { m_pusher.drop(delta); } else { - m_pusher.push(-delta, ""); // fix stack + m_pusher.fixStack(-delta); // fix stack } m_pusher.endContinuation(); } bool TVMFunctionCompiler::hasLoop() const { return std::any_of(m_controlFlowInfo.begin(), m_controlFlowInfo.end(), [](const ControlFlowInfo& info){ - return info.isLoop; + return info.isLoop(); }); } std::optional TVMFunctionCompiler::lastAnalyzeFlag() const { int n = m_controlFlowInfo.size(); for (int i = n - 1; i >= 0; --i) { - if (m_controlFlowInfo.at(i).doAnalyzeFlag) { + if (m_controlFlowInfo.at(i).hasAnalyzeFlag()) { return m_controlFlowInfo.at(i); } } @@ -103,7 +103,7 @@ std::optional TVMFunctionCompiler::lastAnalyzeFlag() const { std::optional TVMFunctionCompiler::lastLoop() const { int n = m_controlFlowInfo.size(); for (int i = n - 1; i >= 0; --i) { - if (m_controlFlowInfo.at(i).isLoop) { + if (m_controlFlowInfo.at(i).isLoop()) { return m_controlFlowInfo.at(i); } } @@ -113,10 +113,10 @@ std::optional TVMFunctionCompiler::lastLoop() const { bool TVMFunctionCompiler::lastAnalyzerBeforeLoop() const { int n = m_controlFlowInfo.size(); for (int i = n - 1; i >= 0; --i) { - if (m_controlFlowInfo.at(i).isLoop) { + if (m_controlFlowInfo.at(i).isLoop()) { return false; } - if (m_controlFlowInfo.at(i).doAnalyzeFlag) { + if (m_controlFlowInfo.at(i).hasAnalyzeFlag()) { return true; } } @@ -149,15 +149,15 @@ Pointer TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { StackPusher pusher{&ctx}; pusher.pushRoot(); - pusher.push(-1 + 1, "CTOS"); - pusher.push(-1 + 2, "LDU 256 ; pubkey c4"); + pusher << "CTOS"; + pusher << "LDU 256 ; pubkey c4"; if (pusher.ctx().storeTimestampInC4()) { - pusher.push(+1, "LDU 64 ; pubkey timestamp c4"); + pusher << "LDU 64 ; pubkey timestamp c4"; } - pusher.push(+1, "LDU 1 ; ctor flag"); + pusher << "LDU 1 ; ctor flag"; pusher.dropUnder(1, 1); // ignore if (pusher.ctx().usage().hasAwaitCall()) { - pusher.push(-1 + 2, "LDI 1 ; await flag"); + pusher << "LDI 1 ; await flag"; pusher.dropUnder(1, 1); } if (!pusher.ctx().notConstantStateVariables().empty()) { @@ -185,14 +185,14 @@ TVMFunctionCompiler::generateC4ToC7(TVMCompilerContext& ctx) { } solAssert(ss - 1 == pusher.stackSize(), ""); } else { - pusher.push(-1, "ENDS"); + pusher << "ENDS"; } if (pusher.ctx().storeTimestampInC4()) { pusher.setGlob(TvmConst::C7::ReplayProtTime); } - pusher.push(+1, ""); // fix stack + pusher.fixStack(+1); // fix stack pusher.setGlob(TvmConst::C7::TvmPubkey); Pointer block = pusher.getBlock(); @@ -206,9 +206,9 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { TVMFunctionCompiler funCompiler{pusher, pusher.ctx().getContract()}; pusher.pushRoot(); - pusher.push(-1 + 1, "CTOS"); - pusher.push(-1 + 1, "SBITS"); - pusher.push(-1 + 1, "GTINT 1"); + pusher << "CTOS"; + pusher << "SBITS"; + pusher << "GTINT 1"; pusher.startContinuation(); pusher.pushCall(0, 0, "c4_to_c7"); @@ -217,8 +217,8 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { pusher.startContinuation(); pusher.pushInt(0); pusher.pushRoot(); - pusher.push(0, "CTOS"); - pusher.push(0, "PLDDICT ; D"); + pusher << "CTOS"; + pusher << "PLDDICT ; D"; int varQty = 0; bool tooMuchStateVars = pusher.ctx().tooMuchStateVariables(); @@ -255,9 +255,9 @@ TVMFunctionCompiler::generateC4ToC7WithInitMemory(TVMCompilerContext& ctx) { pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::NoPubkeyInC4)); pusher.endOpaque(3, 1); - pusher.push(0, "PLDU 256"); + pusher << "PLDU 256"; pusher.setGlob(TvmConst::C7::TvmPubkey); - pusher.push(+1, "PUSHINT 0 ; timestamp"); + pusher << "PUSHINT 0 ; timestamp"; pusher.setGlob(TvmConst::C7::ReplayProtTime); for (VariableDeclaration const *variable: pusher.ctx().notConstantStateVariables()) { @@ -312,12 +312,12 @@ TVMFunctionCompiler::generateMacro( 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.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()); + funCompiler.pushLocation(*function, true); + int take = function->parameters().size(); + int ret = function->returnParameters().size(); + return createNode(take, ret, name, Function::FunctionType::Macro, pusher.getBlock()); } Pointer @@ -327,7 +327,7 @@ TVMFunctionCompiler::generateOnCodeUpgrade(TVMCompilerContext& ctx, FunctionDefi funCompiler.visitFunctionWithModifiers(); pusher.pushMacroCallInCallRef(0, 0, "c7_to_c4"); - pusher.push(0, "COMMIT"); + pusher << "COMMIT"; pusher._throw("THROW 0"); int take = function->parameters().size(); const std::string name = ctx.getFunctionInternalName(function, false); @@ -391,17 +391,17 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef Pointer block; TVMFunctionCompiler funCompiler{pusher, 0, function, false, false, 0}; - pusher.push(+1, ""); // slice with args - pusher.push(+1, ""); // functionId + pusher.fixStack(+1); // slice with args + pusher.fixStack(+1); // functionId pusher.drop(); // drop function id pusher.checkCtorCalled(); funCompiler.pushC4ToC7IfNeed(); - funCompiler.pushLocation(*function); + funCompiler.pushLocation(*function); const bool isResponsible = function->isResponsible(); if (isResponsible) { const int saveStakeSize = pusher.stackSize(); - pusher.push(+1, "LDU 32"); // callbackId slice + pusher << "LDU 32"; // callbackId slice pusher.getGlob(TvmConst::C7::ReturnParams); // callbackId slice c7[4] pusher.blockSwap(1, 2); // slice c7[4] callbackId pusher.setIndexQ(TvmConst::C7::ReturnParam::CallbackFunctionId); // slice c7[4] @@ -409,7 +409,7 @@ TVMFunctionCompiler::generatePublicFunction(TVMCompilerContext& ctx, FunctionDef solAssert(saveStakeSize == pusher.stackSize(), ""); } funCompiler.decodeFunctionParamsAndInitVars(isResponsible); - funCompiler.pushLocation(*function, true); + funCompiler.pushLocation(*function, true); int paramQty = function->parameters().size(); int retQty = function->returnParameters().size(); @@ -450,16 +450,16 @@ void TVMFunctionCompiler::generateFunctionWithModifiers( Pointer TVMFunctionCompiler::generateGetter(StackPusher &pusher, VariableDeclaration const* vd) { TVMFunctionCompiler funCompiler{pusher, nullptr}; - pusher.push(+2, ""); // stack: functionId msgBody + pusher.fixStack(+2); // stack: functionId msgBody pusher.drop(); // drop function id - pusher.push(-1, "ENDS"); + pusher << "ENDS"; pusher.pushMacroCallInCallRef(0, 0, "c4_to_c7"); pusher.getGlob(vd); // check ext msg pusher.pushS(1); pusher.startContinuation(); - pusher.push(-1, ""); // fix stack + pusher.fixStack(-1); // fix stack const std::vector outputs = {vd}; auto appendBody = [&](int builderSize) { @@ -568,7 +568,7 @@ void TVMFunctionCompiler::emitOnPublicFunctionReturn() { } m_pusher.pushS(m_pusher.stackSize()); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack bool isResponsible = m_pusher.ctx().getCurrentFunction()->isResponsible(); // emit for ext @@ -589,9 +589,9 @@ void TVMFunctionCompiler::emitOnPublicFunctionReturn() { // get external address of sender m_pusher.pushS(m_pusher.stackSize() + 2); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "LDU 2"); - m_pusher.push(+1, "LDMSGADDR"); + m_pusher << "CTOS"; + m_pusher << "LDU 2"; + m_pusher << "LDMSGADDR"; m_pusher.drop(); m_pusher.popS(1); @@ -603,7 +603,7 @@ void TVMFunctionCompiler::emitOnPublicFunctionReturn() { nullptr, StackPusher::MsgType::ExternalOut ); - m_pusher.push(params.size(), ""); // fix stack + m_pusher.fixStack(params.size()); // fix stack } m_pusher.endContinuation(); @@ -677,8 +677,8 @@ void TVMFunctionCompiler::visitModifierOrFunctionBlock(Block const &body, int ar } acceptBody(body, {{argQty, nameRetQty}}); if (locationReturn == LocationReturn::Last) { - m_pusher.pollLastRetOpcode(); - } + m_pusher.pollLastRetOpcode(); + } if (doPushContinuation) { pushLocation(*m_function); if (m_isLibraryWithObj && m_currentModifier == static_cast(m_function->modifiers().size())) { @@ -686,7 +686,7 @@ void TVMFunctionCompiler::visitModifierOrFunctionBlock(Block const &body, int ar solAssert(argQty > 0, ""); } m_pusher.pushContAndCallX(argQty, retQty, false); - pushLocation(*m_function, true); + pushLocation(*m_function, true); } } @@ -727,19 +727,19 @@ void TVMFunctionCompiler::visitFunctionWithModifiers() { } // TODO move to function - solAssert(!m_function->externalMsg() || !m_function->internalMsg(), ""); + solAssert(!m_function->isExternalMsg() || !m_function->isInternalMsg(), ""); - if (m_function->externalMsg() || m_function->internalMsg()) + if (m_function->isExternalMsg() || m_function->isInternalMsg()) m_pusher.push(createNode(std::vector{ "DEPTH", "ADDCONST -5", "PICK", }, 0, 1, true)); - if (m_function->externalMsg()) { - m_pusher.push(-1 + 1, "EQINT -1"); + if (m_function->isExternalMsg()) { + m_pusher << "EQINT -1"; m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::ByExtMsgOnly)); - } else if (m_function->internalMsg()) { + } else if (m_function->isInternalMsg()) { m_pusher._throw("THROWIF " + toString(TvmConst::RuntimeException::ByIntMsgOnly)); } } @@ -751,7 +751,7 @@ void TVMFunctionCompiler::visitFunctionWithModifiers() { StackPusher pusher = m_pusher; pusher.clear(); - pusher.push(-modSize, ""); // fix stack + pusher.fixStack(-modSize); // fix stack TVMFunctionCompiler funCompiler{pusher, m_currentModifier, m_function, m_isLibraryWithObj, m_pushArgs, 0}; funCompiler.visitModifierOrFunctionBlock(m_function->body(), argQty, retQty, nameRetQty); @@ -850,7 +850,7 @@ bool TVMFunctionCompiler::visit(VariableDeclarationStatement const &_variableDec m_pusher.getStack().change(-varQty + bad); for (const ASTPointer& d : variables) { if (d != nullptr) { - m_pusher.getStack().add(d.get(), true); + m_pusher.getStack().add(d.get(), true); } } m_pusher.ensureSize(saveStackSize + varQty - bad, "VariableDeclarationStatement", &_variableDeclarationStatement); @@ -879,7 +879,7 @@ void TVMFunctionCompiler::acceptBody(Block const& _block, std::optional(_block.statements().back().get()) == nullptr) { m_pusher.drop(delta); } else { - m_pusher.push(-delta, ""); // fix stack + m_pusher.fixStack(-delta); // fix stack } } @@ -902,37 +902,48 @@ bool TVMFunctionCompiler::visit(Block const& _block) { bool TVMFunctionCompiler::visit(ExpressionStatement const& _statement) { if (!*_statement.expression().annotation().isPure) { - pushLocation(_statement); + pushLocation(_statement); auto savedStackSize = m_pusher.stackSize(); acceptExpr(&_statement.expression(), false); // TODO DELETE FIX ME // m_pusher.ensureSize(savedStackSize, _statement.location().text()); m_pusher.ensureSize(savedStackSize, ""); - pushLocation(_statement, true); + pushLocation(_statement, true); } return false; } bool TVMFunctionCompiler::visit(TryStatement const& _tryState) { - // try body - m_pusher.startContinuation(); - const int startStackSize = m_pusher.stackSize(); - _tryState.body().accept(*this); - m_pusher.drop(m_pusher.stackSize() - startStackSize); - m_pusher.endContinuation(); + // return flag + const int stackSize = m_pusher.stackSize(); + CFAnalyzer ci{_tryState}; + ControlFlowInfo info = beforeTryOrIfCheck(ci); + m_controlFlowInfo.push_back(info); // try body m_pusher.startContinuation(); - for (ASTPointer const& variable : _tryState.clause().parameters()->parameters()) { + const int startStackSize = m_pusher.stackSize(); + _tryState.body().accept(*this); + m_pusher.drop(m_pusher.stackSize() - startStackSize); + m_pusher.endContinuation(); + + // try body + m_pusher.startContinuation(); + for (ASTPointer const& variable : _tryState.clause().parameters()->parameters()) { m_pusher.getStack().add(variable.get(), true); - } + } _tryState.clause().block().accept(*this); m_pusher.drop(m_pusher.stackSize() - startStackSize); m_pusher.endContinuation(); - m_pusher.tryOpcode(); + m_pusher.tryOpcode(ci.canReturn() || ci.canBreak()); + + // bottom + afterTryOrIfCheck(info); + + solAssert(stackSize == m_pusher.stackSize(), "TryStatement fail"); - return false; + return false; } bool TVMFunctionCompiler::visit(IfStatement const &_ifStatement) { @@ -944,22 +955,14 @@ bool TVMFunctionCompiler::visit(IfStatement const &_ifStatement) { CFAnalyzer(_ifStatement.trueStatement()).doThatAlways() && CFAnalyzer(*_ifStatement.falseStatement()).doThatAlways() : CFAnalyzer(_ifStatement.trueStatement()).doThatAlways(); - ControlFlowInfo info{}; - info.isLoop = false; - if (canUseJmp) { - info.doAnalyzeFlag = false; - } else { - if (ci.canContinue() || (!hasLoop() && ci.canReturn())) { - m_pusher.declRetFlag(); - info.doAnalyzeFlag = true; - } - } - info.stackSize = m_pusher.stackSize(); + ControlFlowInfo info = canUseJmp ? + ControlFlowInfo{m_pusher.stackSize(), false, false} : + beforeTryOrIfCheck(ci); m_controlFlowInfo.push_back(info); // condition acceptExpr(&_ifStatement.condition(), true); - m_pusher.push(-1, ""); // drop condition + m_pusher.fixStack(-1); // drop condition // if m_pusher.startContinuation(); _ifStatement.trueStatement().accept(*this); @@ -983,24 +986,11 @@ bool TVMFunctionCompiler::visit(IfStatement const &_ifStatement) { } else { m_pusher._if(); } - pushLocation(_ifStatement, true); + pushLocation(_ifStatement, true); } - m_controlFlowInfo.pop_back(); - // bottom - if (info.doAnalyzeFlag) { - std::optional lastAnalyze = lastAnalyzeFlag(); - m_pusher.startOpaque(); - if (lastAnalyze.has_value()) { - m_pusher.pushS(0); - m_pusher.ifret(); - m_pusher.drop(); - } else { - m_pusher.ifret(); - } - m_pusher.endOpaque(1, 0); - } + afterTryOrIfCheck(info); m_pusher.ensureSize(saveStackSize, ""); return false; @@ -1030,8 +1020,8 @@ void TVMFunctionCompiler::doWhile(WhileStatement const &_whileStatement) { // condition acceptExpr(&_whileStatement.condition(), true); - m_pusher.push(0, "NOT"); - m_pusher.push(-1, ""); // drop condition + m_pusher << "NOT"; + m_pusher.fixStack(-1); // drop condition m_pusher.endContinuation(); m_pusher.until(ci->canBreak() || ci->canReturn()); @@ -1039,23 +1029,23 @@ void TVMFunctionCompiler::doWhile(WhileStatement const &_whileStatement) { m_controlFlowInfo.pop_back(); // bottom - afterLoopCheck(ci, 0, info.doAnalyzeFlag); + afterLoopCheck(ci, 0, info.hasAnalyzeFlag()); m_pusher.ensureSize(saveStackSize, ""); } void TVMFunctionCompiler::visitForOrWhileCondition( - const std::function& pushCondition + const std::function& pushCondition ) { int stackSize = m_pusher.stackSize(); m_pusher.startContinuation(); if (pushCondition) { pushCondition(); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack } else { - m_pusher.push(+1, "TRUE"); - m_pusher.push(-1, ""); // fix stack + m_pusher << "TRUE"; + m_pusher.fixStack(-1); // fix stack } m_pusher.endContinuation(); m_pusher.ensureSize(stackSize, "visitForOrWhileCondition"); @@ -1071,7 +1061,7 @@ void TVMFunctionCompiler::afterLoopCheck(const std::unique_ptr& ci, m_pusher.pushS(0); } if (ci->canBreak() || ci->canContinue()) { - m_pusher.push(0, "EQINT " + toString(TvmConst::RETURN_FLAG)); + m_pusher << "EQINT " + toString(TvmConst::RETURN_FLAG); } loopFlag.has_value() ? m_pusher.ifRetAlt() : m_pusher.ifret(); if (analyzeFlag.has_value()) { @@ -1082,6 +1072,32 @@ void TVMFunctionCompiler::afterLoopCheck(const std::unique_ptr& ci, m_pusher.drop(loopVarQty); } +ControlFlowInfo TVMFunctionCompiler::beforeTryOrIfCheck(CFAnalyzer const& ci) { + bool hasAnalyzeFlag{}; + bool isLoop = false; + if (ci.canContinue() || (!hasLoop() && ci.canReturn())) { + m_pusher.declRetFlag(); + hasAnalyzeFlag = true; + } + return ControlFlowInfo{m_pusher.stackSize(), hasAnalyzeFlag, isLoop}; +} + +void TVMFunctionCompiler::afterTryOrIfCheck(ControlFlowInfo const& info) { + m_controlFlowInfo.pop_back(); + if (info.hasAnalyzeFlag()) { + std::optional lastAnalyze = lastAnalyzeFlag(); + m_pusher.startOpaque(); + if (lastAnalyze.has_value()) { + m_pusher.pushS(0); + m_pusher.ifret(); + m_pusher.drop(); + } else { + m_pusher.ifret(); + } + m_pusher.endOpaque(1, 0); + } +} + bool TVMFunctionCompiler::visit(WhileStatement const &_whileStatement) { int saveStackSizeForWhile = m_pusher.stackSize(); @@ -1098,7 +1114,7 @@ bool TVMFunctionCompiler::visit(WhileStatement const &_whileStatement) { // condition if (_whileStatement.loopType() == WhileStatement::LoopType::REPEAT) { acceptExpr(&_whileStatement.condition()); - m_pusher.push(-1, ""); + m_pusher.fixStack(-1); } else { std::function pushCondition = [&]() { acceptExpr(&_whileStatement.condition(), true); @@ -1122,7 +1138,7 @@ bool TVMFunctionCompiler::visit(WhileStatement const &_whileStatement) { m_controlFlowInfo.pop_back(); // bottom - afterLoopCheck(ci, 0, info.doAnalyzeFlag); + afterLoopCheck(ci, 0, info.hasAnalyzeFlag()); m_pusher.ensureSize(saveStackSizeForWhile, ""); @@ -1163,7 +1179,7 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { solAssert(vds->declarations().size() == 1, ""); auto iterVar = vds->declarations().at(0).get(); if (arrayType->isByteArrayOrString()) { - m_pusher.push(0, "CTOS"); + m_pusher << "CTOS"; m_pusher.pushNull(); // stack: dict value loopVarQty = 2; } else { @@ -1181,15 +1197,15 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { dictMinMax.minOrMax(true); // stack: dict minKey(private) minKey(pub) value - m_pusher.push(-2, ""); // fix stack + m_pusher.fixStack(-2); // fix stack auto iterKey = vds->declarations().at(0).get(); auto iterVal = vds->declarations().at(1).get(); if (iterKey == nullptr) - m_pusher.push(+1, ""); + m_pusher.fixStack(+1); else m_pusher.getStack().add(iterKey, true); if (iterVal == nullptr) - m_pusher.push(+1, ""); + m_pusher.fixStack(+1); else m_pusher.getStack().add(iterVal, true); @@ -1200,7 +1216,7 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { } m_pusher.ensureSize(saveStackSize + loopVarQty, "for"); - // header + // return flag auto [ci, info] = pushControlFlowFlag(_forStatement.body()); // condition @@ -1209,8 +1225,8 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { if (arrayType->isByteArrayOrString()) { // stack: cell value [flag] m_pusher.pushS(m_pusher.stackSize() - saveStackSize - 1); // stack: cell value [flag] cell - m_pusher.push(-1 + 1, "SEMPTY"); - m_pusher.push(-1 + 1, "NOT"); + m_pusher << "SEMPTY"; + m_pusher << "NOT"; } else { // stack: dict index value [flag] m_pusher.pushS(m_pusher.stackSize() - saveStackSize - 2); // stack: dict index value [flag] index @@ -1221,14 +1237,14 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { m_pusher.pushS(0); // stack: dict index value [flag] newValue newValue m_pusher.popS( m_pusher.stackSize() - saveStackSize - 3); // stack: dict index newValue [flag] newValue - m_pusher.push(-1 + 1, "ISNULL"); - m_pusher.push(-1 + 1, "NOT"); + m_pusher << "ISNULL"; + m_pusher << "NOT"; } } else if (mappingType) { // stack: dict minKey(private) minKey(pub) value [flag] m_pusher.pushS(m_pusher.stackSize() - saveStackSize - 2); - m_pusher.push(-1 + 1, "ISNULL"); - m_pusher.push(-1 + 1, "NOT"); + m_pusher << "ISNULL"; + m_pusher << "NOT"; } else { solUnimplemented(""); } @@ -1247,13 +1263,13 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { m_pusher.startOpaque(); m_pusher.pushAsym("LDUQ 8"); - m_pusher.push(+1, ""); // fix stack + m_pusher.fixStack(+1); // fix stack m_pusher.startContinuation(); // stack: cell value [flag] slice - m_pusher.push(-1 + 1, "PLDREF"); - m_pusher.push(-1 + 1, "CTOS"); - m_pusher.push(-1 + 2, "LDU 8"); - m_pusher.push(-2, ""); // fix stack + m_pusher << "PLDREF"; + m_pusher << "CTOS"; + m_pusher << "LDU 8"; + m_pusher.fixStack(-2); // fix stack m_pusher.endContinuation(); m_pusher.ifNot(); m_pusher.endOpaque(1, 2); @@ -1276,7 +1292,7 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { } else { // stack: dict 0 value [flag] m_pusher.pushS(m_pusher.stackSize() - saveStackSize - 2); // stack: dict index value [flag] index - m_pusher.push(0, "INC"); // stack: dict index value [flag] newIndex + m_pusher << "INC"; // stack: dict index value [flag] newIndex m_pusher.popS(m_pusher.stackSize() - saveStackSize - 2); // stack: dict newIndex [flag] value } } else if (mappingType) { @@ -1301,7 +1317,7 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { visitBodyOfForLoop(ci, pushStartBody, _forStatement.body(), pushLoopExpression); // bottom - afterLoopCheck(ci, loopVarQty, info.doAnalyzeFlag); + afterLoopCheck(ci, loopVarQty, info.hasAnalyzeFlag()); m_pusher.ensureSize(saveStackSize, "for"); return false; @@ -1309,14 +1325,15 @@ bool TVMFunctionCompiler::visit(ForEachStatement const& _forStatement) { std::pair, ControlFlowInfo> TVMFunctionCompiler::pushControlFlowFlag(Statement const& body) { std::unique_ptr ci = std::make_unique(body); - ControlFlowInfo info; - info.isLoop = true; - info.stackSize = -1; + bool isLoop = true; + int stackSize = -1; + bool hasAnalyzeFlag = false; if (ci->canReturn()) { m_pusher.declRetFlag(); - info.doAnalyzeFlag = true; + hasAnalyzeFlag = true; } - info.stackSize = m_pusher.stackSize(); + stackSize = m_pusher.stackSize(); + ControlFlowInfo info {stackSize, hasAnalyzeFlag, isLoop}; m_controlFlowInfo.push_back(info); return {std::move(ci), info}; } @@ -1414,7 +1431,7 @@ bool TVMFunctionCompiler::visit(ForStatement const &_forStatement) { visitBodyOfForLoop(ci, {}, _forStatement.body(), pushLoopExpression); // bottom - afterLoopCheck(ci, haveDeclLoopVar, info.doAnalyzeFlag); + afterLoopCheck(ci, haveDeclLoopVar, info.hasAnalyzeFlag()); m_pusher.ensureSize(saveStackSize, "for"); return false; @@ -1479,24 +1496,15 @@ bool TVMFunctionCompiler::visit(Return const& _return) { if (lastAnalyzeFlag().has_value()) { m_pusher.pushInt(TvmConst::RETURN_FLAG); --revertDelta; - m_pusher.push(revertDelta, ""); // fix stack + m_pusher.fixStack(revertDelta); // fix stack } else { // all continuation are run by JMPX - m_pusher.push(revertDelta, ""); // fix stack + m_pusher.fixStack(revertDelta); // fix stack } - - int i{}; - ControlFlowInfo controlFlowInfo; - for (i = static_cast(m_controlFlowInfo.size()) - 1; i >= 0; --i) { - if (m_controlFlowInfo.at(i).isLoop) { - controlFlowInfo = m_controlFlowInfo.at(i); - break; - } - } - if (i == -1) { - m_pusher.ret(); - } else { + if (hasLoop()) { m_pusher.retAlt(); + } else { + m_pusher.ret(); } m_pusher.endRetOrBreakOrCont(retCount); @@ -1504,13 +1512,13 @@ bool TVMFunctionCompiler::visit(Return const& _return) { } bool TVMFunctionCompiler::visit(Break const&) { - const int sizeDelta = m_pusher.stackSize() - lastLoop().value().stackSize; + const int sizeDelta = m_pusher.stackSize() - lastLoop().value().stackSize(); m_pusher.startContinuation(); m_pusher.drop(sizeDelta); m_pusher.retAlt(); - m_pusher.push(sizeDelta, ""); // fix stack + m_pusher.fixStack(sizeDelta); // fix stack m_pusher.endRetOrBreakOrCont(0); return false; @@ -1518,11 +1526,11 @@ bool TVMFunctionCompiler::visit(Break const&) { bool TVMFunctionCompiler::visit(Continue const&) { bool hasAnalyzer = lastAnalyzerBeforeLoop(); - const int sizeDelta = m_pusher.stackSize() - lastLoop().value().stackSize; + const int sizeDelta = m_pusher.stackSize() - lastLoop().value().stackSize(); m_pusher.startContinuation(); if (hasAnalyzer) { - m_pusher.drop(sizeDelta + (lastLoop().value().doAnalyzeFlag ? 1 : 0)); + m_pusher.drop(sizeDelta + (lastLoop().value().hasAnalyzeFlag() ? 1 : 0)); m_pusher.pushInt(TvmConst::CONTINUE_FLAG); } else { m_pusher.drop(sizeDelta); @@ -1530,7 +1538,7 @@ bool TVMFunctionCompiler::visit(Continue const&) { m_pusher.ret(); - m_pusher.push(sizeDelta, ""); // fix stack + m_pusher.fixStack(sizeDelta); // fix stack m_pusher.endRetOrBreakOrCont(0); return false; } @@ -1577,9 +1585,9 @@ void TVMFunctionCompiler::setGlobSenderAddressIfNeed() { void TVMFunctionCompiler::setCtorFlag() { m_pusher.pushRoot(); - m_pusher.push(0, "CTOS"); - m_pusher.push(0, "SBITS"); - m_pusher.push(0, "NEQINT 1"); + m_pusher << "CTOS"; + m_pusher << "SBITS"; + m_pusher << "NEQINT 1"; m_pusher.setGlob(TvmConst::C7::ConstructorFlag); } @@ -1593,10 +1601,6 @@ void TVMFunctionCompiler::setCopyleftAndTryCatch() { m_pusher.pushInt(type.value()); m_pusher << "COPYLEFT"; } - - if (m_pusher.ctx().usage().hasTryCatch()) { - m_pusher << "NEWEXCMODEL"; - } } Pointer TVMFunctionCompiler::generateMainExternal( @@ -1632,7 +1636,7 @@ Pointer TVMFunctionCompiler::generateMainExternal( } // msg_body - pusher.push(+1, "LDU 32 ; funcId body"); + pusher << "LDU 32 ; funcId body"; pusher.exchange(1); f.callPublicFunctionOrFallback(); @@ -1648,11 +1652,11 @@ void TVMFunctionCompiler::pushMsgPubkey() { if (m_pusher.ctx().pragmaHelper().hasPubkey()) { m_pusher.exchange(1); - m_pusher.push(+1, "LDU 1 ; signatureSlice hashMsgSlice hasPubkey msgSlice"); + m_pusher << "LDU 1 ; signatureSlice hashMsgSlice hasPubkey msgSlice"; m_pusher.exchange(1); // signatureSlice hashMsgSlice msgSlice hasPubkey m_pusher.startContinuation(); - m_pusher.push(+1, "LDU 256 ; signatureSlice hashMsgSlice pubkey msgSlice"); + m_pusher << "LDU 256 ; signatureSlice hashMsgSlice pubkey msgSlice"; m_pusher.exchange(3); // msgSlice hashMsgSlice pubkey signatureSlice m_pusher.exchange(1); // msgSlice hashMsgSlice signatureSlice pubkey m_pusher.endContinuation(); @@ -1681,12 +1685,12 @@ void TVMFunctionCompiler::pushMsgPubkey() { void TVMFunctionCompiler::checkSignatureAndReadPublicKey() { // msgSlice - m_pusher.push(-1 + 2, "LDU 1 ; haveSign msgSlice"); + m_pusher << "LDU 1 ; haveSign msgSlice"; m_pusher.exchange(1); m_pusher.startContinuation(); m_pusher.pushInt(512); - m_pusher.push(-2 + 2, "LDSLICEX"); + m_pusher << "LDSLICEX"; // signatureSlice msgSlice m_pusher.pushS(0); @@ -1701,11 +1705,11 @@ void TVMFunctionCompiler::checkSignatureAndReadPublicKey() { m_pusher << "ENDC"; // signatureSlice msgSlice signedCell - m_pusher.push(-1 + 1, "HASHCU"); + m_pusher << "HASHCU"; // signatureSlice msgSlice msgHash pushMsgPubkey(); // signatureSlice msgSlice msgHash pubkey - m_pusher.push(-3 + 1, "CHKSIGNU"); + m_pusher << "CHKSIGNU"; // msgSlice isSigned m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::BadSignature)); // msgSlice @@ -1714,7 +1718,7 @@ void TVMFunctionCompiler::checkSignatureAndReadPublicKey() { if (m_pusher.ctx().pragmaHelper().hasPubkey()) { // External inbound message does not have signature but have public key m_pusher.startContinuation(); - m_pusher.push(+1, "LDU 1 ; hasPubkey msgSlice"); + m_pusher << "LDU 1 ; hasPubkey msgSlice"; m_pusher.exchange(1); m_pusher._throw("THROWIF " + toString(TvmConst::RuntimeException::MessageHasNoSignButHasPubkey) + " ; msgSlice"); m_pusher.endContinuation(); @@ -1726,16 +1730,16 @@ void TVMFunctionCompiler::checkSignatureAndReadPublicKey() { void TVMFunctionCompiler::defaultReplayProtection() { // msgSlice - m_pusher.push(+1, "LDU 64 ; timestamp msgSlice"); + m_pusher << "LDU 64 ; timestamp msgSlice"; m_pusher.exchange(1); m_pusher.pushCall(1, 0, "replay_protection_macro"); } void TVMFunctionCompiler::expire() { - m_pusher.push(+1, "LDU 32 ; expireAt msgSlice"); + m_pusher << "LDU 32 ; expireAt msgSlice"; m_pusher.exchange(1); - m_pusher.push(+1, "NOW ; msgSlice expireAt now"); - m_pusher.push(-1, "GREATER ; msgSlice expireAt>now"); + m_pusher << "NOW ; msgSlice expireAt now"; + m_pusher << "GREATER ; msgSlice expireAt>now"; m_pusher._throw("THROWIFNOT " + toString(TvmConst::RuntimeException::MessageIsExpired)); } @@ -1767,29 +1771,29 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin funCompiler.setCtorFlag(); pusher.pushS(2); - pusher.push(-1 + 1, "CTOS"); + pusher << "CTOS"; // stack: int_msg_info ContactsUsageScanner const &sc = pusher.ctx().usage(); if (sc.hasMsgSender() || sc.hasResponsibleFunction() || sc.hasAwaitCall()) { - pusher.push(-1 + 2, "LDU 4 ; bounced tail"); - pusher.push(-1 + 2, "LDMSGADDR ; bounced src tail"); + pusher << "LDU 4 ; bounced tail"; + pusher << "LDMSGADDR ; bounced src tail"; pusher.drop(); if (sc.hasAwaitCall()) { pusher.pushMacroCallInCallRef(0, 0, "check_resume"); } pusher.setGlob(TvmConst::C7::SenderAddress); - pusher.push(0, "MODPOW2 1"); + pusher << "MODPOW2 1"; } else { - pusher.push(-1 + 1, "PLDU 4"); - pusher.push(-1 + 1, "MODPOW2 1"); + pusher << "PLDU 4"; + pusher << "MODPOW2 1"; } // stack: isBounced // set default params for responsible func if (sc.hasResponsibleFunction()) { pusher.getGlob(TvmConst::C7::ReturnParams); - pusher.push(+1, "TRUE"); // bounce + pusher << "TRUE"; // bounce pusher.setIndexQ(TvmConst::C7::ReturnParam::Bounce); pusher.pushInt(TvmConst::Message::DefaultMsgValue); // tons pusher.setIndexQ(TvmConst::C7::ReturnParam::Value); @@ -1804,7 +1808,7 @@ TVMFunctionCompiler::generateMainInternal(TVMCompilerContext& ctx, ContractDefin if (!isEmptyFunction(contract->onBounceFunction())) { pusher.startContinuation(); pusher.pushS(1); - pusher.push(-1 + 2, "LDSLICE 32"); + pusher << "LDSLICE 32"; pusher.dropUnder(1, 1); pusher.pushCall(0, 0, "on_bounce_macro"); pusher.endContinuationFromRef(); @@ -1873,7 +1877,7 @@ void TVMFunctionCompiler::pushC4ToC7IfNeed() { // c4_to_c7 if need if (m_function->stateMutability() != StateMutability::Pure) { m_pusher.was_c4_to_c7_called(); - m_pusher.push(-1, ""); // fix stack + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); m_pusher.pushCall(0, 0, "c4_to_c7"); m_pusher.endContinuationFromRef(); @@ -1925,7 +1929,7 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { if (!isEmptyFunction(m_contract->receiveFunction())) { m_pusher.pushS(1); - m_pusher.push(0, "SEMPTY ; isEmpty"); + m_pusher << "SEMPTY ; isEmpty"; m_pusher.pushS(0); m_pusher.startContinuation(); { @@ -1940,7 +1944,7 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { // funcId body' m_pusher.pushS(1); // funcId body' isZero - m_pusher.push(0, "EQINT 0 ; funcId body' isZero"); + m_pusher << "EQINT 0 ; funcId body' isZero"; m_pusher.pushS(0); // funcId body' isZero isZero" m_pusher.startContinuation(); m_pusher.dropUnder(2, 1); @@ -1955,7 +1959,7 @@ void TVMFunctionCompiler::pushReceiveOrFallback() { m_pusher.ifJmp(); } else { m_pusher.pushS(1); - m_pusher.push(0, "SEMPTY ; isEmpty"); + m_pusher << "SEMPTY ; isEmpty"; m_pusher.checkIfCtorCalled(true); m_pusher.pushS(1); @@ -1986,8 +1990,8 @@ void TVMFunctionCompiler::buildPublicFunctionSelector( auto pushOne = [&](uint32_t functionId, const std::string& name) { m_pusher.pushS(0); m_pusher.pushInt(functionId); - m_pusher.push(-2 + 1, "EQUAL"); - m_pusher.push(-1, ""); // fix stack + m_pusher << "EQUAL"; + m_pusher.fixStack(-1); // fix stack m_pusher.startContinuation(); m_pusher.pushCall(0, 0, name); m_pusher.endContinuationFromRef(); @@ -2009,7 +2013,7 @@ void TVMFunctionCompiler::buildPublicFunctionSelector( } else { m_pusher.pushS(0); m_pusher.pushInt(functionId); - m_pusher.push(-2 + 1, "LEQ"); + m_pusher << "LEQ"; m_pusher.startContinuation(); buildPublicFunctionSelector(functions, i, j); m_pusher.endContinuationFromRef(); @@ -2021,6 +2025,6 @@ void TVMFunctionCompiler::buildPublicFunctionSelector( void TVMFunctionCompiler::pushLocation(const ASTNode& node, bool reset) { SourceReference sr = SourceReferenceExtractor::extract(*GlobalParams::g_charStreamProvider, &node.location()); - const int line = reset ? 0 : sr.position.line + 1; - m_pusher.pushLoc(sr.sourceName, line); + const int line = reset ? 0 : sr.position.line + 1; + m_pusher.pushLoc(sr.sourceName, line); } diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp index 61343157..4a491218 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp @@ -89,6 +89,8 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable private: void visitForOrWhileCondition(const std::function& pushCondition); void afterLoopCheck(const std::unique_ptr& ci, const int& loopVarQty, bool _doAnalyzeFlag); + ControlFlowInfo beforeTryOrIfCheck(CFAnalyzer const& ci); + void afterTryOrIfCheck(ControlFlowInfo const& info); bool visitNode(ASTNode const&) override { solUnimplemented("Internal error: unreachable"); } bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; diff --git a/compiler/libsolidity/codegen/TVMPusher.cpp b/compiler/libsolidity/codegen/TVMPusher.cpp index 46fa200d..edaca1be 100644 --- a/compiler/libsolidity/codegen/TVMPusher.cpp +++ b/compiler/libsolidity/codegen/TVMPusher.cpp @@ -77,8 +77,8 @@ void StackPusher::pushString(const std::string& _str, bool toSlice) { } void StackPusher::pushLog() { - push(0, "CTOS"); - push(0, "STRDUMP"); + *this << "CTOS"; + *this << "STRDUMP"; drop(); } @@ -89,7 +89,7 @@ Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { if (ctx().tooMuchStateVariables()) { const int saveStack = stackSize(); pushC7(); - push(+1, "FALSE"); + *this << "FALSE"; setIndexQ(stateVarQty + TvmConst::C7::FirstIndexForVariables); untuple(stateVarQty + TvmConst::C7::FirstIndexForVariables + 1); drop(); @@ -105,14 +105,14 @@ Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { getGlob(TvmConst::C7::ReplayProtTime); } getGlob(TvmConst::C7::TvmPubkey); - push(+1, "NEWC"); - push(-2 + 1, "STU 256"); + *this << "NEWC"; + *this << "STU 256"; if (ctx().storeTimestampInC4()) { - push(-2 + 1, "STU 64"); + *this << "STU 64"; } - push(-1 + 1, "STONE"); // constructor flag + *this << "STONE"; // constructor flag if (forAwait) { - push(-1 + 1, "STONE"); // remoteAddr stateVars... b + *this << "STONE"; // remoteAddr stateVars... b blockSwap(1, stateVarQty + 1); // stateVars... b remoteAddr push(createNode(std::vector{ "NEWC", // stateVars... b remoteAddr B @@ -161,7 +161,7 @@ Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { }, 0, 0, false)); } else { if (m_ctx->usage().hasAwaitCall()) { - push(-1 + 1, "STZERO"); + *this << "STZERO"; } } if (!memberTypes.empty()) { @@ -180,7 +180,7 @@ Pointer StackPusher::generateC7ToT4Macro(bool forAwait) { "CALLCC", }, 0, 0, false)); } else { - push(-1 + 1, "ENDC"); + *this << "ENDC"; popRoot(); } Pointer block = getBlock(); @@ -194,7 +194,7 @@ bool StackPusher::doesFitInOneCellAndHaveNoStruct(Type const* key, Type const* v return TvmConst::MAX_HASH_MAP_INFO_ABOUT_KEY + keyLength + - ABITypeSize{value}.maxBits + ABITypeSize{value}.maxBits < TvmConst::CellBitLength; } @@ -211,9 +211,9 @@ StackPusher::prepareValueForDictOperations(Type const *keyType, Type const *valu case DictValueType::Address: case DictValueType::Contract: { if (!doesFitInOneCellAndHaveNoStruct(keyType, valueType)) { - push(+1, "NEWC"); - push(-1, "STSLICE"); - push(0, "ENDC"); + *this << "NEWC"; + *this << "STSLICE"; + *this << "ENDC"; return DataType::Cell; } return DataType::Slice; @@ -237,10 +237,10 @@ StackPusher::prepareValueForDictOperations(Type const *keyType, Type const *valu case DictValueType::VarInteger: case DictValueType::Function: { - push(+1, "NEWC"); + *this << "NEWC"; store(valueType, false); if (!doesFitInOneCellAndHaveNoStruct(keyType, valueType)) { - push(0, "ENDC"); + *this << "ENDC"; return DataType::Cell; } return DataType::Builder; @@ -250,7 +250,7 @@ StackPusher::prepareValueForDictOperations(Type const *keyType, Type const *valu StructCompiler sc{this, to(valueType)}; sc.tupleToBuilder(); if (!doesFitInOneCellAndHaveNoStruct(keyType, valueType)) { - push(0, "ENDC"); + *this << "ENDC"; return DataType::Cell; } return DataType::Builder; @@ -285,9 +285,9 @@ DataType StackPusher::pushDefaultValueForDict(Type const* keyType, Type const* v pushDefaultValue(valueType); value = DataType::Cell; } else { - push(+1, "NEWC"); + *this << "NEWC"; pushInt(33); - push(-1, "STZEROES"); + *this << "STZEROES"; value = DataType::Builder; } break; @@ -417,17 +417,17 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( case DictValueType::TvmSlice: { if (didUseOpcodeWithRef) { - push(0, "CTOS"); + *this << "CTOS"; } else if (doesDictStoreValueInRef(keyType, valueType)) { - push(0, "PLDREF"); - push(0, "CTOS"); + *this << "PLDREF"; + *this << "CTOS"; } break; } case DictValueType::Array: if (isByteArrayOrString(valueType)) { if (!didUseOpcodeWithRef) { - push(0, "PLDREF"); + *this << "PLDREF"; } break; } @@ -446,11 +446,11 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( { bool pushCallRef = false; if (didUseOpcodeWithRef) { - push(0, "CTOS"); + *this << "CTOS"; pushCallRef = true; } else if (doesDictStoreValueInRef(keyType, valueType)) { - push(0, "PLDREF"); - push(0, "CTOS"); + *this << "PLDREF"; + *this << "CTOS"; pushCallRef = true; } pushCallRef &= isValueStruct; @@ -466,7 +466,7 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( case DictValueType::TvmCell: { if (!didUseOpcodeWithRef) { - push(0, "PLDREF"); + *this << "PLDREF"; } break; } @@ -526,7 +526,7 @@ void StackPusher::recoverKeyAndValueAfterDictOperation( pushNull(); pushNull(); pushNull(); - push(-3, ""); // fix stack + fixStack(-3); // fix stack endContinuation(); ifElse(); @@ -644,95 +644,54 @@ void StackPusher::declRetFlag() { Pointer StackPusher::makeAsym(const string& cmd) { - auto f = [&](const std::string& pattert) { - istringstream iss(cmd); - string real; - iss >> real; - return real == pattert; - }; - - auto dictRem = [&]() { - for (std::string key : {"", "I", "U"}) { - for (std::string op : {"MIN", "MAX"}) { - for (std::string suf : {"", "REF"}) { - std::string candidate = "DICT" + key + "REM" + op + suf; - if (candidate == cmd) { - return true; - } + static std::set asymOpcodes; + if (asymOpcodes.empty()) { + for (std::string type : {"", "I", "U"}) { + for (std::string suf : {"", "REF"}) { + for (std::string op : {"MIN", "MAX"}) { + asymOpcodes.insert("DICT" + type + "REM" + op + suf); + asymOpcodes.insert("DICT" + type + op + suf); } } - } - return false; - }; - - auto dictSomeGet = [&]() { - for (std::string key : {"", "I", "U"}) { for (std::string op : {"SETGET", "ADDGET", "REPLACEGET"}) { for (std::string suf : {"", "REF", "B"}) { - std::string candidate = "DICT" + key + op + suf; - if (candidate == cmd) { - return true; - } + asymOpcodes.insert("DICT" + type + op + suf); } } + for (std::string suf : {"", "REF", "PREV", "PREVEQ", "NEXT", "NEXTEQ"}) { + asymOpcodes.insert("DICT" + type + "GET" + suf); + } } - return false; - }; - Pointer opcode = nullptr; - if (f("CONFIGPARAM")) { opcode = createNode(cmd); } - else if (f("NULLSWAPIF")) { opcode = createNode(cmd); } - else if (f("NULLSWAPIFNOT")) { opcode = createNode(cmd); } - - else if (f("LDDICTQ")) { opcode = createNode(cmd); } - else if (f("LDIQ")) { opcode = createNode(cmd); } - else if (f("LDMSGADDRQ")) { opcode = createNode(cmd); } - else if (f("LDUQ")) { opcode = createNode(cmd); } - - else if (f("DICTMIN")) { opcode = createNode(cmd); } - else if (f("DICTIMIN")) { opcode = createNode(cmd); } - else if (f("DICTUMIN")) { opcode = createNode(cmd); } - else if (f("DICTMINREF")) { opcode = createNode(cmd); } - else if (f("DICTIMINREF")) { opcode = createNode(cmd); } - else if (f("DICTUMINREF")) { opcode = createNode(cmd); } - - else if (f("DICTMAX")) { opcode = createNode(cmd); } - else if (f("DICTIMAX")) { opcode = createNode(cmd); } - else if (f("DICTUMAX")) { opcode = createNode(cmd); } - else if (f("DICTMAXREF")) { opcode = createNode(cmd); } - else if (f("DICTIMAXREF")) { opcode = createNode(cmd); } - else if (f("DICTUMAXREF")) { opcode = createNode(cmd); } - - else if (f("CDATASIZEQ")) { opcode = createNode(cmd); } - else if (f("SDATASIZEQ")) { opcode = createNode(cmd); } - - else if (dictRem()) { opcode = createNode(cmd); } - else if (f("NULLROTRIFNOT")) { opcode = createNode(cmd); } - - else if (f("DICTGET")) { opcode = createNode(cmd); } - else if (f("DICTIGET")) { opcode = createNode(cmd); } - else if (f("DICTUGET")) { opcode = createNode(cmd); } - else if (f("DICTGETREF")) { opcode = createNode(cmd); } - else if (f("DICTIGETREF")) { opcode = createNode(cmd); } - else if (f("DICTUGETREF")) { opcode = createNode(cmd); } - - else if (f("DICTGETNEXT")) { opcode = createNode(cmd); } - else if (f("DICTGETNEXTEQ")) { opcode = createNode(cmd); } - else if (f("DICTGETPREV")) { opcode = createNode(cmd); } - else if (f("DICTGETPREVEQ")) { opcode = createNode(cmd); } - else if (f("DICTIGETNEXT")) { opcode = createNode(cmd); } - else if (f("DICTIGETNEXTEQ")) { opcode = createNode(cmd); } - else if (f("DICTIGETPREV")) { opcode = createNode(cmd); } - else if (f("DICTIGETPREVEQ")) { opcode = createNode(cmd); } - else if (f("DICTUGETNEXT")) { opcode = createNode(cmd); } - else if (f("DICTUGETNEXTEQ")) { opcode = createNode(cmd); } - else if (f("DICTUGETPREV")) { opcode = createNode(cmd); } - else if (f("DICTUGETPREVEQ")) { opcode = createNode(cmd); } - - else if (dictSomeGet()) { opcode = createNode(cmd); } - - else solAssert(opcode, "StackPusher::makeAsym " + cmd); - return opcode; + for (std::string preload : {"", "P"}) + for (std::string type : {"I", "U"}) { + for (std::string size : {"4", "8"}) + asymOpcodes.insert(preload + "LD" + type + "LE" + size + "Q"); + for (std::string x : {"", "X"}) + asymOpcodes.insert(preload + "LD" + type + x + "Q"); + } + + asymOpcodes.insert("CDATASIZEQ"); + asymOpcodes.insert("CONFIGPARAM"); + asymOpcodes.insert("LDDICTQ"); + asymOpcodes.insert("LDMSGADDRQ"); + asymOpcodes.insert("LDSLICEQ"); + asymOpcodes.insert("LDSLICEXQ"); + asymOpcodes.insert("NULLROTRIFNOT"); + asymOpcodes.insert("NULLSWAPIF"); + asymOpcodes.insert("NULLSWAPIFNOT"); + asymOpcodes.insert("PLDSLICEQ"); + asymOpcodes.insert("PLDSLICEXQ"); + asymOpcodes.insert("SDATASIZEQ"); + asymOpcodes.insert("SPLITQ"); + } + + istringstream iss(cmd); + string baseCmd; + iss >> baseCmd; + solAssert(asymOpcodes.count(baseCmd) > 0, "Unknown asym opcode: " + cmd); + + return createNode(cmd); } void StackPusher::push(const Pointer& opcode) { @@ -750,10 +709,14 @@ void StackPusher::push(const Pointer& opcode) { change(opcode->take(), opcode->ret()); } +void StackPusher::pushAsym(Pointer&& node) { + m_instructions.back().push_back(node); +} + void StackPusher::pushAsym(std::string const& opcode) { solAssert(lockStack >= 1, ""); Pointer node = makeAsym(opcode); - m_instructions.back().push_back(node); + pushAsym(std::move(node)); } StackPusher& StackPusher::operator<<(std::string const& opcode) { @@ -767,14 +730,8 @@ void StackPusher::push(std::string const& cmd) { m_instructions.back().push_back(opcode); } -void StackPusher::push(int stackDiff, const string &cmd) { - if (cmd.empty()) { - change(stackDiff); - return; - } - Pointer opcode = gen(cmd); - solAssert(stackDiff == -opcode->take() + opcode->ret(), "stackDiff == -opcode->take() + opcode->ret() " + cmd); - push(cmd); +void StackPusher::fixStack(int stackDiff) { + change(stackDiff); } void StackPusher::pushCellOrSlice(const Pointer& opcode) { @@ -791,7 +748,7 @@ void StackPusher::pushSlice(std::string const& data) { void StackPusher::pushPrivateFunctionId(FunctionDefinition const& funDef) { std::string funName = ctx().getFunctionInternalName(&funDef, false); - push(+1,"PUSHINT $" + funName + "$"); + *this << "PUSHINT $" + funName + "$"; } void StackPusher::startContinuation() { @@ -875,7 +832,7 @@ void StackPusher::pushConditional(int ret) { m_instructions.back().pop_back(); auto b = createNode(false, false, trueBlock, falseBlock, ret); m_instructions.back().push_back(b); - push(ret, ""); + fixStack(ret); } void StackPusher::if_or_ifNot(bool _withNot, bool _withJmp) { @@ -939,7 +896,7 @@ void StackPusher::_while(bool _withBreakOrReturn) { m_instructions.back().push_back(b); } -void StackPusher::tryOpcode() { +void StackPusher::tryOpcode(bool saveAltC2) { solAssert(m_instructions.back().size() >= 2, ""); auto catchBody = dynamic_pointer_cast(m_instructions.back().back()); solAssert(catchBody != nullptr, ""); @@ -947,7 +904,7 @@ void StackPusher::tryOpcode() { auto tryBody = dynamic_pointer_cast(m_instructions.back().back()); solAssert(tryBody != nullptr, ""); m_instructions.back().pop_back(); - auto b = createNode(tryBody, catchBody); + auto b = createNode(tryBody, catchBody, saveAltC2); m_instructions.back().push_back(b); } @@ -992,7 +949,7 @@ TVMStack &StackPusher::getStack() { void StackPusher::untuple(int n) { solAssert(0 <= n, ""); if (n <= 15) { - push(-1 + n, "UNTUPLE " + toString(n)); + *this << "UNTUPLE " + toString(n); } else { solAssert(n <= 255, ""); pushInt(n); @@ -1004,40 +961,40 @@ void StackPusher::untuple(int n) { void StackPusher::indexWithExcep(int index) { solAssert(0 <= index && index <= 254, ""); - push(-1 + 1, "INDEX_EXCEP " + toString(index)); + *this << "INDEX_EXCEP " + toString(index); } void StackPusher::indexNoexcep(int index) { solAssert(0 <= index && index <= 254, ""); - push(-1 + 1, "INDEX_NOEXCEP " + toString(index)); + *this << "INDEX_NOEXCEP " + toString(index); } void StackPusher::setIndex(int index) { solAssert(0 <= index, ""); if (index <= 15) { - push(-2 + 1, "SETINDEX " + toString(index)); + *this << "SETINDEX " + toString(index); } else { solAssert(index <= 254, ""); pushInt(index); - push(-3 + 1, "SETINDEXVAR"); + *this << "SETINDEXVAR"; } } void StackPusher::setIndexQ(int index) { solAssert(0 <= index, ""); if (index <= 15) { - push(-2 + 1, "SETINDEXQ " + toString(index)); + *this << "SETINDEXQ " + toString(index); } else { solAssert(index <= 254, ""); pushInt(index); - push(-1 - 2 + 1, "SETINDEXVARQ"); + *this << "SETINDEXVARQ"; } } void StackPusher::tuple(int qty) { solAssert(0 <= qty, ""); if (qty <= 15) { - push(-qty + 1, "TUPLE " + toString(qty)); + *this << "TUPLE " + toString(qty); } else { solAssert(qty <= 255, ""); pushInt(qty); @@ -1144,7 +1101,7 @@ void StackPusher::popS(int i) { } void StackPusher::pushInt(const bigint& i) { - push(+1, "PUSHINT " + toString(i)); + *this << "PUSHINT " + toString(i); } bool StackPusher::fastLoad(const Type* type) { @@ -1159,7 +1116,7 @@ bool StackPusher::fastLoad(const Type* type) { if (isSmallOptional(opt)) { load(opt->valueType(), reverseOrder); } else { - push(-1 + 2, "LDREFRTOS"); + *this << "LDREFRTOS"; std::unique_ptr sc; if (auto st = to(opt->valueType())) { sc = std::make_unique(this, st); @@ -1175,9 +1132,9 @@ bool StackPusher::fastLoad(const Type* type) { } }; - push(+1, "LDI 1"); // hasValue slice + *this << "LDI 1"; // hasValue slice exchange(1); // slice hasValue - push(-1, ""); // fix stack + fixStack(-1); // fix stack startContinuation(); if (optValueAsTuple(opt->valueType())) { @@ -1188,7 +1145,7 @@ bool StackPusher::fastLoad(const Type* type) { f(false); } endContinuation(); - push(-1, ""); // fix stack + fixStack(-1); // fix stack if (!hasLock()) { solAssert(saveStakeSize == stackSize(), ""); } @@ -1197,13 +1154,13 @@ bool StackPusher::fastLoad(const Type* type) { pushNull(); exchange(1); endContinuation(); - push(-1, ""); // fix stack + fixStack(-1); // fix stack if (!hasLock()) { solAssert(saveStakeSize == stackSize(), ""); } ifElse(); - push(+1, ""); // fix stack + fixStack(+1); // fix stack if (!hasLock()) { solAssert(saveStakeSize + 1 == stackSize(), ""); } @@ -1221,7 +1178,7 @@ bool StackPusher::fastLoad(const Type* type) { return false; } case Type::Category::TvmCell: - push(-1 + 2, "LDREF"); + *this << "LDREF"; return true; case Type::Category::Struct: { auto st = to(type); @@ -1236,7 +1193,7 @@ bool StackPusher::fastLoad(const Type* type) { } case Type::Category::Address: case Type::Category::Contract: - push(-1 + 2, "LDMSGADDR"); + *this << "LDMSGADDR"; return true; case Type::Category::Enum: case Type::Category::Integer: @@ -1246,28 +1203,28 @@ bool StackPusher::fastLoad(const Type* type) { TypeInfo ti{type}; solAssert(ti.isNumeric, ""); string cmd = ti.isSigned ? "LDI " : "LDU "; - push(-1 + 2, cmd + toString(ti.numBits)); + *this << cmd + toString(ti.numBits); return true; } case Type::Category::Function: { - push(-1 + 2, "LDU 32"); + *this << "LDU 32"; return true; } case Type::Category::Array: { auto arrayType = to(type); if (arrayType->isByteArrayOrString()) { - push(-1 + 2, "LDREF"); + *this << "LDREF"; return true; } else { - push(-1 + 2, "LDU 32"); - push(-1 + 2, "LDDICT"); + *this << "LDU 32"; + *this << "LDDICT"; rotRev(); - push(-2 + 1, "TUPLE 2"); + *this << "TUPLE 2"; return false; } } case Type::Category::Mapping: - push(-1 + 2, "LDDICT"); + *this << "LDDICT"; return true; case Type::Category::VarInteger: { auto varInt = to(type); @@ -1304,11 +1261,11 @@ void StackPusher::preload(const Type *type) { } case Type::Category::Address: case Type::Category::Contract: - push(-1 + 2, "LDMSGADDR"); + *this << "LDMSGADDR"; drop(1); break; case Type::Category::TvmCell: - push(0, "PLDREF"); + *this << "PLDREF"; break; case Type::Category::Struct: { auto structType = to(type); @@ -1324,28 +1281,28 @@ void StackPusher::preload(const Type *type) { TypeInfo ti{type}; solAssert(ti.isNumeric, ""); string cmd = ti.isSigned ? "PLDI " : "PLDU "; - push(-1 + 1, cmd + toString(ti.numBits)); + *this << cmd + toString(ti.numBits); break; } case Type::Category::Function: { - push(-1 + 1, "PLDU 32"); + *this << "PLDU 32"; break; } case Type::Category::Array: { auto arrayType = to(type); if (arrayType->isByteArrayOrString()) { - push(0, "PLDREF"); + *this << "PLDREF"; } else { - push(-1 + 2, "LDU 32"); - push(-1 + 1, "PLDDICT"); - push(-2 + 1, "TUPLE 2"); + *this << "LDU 32"; + *this << "PLDDICT"; + *this << "TUPLE 2"; // stack: array } break; } case Type::Category::Mapping: case Type::Category::ExtraCurrencyCollection: - push(-1 + 1, "PLDDICT"); + *this << "PLDDICT"; break; case Type::Category::VarInteger: load(type, false); @@ -1483,8 +1440,8 @@ void StackPusher::store( if (!reverse) exchange(1); // builder value pushS(0); // builder value value - push(-1 + 1, "ISNULL"); // builder value isnull - push(-1, ""); // fix stack + *this << "ISNULL"; // builder value isnull + fixStack(-1); // fix stack ensureSize(stackSize); startContinuation(); @@ -1492,7 +1449,7 @@ void StackPusher::store( drop(1); // builder stzeroes(1); // builder' endContinuation(); - push(+1, ""); // fix stack + fixStack(+1); // fix stack ensureSize(stackSize); startContinuation(); @@ -1519,11 +1476,11 @@ void StackPusher::store( solUnimplemented(""); } sc->tupleToBuilder(); - push(-2 + 1, "STBREFR"); + *this << "STBREFR"; stones(1); // builder' } endContinuation(); - push(+1, ""); // fix stack + fixStack(+1); // fix stack ensureSize(stackSize); ifElse(); @@ -1531,7 +1488,7 @@ void StackPusher::store( break; } case Type::Category::TvmCell: - push(-1, reverse? "STREFR" : "STREF"); // builder + *this << "STREF"; // builder break; case Type::Category::Struct: { auto structType = to(type); @@ -1547,7 +1504,7 @@ void StackPusher::store( case Type::Category::Address: case Type::Category::Contract: case Type::Category::TvmSlice: - push(-1, reverse? "STSLICER" : "STSLICE"); // builder slice-value + *this << "STSLICE"; // builder slice-value break; case Type::Category::Integer: case Type::Category::Enum: @@ -1564,7 +1521,7 @@ void StackPusher::store( break; } case Type::Category::Function: { - push(-1, reverse ? "STUR 32" : "STU 32"); + *this << "STU 32"; break; } case Type::Category::Mapping: @@ -1573,25 +1530,25 @@ void StackPusher::store( exchange(1); // builder dict } // dict builder - push(-1, "STDICT"); // builder + *this << "STDICT"; // builder break; case Type::Category::Array: { auto arrayType = to(type); if (arrayType->isByteArrayOrString()) { - push(-1, reverse? "STREFR" : "STREF"); // builder + *this << "STREF"; // builder } else { if (!reverse) { exchange(1); // builder arr } - push(-1 + 2, "UNTUPLE 2"); // builder size dict + *this << "UNTUPLE 2"; // builder size dict exchange(2);// dict size builder - push(-1, "STU 32"); // dict builder' - push(-1, "STDICT"); // builder'' + *this << "STU 32"; // dict builder' + *this << "STDICT"; // builder'' } break; } case Type::Category::TvmBuilder: - push(-1, std::string("STB") + (reverse ? "R " : "")); + *this << std::string("STB") + (reverse ? "R " : ""); break; case Type::Category::Tuple: { if (!reverse) @@ -1600,7 +1557,7 @@ void StackPusher::store( const auto[types, names] = getTupleTypes(to(type)); StructCompiler sc{this, types, names}; sc.tupleToBuilder(); - push(-2 + 1, "STBR"); + *this << "STBR"; break; } case Type::Category::VarInteger: { @@ -1665,10 +1622,10 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { if (powerDiff != 0) { if (powerDiff > 0) { pushInt(pow10(powerDiff)); - push(-2 + 1, "MUL"); + *this << "MUL"; } else { pushInt(pow10(-powerDiff)); - push(-2 + 1, "DIV"); + *this << "DIV"; } } if (!impl) @@ -1679,7 +1636,7 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { int powerDiff = r->fractionalDigits(); if (powerDiff > 0) { pushInt(pow10(powerDiff)); - push(-2 + 1, "DIV"); + *this << "DIV"; } if (!impl) checkFit(l); @@ -1698,7 +1655,7 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { int powerDiff = l->fractionalDigits(); if (powerDiff > 0) { pushInt(pow10(powerDiff)); - push(-2 + 1, "MUL"); + *this << "MUL"; } if (!impl) checkFit(l); @@ -1707,9 +1664,9 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { auto fixedBytesFromFixedBytes = [this](FixedBytesType const* l, FixedBytesType const* r) { int diff = 8 * (l->numBytes() - r->numBytes()); if (diff > 0) { - push(0, "LSHIFT " + std::to_string(diff)); + *this << "LSHIFT " + std::to_string(diff); } else if (diff < 0) { - push(0, "RSHIFT " + std::to_string(-diff)); + *this << "RSHIFT " + std::to_string(-diff); } }; @@ -1717,7 +1674,7 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { size_t bits = r->numBytes() * 8; startContinuation(); startOpaque(); - push(0, "CTOS"); // slice + *this << "CTOS"; // slice pushAsym("LDUQ " + std::to_string(bits)); // data slice flag @@ -1730,20 +1687,20 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { startContinuation(); // slice pushS(0); - push(0, "SBITS"); + *this << "SBITS"; // slice slice_bits pushS(0); // slice slice_bits slice_bits rotRev(); // slice_bits slice slice_bits - push(-1, "PLDUX"); + *this << "PLDUX"; // slice_bits number blockSwap(1, 1); // number slice_bits - push(0, "NEGATE"); + *this << "NEGATE"; pushInt(bits); - push(-1, "ADD"); - push(-1, "LSHIFT"); + *this << "ADD"; + *this << "LSHIFT"; // number with trailing zeros endContinuation(); ifElse(); @@ -1763,7 +1720,7 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { ++bytes; } drop(1); // delete old value - push(+1, "PUSHINT " + toString(value)); + *this << "PUSHINT " + toString(value); }; auto fromFixedPoint = [&](FixedPointType const* r) { @@ -1795,9 +1752,9 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { integerFromInteger(&to(leftType)->asIntegerType(), r); break; case Type::Category::Function: { - m_ctx->setPragmaSaveAllFunctions(); - break; - } + m_ctx->setPragmaSaveAllFunctions(); + break; + } case Type::Category::FixedBytes: // do nothing here break; @@ -1882,9 +1839,9 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { case Type::Category::Array: { auto stringType = to(leftType); solAssert(stringType->isByteArrayOrString(), ""); - push(+1, "NEWC"); - push(-2 + 1, "STU " + toString(8 * r->numBytes())); - push(0, "ENDC"); + *this << "NEWC"; + *this << "STU " + toString(8 * r->numBytes()); + *this << "ENDC"; break; } default: @@ -1906,6 +1863,9 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { break; case Type::Category::Array: break; + case Type::Category::TvmSlice: + *this << "CTOS"; + break; default: solUnimplemented(""); break; @@ -1922,7 +1882,7 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { pushS(0); *this << "ISNULL"; - push(-1, ""); // fix stack + fixStack(-1); // fix stack startContinuation(); if (optValueAsTuple(l->valueType())) { @@ -1948,6 +1908,24 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { 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: @@ -1959,7 +1937,6 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { case Type::Category::Struct: case Type::Category::TvmBuilder: case Type::Category::TvmCell: - case Type::Category::TvmSlice: case Type::Category::Null: case Type::Category::EmpyMap: break; @@ -1992,6 +1969,10 @@ void StackPusher::hardConvert(Type const *leftType, Type const *rightType) { break; } + case Type::Category::UserDefinedValueType: { + break; + } + default: solUnimplemented(rightType->toString()); break; @@ -2004,9 +1985,9 @@ void StackPusher::checkFit(Type const *type) { case Type::Category::Integer: { auto it = to(type); if (it->isSigned()) { - push(0, "FITS " + toString(it->numBits())); + *this << "FITS " + toString(it->numBits()); } else { - push(0, "UFITS " + toString(it->numBits())); + *this << "UFITS " + toString(it->numBits()); } break; } @@ -2014,18 +1995,18 @@ void StackPusher::checkFit(Type const *type) { case Type::Category::FixedPoint: { auto fp = to(type); if (fp->isSigned()) { - push(0, "FITS " + toString(fp->numBits())); + *this << "FITS " + toString(fp->numBits()); } else { - push(0, "UFITS " + toString(fp->numBits())); + *this << "UFITS " + toString(fp->numBits()); } break; } - case Type::Category::VarInteger: { - auto varInt = to(type); - checkFit(&varInt->asIntegerType()); - break; - } + case Type::Category::VarInteger: { + auto varInt = to(type); + checkFit(&varInt->asIntegerType()); + break; + } default: solUnimplemented(""); @@ -2072,8 +2053,8 @@ void StackPusher::pushCallOrCallRef( const bool isOnCodeUpgrade = fd->name() == "onCodeUpgrade"; if (hasLoop || isOnCodeUpgrade) { pushPrivateFunctionId(*_to); - pushC3(); - execute(take + 2, ret); + pushC3(); + execute(take + 2, ret); } else { pushMacroCallInCallRef(take, ret, functionName + "_macro"); } @@ -2098,7 +2079,7 @@ void StackPusher::drop(int cnt) { solAssert(cnt >= 0, ""); if (cnt >= 1) { auto opcode = makeDROP(cnt); - push(-cnt, ""); + fixStack(-cnt); m_instructions.back().push_back(opcode); } } @@ -2181,13 +2162,13 @@ void StackPusher::prepareKeyForDictOperations(Type const *key, bool doIgnoreByte // stack: key if (isStringOrStringLiteralOrBytes(key) || key->category() == Type::Category::TvmCell) { if (!doIgnoreBytes) { - push(-1 + 1, "HASHCU"); + *this << "HASHCU"; } } else if (key->category() == Type::Category::Struct) { StructCompiler sc{this, to(key)}; sc.tupleToBuilder(); - push(0, "ENDC"); - push(0, "CTOS"); + *this << "ENDC"; + *this << "CTOS"; } } @@ -2207,7 +2188,7 @@ int StackPusher::int_msg_info(const std::set &isParamOnStack, const std::ma 64, 32}; std::string bitString = "0"; int maxBitStringSize = 0; - push(+1, "NEWC"); + *this << "NEWC"; for (int param = 0; param < static_cast(zeroes.size()); ++param) { solAssert(constParams.count(param) == 0 || isParamOnStack.count(param) == 0, ""); @@ -2223,24 +2204,24 @@ int StackPusher::int_msg_info(const std::set &isParamOnStack, const std::ma bitString = ""; switch (param) { case TvmConst::int_msg_info::bounce: - push(-1, "STI 1"); + *this << "STI 1"; ++maxBitStringSize; break; case TvmConst::int_msg_info::dest: if (isDestBuilder) { - push(-1, "STB"); + *this << "STB"; } else { - push(-1, "STSLICE"); + *this << "STSLICE"; } maxBitStringSize += AddressInfo::maxBitLength(); break; case TvmConst::int_msg_info::tons: exchange(1); - push(-1, "STGRAMS"); + *this << "STGRAMS"; maxBitStringSize += VarUIntegerInfo::maxTonBitLength(); break; case TvmConst::int_msg_info::currency: - push(-1, "STDICT"); + *this << "STDICT"; ++maxBitStringSize; break; default: @@ -2269,7 +2250,7 @@ int StackPusher::ext_msg_info(const set &isParamOnStack, bool isOut = true) } std::string bitString = isOut ? "11" : "10"; int maxBitStringSize = 0; - push(+1, "NEWC"); + *this << "NEWC"; for (int param = 0; param < static_cast(zeroes.size()); ++param) { if (isParamOnStack.count(param) == 0) { bitString += std::string(zeroes.at(param), '0'); @@ -2278,10 +2259,10 @@ int StackPusher::ext_msg_info(const set &isParamOnStack, bool isOut = true) appendToBuilder(bitString); bitString = ""; if (param == TvmConst::ext_msg_info::dest) { - push(-1, "STSLICE"); + *this << "STSLICE"; maxBitStringSize += AddressInfo::maxBitLength(); } else if (param == TvmConst::ext_msg_info::src) { - push(-1, "STB"); + *this << "STB"; maxBitStringSize += TvmConst::ExtInboundSrcLength; } else { solUnimplemented(""); @@ -2307,16 +2288,16 @@ void StackPusher::appendToBuilder(const std::string &bitString) { } else { const std::string hex = StrUtils::binaryStringToSlice(bitString); if (hex.length() * 4 <= 8 * 7 + 1) { - push(0, "STSLICECONST x" + hex); + *this << "STSLICECONST x" + hex; } else { pushSlice("x" + StrUtils::binaryStringToSlice(bitString)); - push(-1, "STSLICER"); + *this << "STSLICER"; } } } void StackPusher::checkOptionalValue() { - push(-1 + 1, "ISNULL"); + *this << "ISNULL"; _throw("THROWIF " + toString(TvmConst::RuntimeException::GetOptionalException)); } @@ -2324,10 +2305,10 @@ void StackPusher::stzeroes(int qty) { if (qty > 0) { // builder if (qty == 1) { - push(0, "STSLICECONST 0"); + *this << "STSLICECONST 0"; } else { pushInt(qty); // builder qty - push(-1, "STZEROES"); + *this << "STZEROES"; } } } @@ -2336,16 +2317,16 @@ void StackPusher::stones(int qty) { if (qty > 0) { // builder if (qty == 1) { - push(0, "STSLICECONST 1"); + *this << "STSLICECONST 1"; } else { pushInt(qty); // builder qty - push(-1, "STONES"); + *this << "STONES"; } } } void StackPusher::sendrawmsg() { - push(-2, "SENDRAWMSG"); + *this << "SENDRAWMSG"; } void StackPusher::sendIntMsg(const std::map &exprs, @@ -2416,7 +2397,7 @@ void StackPusher::prepareMsg( } // stack: builder' - push(0, "ENDC"); // stack: cell + *this << "ENDC"; // stack: cell } void StackPusher::sendMsg(const std::set& isParamOnStack, @@ -2504,16 +2485,22 @@ void TVMStack::takeLast(int n) { solAssert(int(m_stackSize.size()) == n, ""); } -void TVMCompilerContext::initMembers(ContractDefinition const *contract) { - solAssert(!m_contract, ""); - m_contract = contract; - +InherHelper::InherHelper(const ContractDefinition *contract) { for (ContractDefinition const* c : contract->annotation().linearizedBaseContracts) { for (FunctionDefinition const *_function : c->definedFunctions()) { const std::set& b = _function->annotation().baseFunctions; m_baseFunctions.insert(b.begin(), b.end()); } } +} + +bool InherHelper::isBaseFunction(CallableDeclaration const* d) const { + return m_baseFunctions.count(d) != 0; +} + +void TVMCompilerContext::initMembers(ContractDefinition const *contract) { + solAssert(!m_contract, ""); + m_contract = contract; ignoreIntOverflow = m_pragmaHelper.hasIgnoreIntOverflow(); for (VariableDeclaration const *variable: notConstantStateVariables()) { @@ -2523,7 +2510,8 @@ void TVMCompilerContext::initMembers(ContractDefinition const *contract) { TVMCompilerContext::TVMCompilerContext(ContractDefinition const *contract, PragmaDirectiveHelper const &pragmaHelper) : m_pragmaHelper{pragmaHelper}, - m_usage{*contract} + m_usage{*contract}, + m_inherHelper{contract} { initMembers(contract); } @@ -2567,9 +2555,9 @@ string TVMCompilerContext::getFunctionInternalName(FunctionDefinition const* _fu std::string functionName; const std::string hexName = _function->externalIdentifierHex(); ContractDefinition const* contract = _function->annotation().contract; - if (contract && contract->isLibrary()) { - functionName = getLibFunctionName(_function, calledByPoint); - } else if (calledByPoint && isBaseFunction(_function)) { + if (contract && contract->isLibrary()) { + functionName = getLibFunctionName(_function, calledByPoint); + } else if (calledByPoint && isBaseFunction(_function)) { functionName = _function->annotation().contract->name() + "_" + _function->name() + "_" + hexName; } else if (_function->isFree()) { functionName = _function->name() + "_" + hexName + "_free_internal"; @@ -2678,7 +2666,7 @@ bool TVMCompilerContext::addAndDoesHaveLoop(FunctionDefinition const* _v, Functi } bool TVMCompilerContext::isBaseFunction(CallableDeclaration const* d) const { - return m_baseFunctions.count(d) != 0; + return m_inherHelper.isBaseFunction(d); } bool TVMCompilerContext::dfs(FunctionDefinition const* v) { @@ -2701,21 +2689,21 @@ bool TVMCompilerContext::dfs(FunctionDefinition const* v) { void StackPusher::pushEmptyArray() { pushInt(0); - push(+1, "NEWDICT"); - push(-2 + 1, "TUPLE 2"); + *this << "NEWDICT"; + *this << "TUPLE 2"; } void StackPusher::pushNull() { - push(+1, "NULL"); + *this << "NULL"; } void StackPusher::pushEmptyCell() { pushCellOrSlice(createNode(PushCellOrSlice::Type::PUSHREF, "", nullptr)); } -void StackPusher::pushDefaultValue(Type const* type) { +void StackPusher::pushDefaultValue(Type const* _type) { startOpaque(); - Type::Category cat = type->category(); + Type::Category cat = _type->category(); switch (cat) { case Type::Category::Address: case Type::Category::Contract: @@ -2727,11 +2715,11 @@ void StackPusher::pushDefaultValue(Type const* type) { case Type::Category::Enum: case Type::Category::VarInteger: case Type::Category::FixedPoint: - push(+1, "PUSHINT 0"); + *this << "PUSHINT 0"; break; case Type::Category::Array: case Type::Category::TvmCell: - if (cat == Type::Category::TvmCell || to(type)->isByteArrayOrString()) { + if (cat == Type::Category::TvmCell || to(_type)->isByteArrayOrString()) { pushEmptyCell(); break; } @@ -2739,10 +2727,10 @@ void StackPusher::pushDefaultValue(Type const* type) { break; case Type::Category::Mapping: case Type::Category::ExtraCurrencyCollection: - push(+1, "NEWDICT"); + *this << "NEWDICT"; break; case Type::Category::Struct: { - auto structType = to(type); + auto structType = to(_type); StructCompiler structCompiler{this, structType}; structCompiler.createDefaultStruct(); break; @@ -2751,7 +2739,7 @@ void StackPusher::pushDefaultValue(Type const* type) { pushSlice("x8_"); break; case Type::Category::TvmBuilder: - push(+1, "NEWC"); + *this << "NEWC"; break; case Type::Category::Function: { pushInt(TvmConst::FunctionId::DefaultValueForFunctionType); @@ -2764,6 +2752,11 @@ void StackPusher::pushDefaultValue(Type const* type) { case Type::Category::TvmVector: tuple(0); break; + case Type::Category::UserDefinedValueType: { + auto userDefValue = to(_type); + pushDefaultValue(&userDefValue->underlyingType()); + break; + } default: solUnimplemented(""); } @@ -2782,15 +2775,15 @@ void StackPusher::getDict( void StackPusher::byteLengthOfCell() { pushInt(0xFFFFFFFF); - push(-2 + 3, "CDATASIZE"); + *this << "CDATASIZE"; drop(1); dropUnder(1, 1); - push(-1 + 1, "RSHIFT 3"); + *this << "RSHIFT 3"; } void StackPusher::was_c4_to_c7_called() { getGlob(TvmConst::C7::TvmPubkey); - push(-1 + 1, "ISNULL"); + *this << "ISNULL"; } void StackPusher::checkCtorCalled() { diff --git a/compiler/libsolidity/codegen/TVMPusher.hpp b/compiler/libsolidity/codegen/TVMPusher.hpp index b795e4ae..895e788c 100644 --- a/compiler/libsolidity/codegen/TVMPusher.hpp +++ b/compiler/libsolidity/codegen/TVMPusher.hpp @@ -50,6 +50,14 @@ class TVMStack { std::vector m_stackSize; }; +class InherHelper { +public: + explicit InherHelper(ContractDefinition const* contract); + bool isBaseFunction(CallableDeclaration const* d) const; +private: + std::set m_baseFunctions; +}; + class TVMCompilerContext { public: TVMCompilerContext(ContractDefinition const* contract, PragmaDirectiveHelper const& pragmaHelper); @@ -59,7 +67,6 @@ class TVMCompilerContext { bool tooMuchStateVariables() const; std::vector notConstantStateVariableTypes() const; PragmaDirectiveHelper const& pragmaHelper() const; - bool hasTimeInAbiHeader() const; bool isStdlib() const; std::string getFunctionInternalName(FunctionDefinition const* _function, bool calledByPoint = true) const; static std::string getLibFunctionName(FunctionDefinition const* _function, bool withObject) ; @@ -99,8 +106,8 @@ class TVMCompilerContext { } std::map> const& buildTuple() const { return m_tuples; } - bool getPragmaSaveAllFunctions() const { return m_pragmaSaveAllFunctions; } - void setPragmaSaveAllFunctions() { m_pragmaSaveAllFunctions = true; } + bool getPragmaSaveAllFunctions() const { return m_pragmaSaveAllFunctions; } + void setPragmaSaveAllFunctions() { m_pragmaSaveAllFunctions = true; } private: // TODO split to several classes @@ -119,13 +126,13 @@ class TVMCompilerContext { bool m_isFallBackGenerated{}; bool m_isReceiveGenerated{}; bool m_isOnBounceGenerated{}; - std::set m_baseFunctions; - ContactsUsageScanner m_usage; + ContactsUsageScanner m_usage; std::set> m_constArrays; std::set> m_newArray; std::map> m_tuples; - bool m_pragmaSaveAllFunctions{}; + bool m_pragmaSaveAllFunctions{}; + InherHelper const m_inherHelper; }; class StackPusher { @@ -158,10 +165,11 @@ class StackPusher { void push(const Pointer& opcode); void push(const Pointer& opcode); void push(const Pointer& opcode); + void pushAsym(Pointer&& node); void pushAsym(std::string const& opcode); StackPusher& operator<<(std::string const& opcode); void push(std::string const& opcode); - void push(int stackDiff, const std::string& cmd); + void fixStack(int stackDiff); private: void pushCellOrSlice(const Pointer& opcode); public: @@ -199,7 +207,7 @@ class StackPusher { void repeat(bool _withBreakOrReturn); void until(bool withBreakOrReturn); void _while(bool _withBreakOrReturn); - void tryOpcode(); + void tryOpcode(bool saveAltC2); void ret(); void retAlt(); void ifRetAlt(); @@ -209,7 +217,7 @@ class StackPusher { TVMStack& getStack(); void pushLoc(const std::string& file, int line); - void pushString(const std::string& str, bool toSlice); + void pushString(const std::string& str, bool toSlice); void pushLog(); void untuple(int n); void indexWithExcep(int index); @@ -311,13 +319,13 @@ class StackPusher { const Type& keyType, const Type& valueType, GetDictOperation op, - const DataType& dataType = DataType::Slice + const DataType& dataType = DataType::Slice ); void pushEmptyArray(); void pushNull(); void pushEmptyCell(); - void pushDefaultValue(Type const* type); + void pushDefaultValue(Type const* _type); void sendIntMsg(const std::map &exprs, const std::map &constParams, const std::function &appendBody, diff --git a/compiler/libsolidity/codegen/TVMSimulator.cpp b/compiler/libsolidity/codegen/TVMSimulator.cpp index ee0a53c0..057baecc 100644 --- a/compiler/libsolidity/codegen/TVMSimulator.cpp +++ b/compiler/libsolidity/codegen/TVMSimulator.cpp @@ -386,9 +386,9 @@ bool Simulator::visit(LogCircuit &_node) { } bool Simulator::visit(TryCatch &/*_node*/) { - // TODO implement - unableToConvertOpcode(); - return false; + // TODO implement + unableToConvertOpcode(); + return false; } bool Simulator::visit(TvmIfElse &_node) { diff --git a/compiler/libsolidity/codegen/TVMSimulator.hpp b/compiler/libsolidity/codegen/TVMSimulator.hpp index df5fb49b..fe296f33 100644 --- a/compiler/libsolidity/codegen/TVMSimulator.hpp +++ b/compiler/libsolidity/codegen/TVMSimulator.hpp @@ -53,8 +53,8 @@ namespace solidity::frontend { bool visit(CodeBlock &_node) override; bool visit(SubProgram &_node) override; bool visit(LogCircuit &_node) override; - bool visit(TryCatch &_node) override; - bool visit(TvmIfElse &_node) override; + bool visit(TryCatch &_node) override; + bool visit(TvmIfElse &_node) override; bool visit(TvmRepeat &_node) override; bool visit(TvmUntil &_node) override; bool visit(While &_node) override; diff --git a/compiler/libsolidity/codegen/TVMStructCompiler.cpp b/compiler/libsolidity/codegen/TVMStructCompiler.cpp index fb61d4e5..c7b7e26c 100644 --- a/compiler/libsolidity/codegen/TVMStructCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMStructCompiler.cpp @@ -137,7 +137,7 @@ void StructCompiler::structConstructor( } void StructCompiler::tupleToBuilder() { - // stack: tuple + // stack: tuple const int ss = pusher->stackSize(); pusher->startContinuation(); @@ -147,7 +147,7 @@ void StructCompiler::tupleToBuilder() { if (n >= 2) { pusher->reverse(n, 0); } - pusher->push(+1, "NEWC"); + *pusher << "NEWC"; ChainDataEncoder encoder{pusher}; DecodePositionAbiV2 position{0, 0, m_types}; diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.cpp b/compiler/libsolidity/codegen/TVMTypeChecker.cpp index e4ba3155..bc1bd954 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.cpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.cpp @@ -22,6 +22,7 @@ #include "TVM.hpp" #include "TVMCommons.hpp" +#include "TVMPusher.hpp" #include "TVMConstants.hpp" #include "TVMTypeChecker.hpp" @@ -106,7 +107,7 @@ void TVMTypeChecker::checkOverrideAndOverload() { "Both override and base functions should have functionID if it is defined for one of them."); } - if ((f->internalMsg() ^ baseFunction->internalMsg()) || (f->externalMsg() ^ baseFunction->externalMsg())) { + if ((f->isInternalMsg() ^ baseFunction->isInternalMsg()) || (f->isExternalMsg() ^ baseFunction->isExternalMsg())) { m_errorReporter.typeError( 228_error, f->location(), @@ -161,6 +162,14 @@ void TVMTypeChecker::check_onCodeUpgrade(FunctionDefinition const& f) { } } +bool TVMTypeChecker::visit(TryStatement const& _tryStatement) { + if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { + m_errorReporter.typeError(228_error, _tryStatement.location(), + "\"try-catch\"" + isNotSupportedVM); + } + return true; +} + 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."); @@ -169,14 +178,14 @@ bool TVMTypeChecker::visit(VariableDeclaration const& _node) { } bool TVMTypeChecker::visit(const Mapping &_mapping) { - if (auto keyType = to(&_mapping.keyType())) { - if (keyType->annotation().type->category() == Type::Category::Struct) { - auto structType = to(_mapping.keyType().annotation().type); - int bitLength = 0; - StructDefinition const& structDefinition = structType->structDefinition(); - for (const auto& member : structDefinition.members()) { - TypeInfo ti {member->type()}; - if (!ti.isNumeric) { + if (auto keyType = to(&_mapping.keyType())) { + if (keyType->annotation().type->category() == Type::Category::Struct) { + auto structType = to(_mapping.keyType().annotation().type); + int bitLength = 0; + StructDefinition const& structDefinition = structType->structDefinition(); + for (const auto& member : structDefinition.members()) { + TypeInfo ti {member->type()}; + if (!ti.isNumeric) { m_errorReporter.typeError( 228_error, _mapping.keyType().location(), @@ -184,27 +193,29 @@ bool TVMTypeChecker::visit(const Mapping &_mapping) { "If struct type is used as a key type for mapping, then " "fields of the struct must have integer, boolean, fixed bytes or enum type" ); - } - bitLength += ti.numBits; - } - if (bitLength > TvmConst::CellBitLength) { + } + 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 " "struct must fit in " + toString(TvmConst::CellBitLength) + " bits"); - } - } - } - return true; + } + } + } + return true; } bool TVMTypeChecker::visit(const FunctionDefinition &f) { - if (f.functionID().has_value() && 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."); - } - if (f.functionID().has_value() && (!f.isPublic() && f.name() != "onCodeUpgrade")) { - m_errorReporter.typeError(228_error, f.location(), "Only public/external functions and function `onCodeUpgrade` can have functionID."); - } - if (f.functionID().has_value() && (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."); + 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."); + } + if (!f.isPublic() && f.name() != "onCodeUpgrade") { + m_errorReporter.typeError(228_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."); + } } if (f.isInline() && f.isPublic()) { @@ -217,10 +228,10 @@ bool TVMTypeChecker::visit(const FunctionDefinition &f) { if (f.name() == "afterSignatureCheck") { const std::string s = "\nExpected follow format: \"function afterSignatureCheck(TvmSlice restOfMessageBody, TvmCell message) private inline returns (TvmSlice) { /*...*/ }\""; if ( - f.parameters().size() != 2 || - f.parameters().at(0)->type()->category() != Type::Category::TvmSlice || - f.parameters().at(1)->type()->category() != Type::Category::TvmCell - ) { + f.parameters().size() != 2 || + 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(), "Unexpected function parameters." + s); } @@ -236,6 +247,10 @@ bool TVMTypeChecker::visit(const FunctionDefinition &f) { } } + if (!f.isFree() && f.isInlineAssembly()) { + m_errorReporter.typeError(228_error, f.location(), "Only free functions can be marked as \"assembly\"."); + } + return true; } @@ -249,10 +264,25 @@ bool TVMTypeChecker::visit(IndexRangeAccess const& indexRangeAccess) { } bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { + auto memberAccess = to(&_functionCall.expression()); + ASTString const& memberName = memberAccess ? memberAccess->memberName() : ""; Type const* expressionType = _functionCall.expression().annotation().type; + std::vector> const& arguments = _functionCall.arguments(); switch (expressionType->category()) { case Type::Category::Function: { auto functionType = to(expressionType); + + if (functionType->hasDeclaration()) { + auto fd = to(&functionType->declaration()); + if (fd && fd->name() == "onCodeUpgrade") { + if (m_inherHelper->isBaseFunction(fd)) { + m_errorReporter.typeError( + 228_error, _functionCall.location(), + "It is forbidden to call base functions of \"onCodeUpgrade\"."); + } + } + } + switch (functionType->kind()) { case FunctionType::Kind::TVMInitCodeHash: if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { @@ -266,6 +296,86 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { "\"tvm.code()\"" + isNotSupportedVM); } break; + case FunctionType::Kind::TVMSliceLoad: + if (memberName == "decode") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".decode()\" is deprecated. Use \".load()\""); + } + break; + case FunctionType::Kind::TVMSliceLoadQ: + if (memberName == "decodeQ") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".decodeQ()\" is deprecated. Use \".loadQ()\""); + } + break; + case FunctionType::Kind::TVMSliceLoadFunctionParams: + if (memberName == "decodeFunctionParams") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".decodeFunctionParams()\" is deprecated. Use \".loadFunctionParams()\""); + } + break; + case FunctionType::Kind::TVMSliceLoadStateVars: + if (memberName == "decodeStateVars") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".decodeStateVars()\" is deprecated. Use \".loadStateVars()\""); + } + break; + case FunctionType::Kind::TVMSliceLoadUint: + if (memberName == "loadUnsigned") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".loadUnsigned()\" is deprecated. Use \".loadUint()\""); + } + break; + case FunctionType::Kind::TVMSliceLoadInt: + if (memberName == "loadSigned") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".loadSigned()\" is deprecated. Use \".loadInt()\""); + } + break; + case FunctionType::Kind::TVMBuilderStoreUint: + if (memberName == "storeUnsigned") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".storeUnsigned()\" is deprecated. Use \".storeUint()\""); + } + break; + case FunctionType::Kind::TVMBuilderStoreInt: + if (memberName == "storeSigned") { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".storeSigned()\" is deprecated. Use \".storeInt()\""); + } + break; + case FunctionType::Kind::ByteToSlice: { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".toSlice()\" is deprecated. Use explicit conversion: \"TvmSlice()\""); + break; + } + case FunctionType::Kind::StringToSlice: { + m_errorReporter.warning(228_error, _functionCall.location(), + "\".toSlice()\" is deprecated. Use explicit conversion: \"TvmSlice()\""); + break; + } + default: + break; + } + + switch (functionType->kind()) { + case FunctionType::Kind::TVMSliceLoadInt: + case FunctionType::Kind::TVMSliceLoadIntQ: + case FunctionType::Kind::TVMSliceLoadUint: + case FunctionType::Kind::TVMSliceLoadUintQ: + case FunctionType::Kind::TVMSlicePreLoadInt: + case FunctionType::Kind::TVMSlicePreLoadIntQ: + case FunctionType::Kind::TVMSlicePreLoadUint: + case FunctionType::Kind::TVMSlicePreLoadUintQ: { + const auto& value = ExprUtils::constValue(*arguments.at(0)); + if (value.has_value()) { + if (value > 256) { + m_errorReporter.syntaxError(228_error, arguments.at(0)->location(), + "Too big value. The value must be in the range 0 - 256."); + } + } + break; + } default: break; } @@ -275,7 +385,6 @@ bool TVMTypeChecker::visit(FunctionCall const& _functionCall) { break; } - if (_functionCall.isAwait() && *GlobalParams::g_tvmVersion == TVMVersion::ton()) { m_errorReporter.typeError(228_error, _functionCall.location(), "\"*.await\"" + isNotSupportedVM); @@ -296,20 +405,33 @@ bool TVMTypeChecker::visit(PragmaDirective const& _pragma) { bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.memberName(); - Expression const& expression = _memberAccess.expression(); - switch (expression.annotation().type->category()) { + Type const* exprType = _memberAccess.expression().annotation().type; + switch (exprType->category()) { case Type::Category::Magic: { - if (member == "storageFee") { - if (*GlobalParams::g_tvmVersion == TVMVersion::ton()) { - m_errorReporter.typeError(228_error, _memberAccess.location(), - "\"tx.storageFee\"" + isNotSupportedVM); + auto magicType = dynamic_cast(exprType); + switch (magicType->kind()) { + case MagicType::Kind::Transaction: { + if (member == "timestamp") { + m_errorReporter.warning(228_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(), + "\"tx.storageFee\"" + isNotSupportedVM); + } + } + break; } - if (auto ident = to(&expression)) { - if (ident->name() == "gosh" && *GlobalParams::g_tvmVersion != TVMVersion::gosh()) { + case MagicType::Kind::Gosh: { + if (*GlobalParams::g_tvmVersion != TVMVersion::gosh()) { m_errorReporter.typeError(228_error, _memberAccess.location(), "\"gosh." + member + "\"" + isNotSupportedVM); } + break; + } + default: + break; } break; } @@ -322,6 +444,8 @@ bool TVMTypeChecker::visit(MemberAccess const& _memberAccess) { bool TVMTypeChecker::visit(ContractDefinition const& cd) { contractDefinition = &cd; + m_inherHelper = std::make_unique(&cd); + checkOverrideAndOverload(); return true; @@ -329,4 +453,5 @@ bool TVMTypeChecker::visit(ContractDefinition const& cd) { void TVMTypeChecker::endVisit(ContractDefinition const& ) { contractDefinition = nullptr; + m_inherHelper = nullptr; } diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.hpp b/compiler/libsolidity/codegen/TVMTypeChecker.hpp index 1f6dcaf5..664d69b9 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.hpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.hpp @@ -19,6 +19,8 @@ namespace solidity::frontend { +class InherHelper; + class TVMTypeChecker : public ASTConstVisitor { public: explicit TVMTypeChecker(langutil::ErrorReporter& _errorReporter); @@ -28,6 +30,7 @@ class TVMTypeChecker : public ASTConstVisitor { void check_onCodeUpgrade(FunctionDefinition const& f); public: + bool visit(TryStatement const& _node) override; bool visit(VariableDeclaration const& _node) override; bool visit(Mapping const& _mapping) override; bool visit(FunctionDefinition const& fc) override; @@ -39,6 +42,7 @@ class TVMTypeChecker : public ASTConstVisitor { void endVisit(ContractDefinition const& ) override; private: + std::unique_ptr m_inherHelper; langutil::ErrorReporter& m_errorReporter; ContractDefinition const* contractDefinition{}; }; diff --git a/compiler/libsolidity/codegen/TvmAst.cpp b/compiler/libsolidity/codegen/TvmAst.cpp index d8d8ac24..9797d8ea 100644 --- a/compiler/libsolidity/codegen/TvmAst.cpp +++ b/compiler/libsolidity/codegen/TvmAst.cpp @@ -373,7 +373,7 @@ bool LogCircuit::operator==(TvmAstNode const& _node) const { TvmIfElse::TvmIfElse(bool _withNot, bool _withJmp, Pointer const &trueBody, Pointer const &falseBody, int ret) : Gen{false}, - m_withNot{_withNot}, + m_withNot{_withNot}, m_withJmp{_withJmp}, m_trueBody(trueBody), m_falseBody(falseBody), @@ -415,11 +415,11 @@ void While::accept(TvmAstVisitor& _visitor) { } void TryCatch::accept(TvmAstVisitor& _visitor) { - if (_visitor.visit(*this)) - { - m_tryBody->accept(_visitor); - m_catchBody->accept(_visitor); - } + if (_visitor.visit(*this)) + { + m_tryBody->accept(_visitor); + m_catchBody->accept(_visitor); + } } Function::Function(int take, int ret, std::string name, Function::FunctionType type, Pointer block, @@ -512,7 +512,6 @@ Pointer gen(const std::string& cmd) { const static std::unordered_map opcodes = { {"ACCEPT", {0, 0}}, {"COMMIT", {0, 0}}, - {"NEWEXCMODEL", {0, 0}}, {"PRINTSTR", {0, 0}}, {"BLOCKLT", {0, 1, true}}, @@ -587,9 +586,15 @@ Pointer gen(const std::string& cmd) { {"PARSEMSGADDR", {1, 1}}, {"PLDDICT", {1, 1}}, {"PLDI", {1, 1}}, + {"PLDILE4", {1, 1}}, + {"PLDILE8", {1, 1}}, {"PLDREF", {1, 1}}, {"PLDREFIDX", {1, 1}}, + {"PLDREFIDX", {1, 1}}, + {"PLDSLICE", {1, 1}}, {"PLDU", {1, 1}}, + {"PLDULE4", {1, 1}}, + {"PLDULE8", {1, 1}}, {"RAND", {1, 1}}, {"SBITS", {1, 1, true}}, {"SDEMPTY", {1, 1, true}}, @@ -613,6 +618,8 @@ Pointer gen(const std::string& cmd) { {"LDDICT", {1, 2}}, {"LDGRAMS", {1, 2}}, {"LDI", {1, 2}}, + {"LDILE4", {1, 2}}, + {"LDILE8", {1, 2}}, {"LDMSGADDR", {1, 2}}, {"LDONES", {1, 2, true}}, {"LDOPTREF", {1, 2}}, @@ -620,6 +627,8 @@ Pointer gen(const std::string& cmd) { {"LDREFRTOS", {1, 2}}, {"LDSLICE", {1, 2}}, {"LDU", {1, 2}}, + {"LDULE4", {1, 2}}, + {"LDULE8", {1, 2}}, {"LDVARINT16", {1, 2}}, {"LDVARINT32", {1, 2}}, {"LDVARUINT16", {1, 2}}, @@ -662,6 +671,8 @@ Pointer gen(const std::string& cmd) { {"NEQ", {2, 1, true}}, {"OR", {2, 1, true}}, {"PLDIX", {2, 1}}, + {"PLDREFVAR", {2, 1}}, + {"PLDSLICEX", {2, 1}}, {"PLDUX", {2, 1}}, {"SCHKBITSQ", {2, 1, true}}, {"SCHKREFSQ", {2, 1, true}}, @@ -677,6 +688,8 @@ Pointer gen(const std::string& cmd) { {"STDICT", {2, 1}}, {"STGRAMS", {2, 1}}, {"STI", {2, 1}}, + {"STILE4", {2, 1}}, + {"STILE8", {2, 1}}, {"STIR", {2, 1}}, {"STONES", {2, 1}}, {"STOPTREF", {2, 1}}, @@ -685,6 +698,8 @@ Pointer gen(const std::string& cmd) { {"STSLICE", {2, 1}}, {"STSLICER", {2, 1}}, {"STU", {2, 1}}, + {"STULE4", {2, 1}}, + {"STULE8", {2, 1}}, {"STUR", {2, 1}}, {"STVARINT16", {2, 1}}, {"STVARINT32", {2, 1}}, @@ -715,6 +730,7 @@ Pointer gen(const std::string& cmd) { {"MULDIVC", {3, 1}}, {"MULDIVR", {3, 1}}, {"SCHKBITREFSQ", {3, 1, true}}, + {"SCUTFIRST", {3, 1}}, {"SETINDEXVAR", {3, 1}}, {"SETINDEXVARQ", {3, 1, true}}, {"SSKIPFIRST", {3, 1}}, @@ -757,7 +773,7 @@ Pointer gen(const std::string& cmd) { opcode = createNode(cmd, 2, 1); } } else { - solUnimplemented("StackPusher::push: " + cmd); + solUnimplemented("Unknown opcode: " + cmd); } solAssert(opcode != nullptr, ""); return opcode; diff --git a/compiler/libsolidity/codegen/TvmAst.hpp b/compiler/libsolidity/codegen/TvmAst.hpp index 9102c05b..6e4b44b2 100644 --- a/compiler/libsolidity/codegen/TvmAst.hpp +++ b/compiler/libsolidity/codegen/TvmAst.hpp @@ -414,7 +414,7 @@ namespace solidity::frontend public: While(bool _infinite, bool _withBreakOrReturn, Pointer const &condition, Pointer const &body - ) : + ) : m_infinite{_infinite}, m_withBreakOrReturn{_withBreakOrReturn}, m_condition{condition}, @@ -432,20 +432,23 @@ namespace solidity::frontend Pointer m_body; }; - class TryCatch : public TvmAstNode { - public: - TryCatch(Pointer _tryBody, Pointer _catchBody) : - m_tryBody{std::move(_tryBody)}, - m_catchBody{std::move(_catchBody)} - {} - 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; } - private: - Pointer m_tryBody; - Pointer m_catchBody; - }; + 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: @@ -466,7 +469,7 @@ namespace solidity::frontend 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; } + FunctionDefinition const* functionDefinition() const { return m_function; } FunctionType type() const { return m_type; } Pointer const& block() const { return m_block; } private: @@ -475,7 +478,7 @@ namespace solidity::frontend std::string m_name; FunctionType m_type{}; Pointer m_block; - FunctionDefinition const* m_function{}; + FunctionDefinition const* m_function{}; }; class Contract : public TvmAstNode { diff --git a/compiler/libsolidity/codegen/TvmAstVisitor.cpp b/compiler/libsolidity/codegen/TvmAstVisitor.cpp index 30b516f1..e9bea4f5 100644 --- a/compiler/libsolidity/codegen/TvmAstVisitor.cpp +++ b/compiler/libsolidity/codegen/TvmAstVisitor.cpp @@ -97,13 +97,14 @@ bool Printer::visit(TvmException &_node) { bool Printer::visit(GenOpcode &_node) { tabs(); if (_node.fullOpcode() == "BITNOT") m_out << "NOT"; - else if (_node.fullOpcode() == "NEWEXCMODEL") m_out << ".blob xf2fe ;; NEWEXCMODEL"; else if (_node.fullOpcode() == "TUPLE 1") m_out << "SINGLE"; else if (_node.fullOpcode() == "TUPLE 2") m_out << "PAIR"; else if (_node.fullOpcode() == "TUPLE 3") m_out << "TRIPLE"; else if (_node.fullOpcode() == "UNTUPLE 1") m_out << "UNSINGLE"; else if (_node.fullOpcode() == "UNTUPLE 2") m_out << "UNPAIR"; else if (_node.fullOpcode() == "UNTUPLE 3") m_out << "UNTRIPLE"; + else if (_node.fullOpcode() == "STSLICECONST x4_") m_out << "STZERO"; + else if (_node.fullOpcode() == "STSLICECONST xc_") m_out << "STONE"; else if (isIn(_node.opcode(), "INDEX_EXCEP", "INDEX_NOEXCEP")) { int index = boost::lexical_cast(_node.arg()); if (index == 0) { @@ -623,11 +624,15 @@ bool Printer::visit(TvmUntil &_node) { } bool Printer::visit(TryCatch &_node) { - _node.tryBody()->accept(*this); - _node.catchBody()->accept(*this); - tabs(); - m_out << "TRY" << std::endl; - return false; + if (_node.saveAltC2()) { + tabs(); + m_out << "SAVEALT C2" << std::endl; + } + _node.tryBody()->accept(*this); + _node.catchBody()->accept(*this); + tabs(); + m_out << ".blob xF2FE ; TRYKEEP" << std::endl; + return false; } bool Printer::visit(While &_node) { diff --git a/compiler/libsolidity/parsing/Parser.cpp b/compiler/libsolidity/parsing/Parser.cpp index d87bda93..9aca0e4d 100644 --- a/compiler/libsolidity/parsing/Parser.cpp +++ b/compiler/libsolidity/parsing/Parser.cpp @@ -661,6 +661,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari result.responsible = true; m_scanner->next(); } + else if (token == Token::Assembly) + { + if (result.assembly) + parserError(228_error, "assembly already specified."); + + result.assembly = true; + m_scanner->next(); + } else break; } @@ -735,7 +743,11 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) advance(); else { - block = parseBlock(); + if (header.assembly) { + block = parseAssemblyBlock(); + } else { + block = parseBlock(); + } nodeFactory.setEndPositionFromNode(block); } return nodeFactory.createNode( @@ -756,7 +768,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) header.isInline, header.responsible, header.externalMsg, - header.internalMsg + header.internalMsg, + header.assembly ); } @@ -1372,6 +1385,31 @@ ASTPointer Parser::parseBlock(bool _allowUnchecked, ASTPointer return nodeFactory.createNode(_docString, unchecked, statements); } +ASTPointer Parser::parseAssemblyBlock(ASTPointer const& _docString) +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + expectToken(Token::LBrace); + ASTPointer statement; + try + { + statement = parseAssemblyStatement(); + nodeFactory.markEndPosition(); + } + catch (FatalError const&) + { + if ( + !m_errorReporter.hasErrors() || + !m_parserErrorRecovery || + m_errorReporter.hasExcessiveErrors() + ) + BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ + m_inParserRecovery = true; + } + expectToken(Token::RBrace); + return nodeFactory.createNode(_docString, false, std::vector>{statement}); +} + ASTPointer Parser::parseStatement(bool _allowUnchecked) { RecursionGuard recursionGuard(*this); @@ -1473,6 +1511,27 @@ ASTPointer Parser::parseStatement(bool _allowUnchecked) return statement; } +ASTPointer Parser::parseAssemblyStatement() +{ + ASTPointer docString; + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + ASTPointer expression; + std::vector> lines; + while ( + m_scanner->currentToken() == Token::StringLiteral || + m_scanner->currentToken() == Token::UnicodeStringLiteral || + m_scanner->currentToken() == Token::HexStringLiteral + ) { + lines.push_back(parsePrimaryExpression()); + if (m_scanner->currentToken() == Token::Comma) { + advance(); + } + } + nodeFactory.markEndPosition(); + return nodeFactory.createNode(docString, lines); +} + ASTPointer Parser::parseInlineAssembly(ASTPointer const& /*_docString*/) { m_errorReporter.fatalTypeError(228_error, currentLocation(), "Inline assembly is disabled."); diff --git a/compiler/libsolidity/parsing/Parser.h b/compiler/libsolidity/parsing/Parser.h index 4b300846..855cf02b 100644 --- a/compiler/libsolidity/parsing/Parser.h +++ b/compiler/libsolidity/parsing/Parser.h @@ -79,6 +79,7 @@ class Parser: public langutil::ParserBase bool responsible = false; bool externalMsg = false; bool internalMsg = false; + bool assembly = false; }; /// Struct to share parsed function call arguments. @@ -133,7 +134,9 @@ class Parser: public langutil::ParserBase bool _allowEmpty = true ); ASTPointer parseBlock(bool _allowUncheckedBlock = false, ASTPointer const& _docString = {}); + ASTPointer parseAssemblyBlock(ASTPointer const& _docString = {}); ASTPointer parseStatement(bool _allowUncheckedBlock = false); + ASTPointer parseAssemblyStatement(); ASTPointer parseInlineAssembly(ASTPointer const& _docString = {}); ASTPointer parseIfStatement(ASTPointer const& _docString); ASTPointer parseTryStatement(ASTPointer const& _docString); diff --git a/lib/stdlib_sol.tvm b/lib/stdlib_sol.tvm index c4430a64..f80d6998 100644 --- a/lib/stdlib_sol.tvm +++ b/lib/stdlib_sol.tvm @@ -1880,7 +1880,7 @@ PUSHCONT { .loc stdlib.sol, 418 PUSH3 S1, S3, S0 LSHIFT 3 - UFITS 16 + UFITS 10 LDSLICEX POP S6 STSLICER diff --git a/sold/Cargo.toml b/sold/Cargo.toml index 4e98ba1d..5425c8da 100644 --- a/sold/Cargo.toml +++ b/sold/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2021' name = 'sold' -version = '0.69.0' +version = '0.70.0' [[bin]] name = 'sold' @@ -16,10 +16,10 @@ serde_json = { features = [ 'unbounded_depth' ], version = '1.0' } strip-ansi-escapes = '0.1' clap = { features = [ 'derive' ], version = '4.2' } serde = { features = [ 'derive' ], version = '1.0' } -ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.3.80' } -ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.43' } -ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.2' } -tvm_linker = { git = 'https://github.com/tonlabs/TVM-linker.git', tag = '0.20.1' } +ton_abi = { git = 'https://github.com/tonlabs/ever-abi.git', tag = '2.3.108' } +ton_block = { git = 'https://github.com/tonlabs/ever-block.git', tag = '1.9.70' } +ton_types = { git = 'https://github.com/tonlabs/ever-types.git', tag = '2.0.12' } +tvm_linker = { git = 'https://github.com/tonlabs/TVM-linker.git', tag = '0.20.3' } [build-dependencies] cmake = '0.1'