diff --git a/.circleci/config.yml b/.circleci/config.yml index 0319ca1529fe..23da90b8f35a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,6 +122,10 @@ defaults: name: command line tests command: ./test/cmdlineTests.sh + - run_docs_pragma_min_version: &run_docs_pragma_min_version + name: docs pragma version check + command: ./scripts/docs_version_pragma_check.sh + - test_ubuntu1604_clang: &test_ubuntu1604_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1604-clang-ossfuzz-<< pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image-rev >> @@ -303,6 +307,18 @@ jobs: name: Linting Python Scripts command: ./scripts/pylint_all.py + chk_antlr_grammar: + docker: + - image: buildpack-deps:eoan + steps: + - checkout + - run: + name: Install Java + command: apt -q update && apt install -y openjdk-14-jdk + - run: + name: Run tests + command: ./scripts/test_antlr_grammar.sh + chk_buglist: docker: - image: circleci/node @@ -335,6 +351,15 @@ jobs: pip install --user z3-solver - run: *run_proofs + chk_docs_pragma_min_version: + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + environment: + TERM: xterm + steps: + - checkout + - run: *run_docs_pragma_min_version + b_ubu_clang: &build_ubuntu1904_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >> @@ -768,6 +793,8 @@ workflows: - chk_buglist: *workflow_trigger_on_tags - chk_proofs: *workflow_trigger_on_tags - chk_pylint: *workflow_trigger_on_tags + - chk_antlr_grammar: *workflow_trigger_on_tags + - chk_docs_pragma_min_version: *workflow_trigger_on_tags # build-only - b_docs: *workflow_trigger_on_tags diff --git a/.clang-format b/.clang-format index f9953c17b346..edd5de4d26bc 100644 --- a/.clang-format +++ b/.clang-format @@ -6,26 +6,35 @@ # Note that clang-format cannot express the style that closing parentheses # behave similar to closing curly braces in a multi-line setting in that # they have to be on a line of their own at the same indentation level -# as the opening part. +# as the opening part (aka "dangling parenthesis", see https://reviews.llvm.org/D33029). Language: Cpp BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak AlignEscapedNewlinesLeft: true AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: All BreakBeforeBraces: Allman ColumnLimit: 120 ContinuationIndentWidth: 4 +FixNamespaceComments: false IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 2 PenaltyBreakBeforeFirstCallParameter: 2000 +PointerAlignment: Left SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false TabWidth: 4 -UseTab: ForIndentation +UseTab: Always # Local Variables: # mode: yaml diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md index 410b42e00558..e69de29bb2d1 100644 --- a/.github/ISSUE_TEMPLATE/general.md +++ b/.github/ISSUE_TEMPLATE/general.md @@ -1,21 +0,0 @@ ---- -name: General Feedback -about: Any general feedback (neither feature request nor bug reports) ---- - - -## Description - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9fdbf158eeab..000000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ - - -### Description - - - -### Checklist -- [ ] Code compiles correctly -- [ ] All tests are passing -- [ ] New tests have been created which fail without the change (if possible) -- [ ] README / documentation was extended, if necessary -- [ ] Changelog entry (if change is visible to the user) -- [ ] Used meaningful commit messages diff --git a/CMakeLists.txt b/CMakeLists.txt index 4278770f1dee..dba182345002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,9 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.6.4") +set(PROJECT_VERSION "0.6.5") +# OSX target needed in order to support std::visit +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 6bdb48fba502..78b2ab310312 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -141,7 +141,7 @@ struct MeanSigma double const d = 0; int i = 0; int j = 0; -char* s; +char* s = nullptr; MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a); Derived* x = dynamic_cast(base); for (auto i = x->begin(); i != x->end(); ++i) {} diff --git a/Changelog.md b/Changelog.md index 19e89adf5ebf..17d4fd0aec93 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,28 @@ +### 0.6.5 (2020-04-06) + +Important Bugfixes: + * Code Generator: Restrict the length of dynamic memory arrays to 64 bits during creation at runtime fixing a possible overflow. + + +Language Features: + * Allow local storage variables to be declared without initialization, as long as they are assigned before they are accessed. + * State variables can be marked ``immutable`` which causes them to be read-only, but assignable in the constructor. The value will be stored directly in the code. + + +Compiler Features: + * Commandline Interface: Enable output of storage layout with `--storage-layout`. + * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. + + +Bugfixes: + * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. + * Inline Assembly: Fix internal error when accessing invalid constant variables. + * Inline Assembly: Fix internal error when accessing functions. + * JSON AST: Always add pointer suffix for memory reference types. + * Reference Resolver: Fix internal error when accessing invalid struct members. + * Type Checker: Fix internal errors when assigning nested tuples. + + ### 0.6.4 (2020-03-10) Language Features: @@ -18,7 +43,6 @@ Bugfixes: * SMTChecker: Fix internal errors when analysing tuples. * Yul AST Import: correctly import blocks as statements, switch statements and string literals. - ### 0.6.3 (2020-02-18) Language Features: @@ -32,6 +56,7 @@ Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. * Yul Optimizer: Prune functions that call each other but are otherwise unreferenced. + * SMTChecker: CHC support to internal function calls. Bugfixes: @@ -120,6 +145,12 @@ Compiler Features: * ABIEncoderV2: Do not warn about enabled ABIEncoderV2 anymore (the pragma is still needed, though). +### 0.5.17 (2020-03-17) + +Bugfixes: + * Type Checker: Disallow overriding of private functions. + + ### 0.5.16 (2020-01-02) Backported Bugfixes: diff --git a/README.md b/README.md index c0a98aefa7c2..c3636ff064d6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Solidity is a statically typed, contract-oriented, high-level language for imple - [Development](#development) - [Maintainers](#maintainers) - [License](#license) +- [Security](#security) ## Background @@ -75,3 +76,7 @@ releases [in the projects section](https://github.com/ethereum/solidity/projects Solidity is licensed under [GNU General Public License v3.0](LICENSE.txt). Some third-party code has its [own licensing terms](cmake/templates/license.h.in). + +## Security + +The security policy may be [found here](SECURITY.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..dd39453e853b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +The Solidity team and community take all security bugs in Solidity seriously. +We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. + +## Scope + +Bugs in the Solidity repository are in scope. +Bugs in third-party dependencies e.g., jsoncpp, boost etc. are not in scope unless they result in a Solidity specific bug. + +Only bugs that have a demonstrable security impact on smart contracts are in scope. +For example, a Solidity program whose optimization is incorrect (e.g., leads to an incorrect output) qualifies as a security bug. +Please note that the [rules][2] of the [Ethereum bounty program][1] have precedence over this security policy. + +## Supported Versions + +As a general rule, only the latest release gets security updates. +Exceptions may be made when the current breaking release is relatively new, e.g. less than three months old. +If you are reporting a bug, please state clearly the Solidity version(s) it affects. + +Example 1: Assuming the current release is `0.6.3` and a security bug has been found in it that affects both `0.5.x` and `0.6.x` trees, we may not only patch `0.6.3` (the bug-fix release numbered `0.6.4`) but `0.5.x` as well (the bug-fix release numbered `0.5.(x+1)`). + +Example 2: Assuming the current release is `0.6.25` and a security bug has been found in it, we may only patch `0.6.25` (in the bug-fix release numbered `0.6.26`) even if the bug affects a previous tree such as `0.5.x`. + +## Reporting a Vulnerability + +To report a vulnerability, please follow the instructions stated in the [Ethereum bounty program][1]. + +In the bug report, please include all details necessary to reproduce the vulnerability such as: + +- Input program that triggers the bug +- Compiler version affected +- Target EVM version +- Framework/IDE if applicable +- EVM execution environment/client if applicable +- Operating system + +Please include steps to reproduce the bug you have found in as much detail as possible. + +Once we have received your bug report, we will try to reproduce it and provide a more detailed response. +Once the reported bug has been successfully reproduced, the Solidity team will work on a fix. + +The Solidity team maintains the following JSON-formatted lists of patched security vulnerabilities: + +- [Summary of known security vulnerabilities][3] +- [List of security vulnerabilities affecting a specific version of the compiler][4]. + + +[1]: https://bounty.ethereum.org/ +[2]: https://bounty.ethereum.org/#rules +[3]: https://solidity.readthedocs.io/en/develop/bugs.html +[4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index cc4489e9faee..b36af7e3f8ed 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -26,6 +26,17 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) # Prevent the path of the source directory from ending up in the binary via __FILE__ macros. eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity") +# -Wpessimizing-move warns when a call to std::move would prevent copy elision +# if the argument was not wrapped in a call. This happens when moving a local +# variable in a return statement when the variable is the same type as the +# return type or using a move to create a new object from a temporary object. +eth_add_cxx_compiler_flag_if_supported(-Wpessimizing-move) + +# -Wredundant-move warns when an implicit move would already be made, so the +# std::move call is not needed, such as when moving a local variable in a return +# that is different from the return type. +eth_add_cxx_compiler_flag_if_supported(-Wredundant-move) + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Enables all the warnings about constructions that some users consider questionable, # and that are easy to avoid. Also enable some extra warning flags that are not diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index ede846cc399c..9461cb6bd6bd 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -292,8 +292,9 @@ Consider you have the following pre-0.5.0 contract already deployed: :: - // This will not compile with the current version of the compiler pragma solidity ^0.4.25; + // This will report a warning until version 0.4.25 of the compiler + // This will not compile after 0.5.0 contract OldContract { function someOldFunction(uint8 a) { //... @@ -369,8 +370,8 @@ Old version: :: - // This will not compile pragma solidity ^0.4.25; + // This will not compile after 0.5.0 contract OtherContract { uint x; @@ -396,7 +397,7 @@ Old version: // Throw is fine in this version. if (x > 100) throw; - bytes b = new bytes(x); + bytes memory b = new bytes(x); y = -3 >> 1; // y == -1 (wrong, should be -2) do { @@ -431,14 +432,15 @@ New version: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.5.99; + // This will not compile after 0.6.0 contract OtherContract { uint x; function f(uint y) external { x = y; } - receive() payable external {} + function() payable external {} } contract New { diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 new file mode 100644 index 000000000000..8721f47a4dd8 --- /dev/null +++ b/docs/Solidity.g4 @@ -0,0 +1,482 @@ +// Copyright 2020 Gonçalo Sá +// Copyright 2016-2019 Federico Bond +// Licensed under the MIT license. See LICENSE file in the project root for details. + +// This grammar is much less strict than what Solidity currently parses +// to allow this to pass with older versions of Solidity. + +grammar Solidity; + +sourceUnit + : (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ; + +pragmaDirective + : 'pragma' pragmaName pragmaValue ';' ; + +pragmaName + : identifier ; + +pragmaValue + : version | expression ; + +version + : versionConstraint versionConstraint? ; + +versionConstraint + : versionOperator? VersionLiteral ; + +versionOperator + : '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ; + +importDirective + : 'import' StringLiteralFragment ('as' identifier)? ';' + | 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';' + | 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ; + +importDeclaration + : identifier ('as' identifier)? ; + +contractDefinition + : 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier + ( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )? + '{' contractPart* '}' ; + +inheritanceSpecifier + : userDefinedTypeName ( '(' expressionList? ')' )? ; + +contractPart + : stateVariableDeclaration + | usingForDeclaration + | structDefinition + | modifierDefinition + | functionDefinition + | eventDefinition + | enumDefinition ; + +stateVariableDeclaration + : typeName + ( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )* + identifier ('=' expression)? ';' ; + +overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ; + +usingForDeclaration + : 'using' identifier 'for' ('*' | typeName) ';' ; + +structDefinition + : 'struct' identifier + '{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ; + +modifierDefinition + : 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* block ; + +functionDefinition + : functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ; + +functionDescriptor + : 'function' ( identifier | ReceiveKeyword | FallbackKeyword )? + | ConstructorKeyword + | FallbackKeyword + | ReceiveKeyword ; + +returnParameters + : 'returns' parameterList ; + +modifierList + : ( modifierInvocation | stateMutability | ExternalKeyword + | PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ; + +modifierInvocation + : identifier ( '(' expressionList? ')' )? ; + +eventDefinition + : 'event' identifier eventParameterList AnonymousKeyword? ';' ; + +enumDefinition + : 'enum' identifier '{' enumValue? (',' enumValue)* '}' ; + +enumValue + : identifier ; + +parameterList + : '(' ( parameter (',' parameter)* )? ')' ; + +parameter + : typeName storageLocation? identifier? ; + +eventParameterList + : '(' ( eventParameter (',' eventParameter)* )? ')' ; + +eventParameter + : typeName IndexedKeyword? identifier? ; + +variableDeclaration + : typeName storageLocation? identifier ; + +typeName + : elementaryTypeName + | userDefinedTypeName + | mapping + | typeName '[' expression? ']' + | functionTypeName ; + +userDefinedTypeName + : identifier ( '.' identifier )* ; + +mapping + : 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ; + +functionTypeName + : 'function' parameterList modifierList returnParameters? ; + +storageLocation + : 'memory' | 'storage' | 'calldata'; + +stateMutability + : PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ; + +block + : '{' statement* '}' ; + +statement + : ifStatement + | tryStatement + | whileStatement + | forStatement + | block + | inlineAssemblyStatement + | doWhileStatement + | continueStatement + | breakStatement + | returnStatement + | throwStatement + | emitStatement + | simpleStatement ; + +expressionStatement + : expression ';' ; + +ifStatement + : 'if' '(' expression ')' statement ( 'else' statement )? ; + +tryStatement : 'try' expression returnParameters? block catchClause+ ; + +// In reality catch clauses still are not processed as below +// the identifier can only be a set string: "Error". But plans +// of the Solidity team include possible expansion so we'll +// leave this as is, befitting with the Solidity docs. +catchClause : 'catch' ( identifier? parameterList )? block ; + +whileStatement + : 'while' '(' expression ')' statement ; + +forStatement + : 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ; + +simpleStatement + : ( variableDeclarationStatement | expressionStatement ) ; + +inlineAssemblyStatement + : 'assembly' StringLiteralFragment? assemblyBlock ; + +doWhileStatement + : 'do' statement 'while' '(' expression ')' ';' ; + +continueStatement + : 'continue' ';' ; + +breakStatement + : 'break' ';' ; + +returnStatement + : 'return' expression? ';' ; + +// throw is no longer supported by latest Solidity. +throwStatement + : 'throw' ';' ; + +emitStatement + : 'emit' functionCall ';' ; + +// 'var' is no longer supported by latest Solidity. +variableDeclarationStatement + : ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';'; + +variableDeclarationList + : variableDeclaration? (',' variableDeclaration? )* ; + +identifierList + : '(' ( identifier? ',' )* identifier? ')' ; + +elementaryTypeName + : 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ; + +Int + : 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ; + +Uint + : 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ; + +Byte + : 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ; + +Fixed + : 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ; + +Ufixed + : 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ; + +expression + : expression ('++' | '--') + | 'new' typeName + | expression '[' expression? ']' + | expression '[' expression? ':' expression? ']' + | expression '.' identifier + | expression '{' nameValueList '}' + | expression '(' functionCallArguments ')' + | PayableKeyword '(' expression ')' + | '(' expression ')' + | ('++' | '--') expression + | ('+' | '-') expression + | ('after' | 'delete') expression + | '!' expression + | '~' expression + | expression '**' expression + | expression ('*' | '/' | '%') expression + | expression ('+' | '-') expression + | expression ('<<' | '>>') expression + | expression '&' expression + | expression '^' expression + | expression '|' expression + | expression ('<' | '>' | '<=' | '>=') expression + | expression ('==' | '!=') expression + | expression '&&' expression + | expression '||' expression + | expression '?' expression ':' expression + | expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression + | primaryExpression ; + +primaryExpression + : BooleanLiteral + | numberLiteral + | hexLiteral + | stringLiteral + | identifier ('[' ']')? + | TypeKeyword + | tupleExpression + | typeNameExpression ('[' ']')? ; + +expressionList + : expression (',' expression)* ; + +nameValueList + : nameValue (',' nameValue)* ','? ; + +nameValue + : identifier ':' expression ; + +functionCallArguments + : '{' nameValueList? '}' + | expressionList? ; + +functionCall + : expression '(' functionCallArguments ')' ; + +tupleExpression + : '(' ( expression? ( ',' expression? )* ) ')' + | '[' ( expression ( ',' expression )* )? ']' ; + +typeNameExpression + : elementaryTypeName + | userDefinedTypeName ; + +assemblyItem + : identifier + | assemblyBlock + | assemblyExpression + | assemblyLocalDefinition + | assemblyAssignment + | assemblyStackAssignment + | labelDefinition + | assemblySwitch + | assemblyFunctionDefinition + | assemblyFor + | assemblyIf + | BreakKeyword + | ContinueKeyword + | LeaveKeyword + | subAssembly + | numberLiteral + | stringLiteral + | hexLiteral ; + +assemblyBlock + : '{' assemblyItem* '}' ; + +assemblyExpression + : assemblyCall | assemblyLiteral ; + +assemblyCall + : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; + +assemblyLocalDefinition + : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; + +assemblyAssignment + : assemblyIdentifierList ':=' assemblyExpression ; + +assemblyIdentifierList + : identifier ( ',' identifier )* ; + +assemblyStackAssignment + : '=:' identifier ; + +labelDefinition + : identifier ':' ; + +assemblySwitch + : 'switch' assemblyExpression assemblyCase* ; + +assemblyCase + : 'case' assemblyLiteral assemblyType? assemblyBlock + | 'default' assemblyBlock ; + +assemblyFunctionDefinition + : 'function' identifier '(' assemblyTypedVariableList? ')' + assemblyFunctionReturns? assemblyBlock ; + +assemblyFunctionReturns + : ( '-' '>' assemblyTypedVariableList ) ; + +assemblyFor + : 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ; + +assemblyIf + : 'if' assemblyExpression assemblyBlock ; + +assemblyLiteral + : ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ; + +assemblyTypedVariableList + : identifier assemblyType? ( ',' assemblyTypedVariableList )? ; + +assemblyType + : ':' identifier ; + +subAssembly + : 'assembly' identifier assemblyBlock ; + +numberLiteral + : (DecimalNumber | HexNumber) NumberUnit? ; + +identifier + : ('from' | 'calldata' | 'address' | Identifier) ; + +BooleanLiteral + : 'true' | 'false' ; + +DecimalNumber + : ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ; + +fragment +DecimalDigits + : [0-9] ( '_'? [0-9] )* ; + +HexNumber + : '0' [xX] HexDigits ; + +fragment +HexDigits + : HexCharacter ( '_'? HexCharacter )* ; + +NumberUnit + : 'wei' | 'szabo' | 'finney' | 'ether' + | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; + +HexLiteralFragment + : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; + +hexLiteral : HexLiteralFragment+ ; + +fragment +HexPair + : HexCharacter HexCharacter ; + +fragment +HexCharacter + : [0-9A-Fa-f] ; + +ReservedKeyword + : 'after' + | 'case' + | 'default' + | 'final' + | 'in' + | 'inline' + | 'let' + | 'match' + | 'null' + | 'of' + | 'relocatable' + | 'static' + | 'switch' + | 'typeof' ; + +AnonymousKeyword : 'anonymous' ; +BreakKeyword : 'break' ; +ConstantKeyword : 'constant' ; +ImmutableKeyword : 'immutable' ; +ContinueKeyword : 'continue' ; +LeaveKeyword : 'leave' ; +ExternalKeyword : 'external' ; +IndexedKeyword : 'indexed' ; +InternalKeyword : 'internal' ; +PayableKeyword : 'payable' ; +PrivateKeyword : 'private' ; +PublicKeyword : 'public' ; +VirtualKeyword : 'virtual' ; +PureKeyword : 'pure' ; +TypeKeyword : 'type' ; +ViewKeyword : 'view' ; + +ConstructorKeyword : 'constructor' ; +FallbackKeyword : 'fallback' ; +ReceiveKeyword : 'receive' ; + +Identifier + : IdentifierStart IdentifierPart* ; + +fragment +IdentifierStart + : [a-zA-Z$_] ; + +fragment +IdentifierPart + : [a-zA-Z0-9$_] ; + +stringLiteral + : StringLiteralFragment+ ; + +StringLiteralFragment + : '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\'' ; + +fragment +DoubleQuotedStringCharacter + : ~["\r\n\\] | ('\\' .) ; + +fragment +SingleQuotedStringCharacter + : ~['\r\n\\] | ('\\' .) ; + +VersionLiteral + : [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ; + +WS + : [ \t\r\n\u000C]+ -> skip ; + +COMMENT + : '/*' .*? '*/' -> channel(HIDDEN) ; + +LINE_COMMENT + : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index a3bd78319a5d..d645a3d44d33 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -234,7 +234,6 @@ Given the contract: pragma solidity >=0.4.16 <0.7.0; - contract Foo { function bar(bytes3[2] memory) public pure {} function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; } @@ -583,12 +582,11 @@ As an example, the code pragma solidity >=0.4.19 <0.7.0; pragma experimental ABIEncoderV2; - contract Test { struct S { uint a; uint[] b; T[] c; } struct T { uint x; uint y; } - function f(S memory s, T memory t, uint a) public {} - function g() public returns (S memory s, T memory t, uint a) {} + function f(S memory, T memory, uint) public pure {} + function g() public pure returns (S memory, T memory, uint) {} } would result in the JSON: diff --git a/docs/assembly.rst b/docs/assembly.rst index f908c0f5769c..19e4449e6c12 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -41,7 +41,7 @@ without a compiler change. .. code:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; library GetCode { function at(address _addr) public view returns (bytes memory o_code) { @@ -136,7 +136,7 @@ Local Solidity variables are available for assignments, for example: .. code:: - pragma solidity >=0.4.11 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint b; diff --git a/docs/bugs.json b/docs/bugs.json index 66f1808abd79..066e17e93b8a 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,12 @@ [ + { + "name": "MemoryArrayCreationOverflow", + "summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.", + "description": "No runtime overflow checks were performed for the length of memory arrays during creation. In cases for which the memory size of an array in bytes, i.e. the array length times 32, is larger than 2^256-1, the memory allocation will overflow, potentially resulting in overlapping memory areas. The length of the array is still stored correctly, so copying or iterating over such an array will result in out-of-gas.", + "introduced": "0.2.0", + "fixed": "0.6.5", + "severity": "low" + }, { "name": "YulOptimizerRedundantAssignmentBreakContinue", "summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.", @@ -10,6 +18,14 @@ "yulOptimizer": true } }, + { + "name": "privateCanBeOverridden", + "summary": "Private methods can be overridden by inheriting contracts.", + "description": "While private methods of base contracts are not visible and cannot be called directly from the derived contract, it is still possible to declare a function of the same name and type and thus change the behaviour of the base contract's function.", + "introduced": "0.3.0", + "fixed": "0.5.17", + "severity": "low" + }, { "name": "YulOptimizerRedundantAssignmentBreakContinue0.5", "summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index dc32e2687d33..b123930167b7 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -151,6 +151,7 @@ }, "0.2.0": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -171,6 +172,7 @@ }, "0.2.1": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -191,6 +193,7 @@ }, "0.2.2": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -211,6 +214,8 @@ }, "0.3.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -232,6 +237,8 @@ }, "0.3.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -252,6 +259,8 @@ }, "0.3.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -272,6 +281,8 @@ }, "0.3.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -291,6 +302,8 @@ }, "0.3.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -310,6 +323,8 @@ }, "0.3.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -329,6 +344,8 @@ }, "0.3.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -346,6 +363,8 @@ }, "0.4.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -363,6 +382,8 @@ }, "0.4.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -380,6 +401,8 @@ }, "0.4.10": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -395,6 +418,8 @@ }, "0.4.11": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -409,6 +434,8 @@ }, "0.4.12": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -422,6 +449,8 @@ }, "0.4.13": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -435,6 +464,8 @@ }, "0.4.14": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -447,6 +478,8 @@ }, "0.4.15": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -458,6 +491,8 @@ }, "0.4.16": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -471,6 +506,8 @@ }, "0.4.17": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -485,6 +522,8 @@ }, "0.4.18": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -498,6 +537,8 @@ }, "0.4.19": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -512,6 +553,8 @@ }, "0.4.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -528,6 +571,8 @@ }, "0.4.20": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -542,6 +587,8 @@ }, "0.4.21": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -556,6 +603,8 @@ }, "0.4.22": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -570,6 +619,8 @@ }, "0.4.23": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -583,6 +634,8 @@ }, "0.4.24": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -596,6 +649,8 @@ }, "0.4.25": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -607,6 +662,8 @@ }, "0.4.26": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2" @@ -615,6 +672,8 @@ }, "0.4.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -630,6 +689,8 @@ }, "0.4.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -644,6 +705,8 @@ }, "0.4.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -660,6 +723,8 @@ }, "0.4.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -675,6 +740,8 @@ }, "0.4.7": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -690,6 +757,8 @@ }, "0.4.8": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -705,6 +774,8 @@ }, "0.4.9": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -720,6 +791,8 @@ }, "0.5.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -731,6 +804,8 @@ }, "0.5.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -742,6 +817,8 @@ }, "0.5.10": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers" ], @@ -749,24 +826,32 @@ }, "0.5.11": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-08-12" }, "0.5.12": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-10-01" }, "0.5.13": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-11-14" }, "0.5.14": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2LoopYulOptimizer" ], @@ -774,16 +859,29 @@ }, "0.5.15": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-12-17" }, "0.5.16": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden" + ], "released": "2020-01-02" }, + "0.5.17": { + "bugs": [ + "MemoryArrayCreationOverflow" + ], + "released": "2020-03-17" + }, "0.5.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -795,6 +893,8 @@ }, "0.5.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -806,6 +906,8 @@ }, "0.5.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -817,6 +919,8 @@ }, "0.5.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -830,6 +934,8 @@ }, "0.5.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -843,6 +949,8 @@ }, "0.5.7": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -854,6 +962,8 @@ }, "0.5.8": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -864,6 +974,8 @@ }, "0.5.9": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -873,24 +985,37 @@ }, "0.6.0": { "bugs": [ + "MemoryArrayCreationOverflow", "YulOptimizerRedundantAssignmentBreakContinue" ], "released": "2019-12-17" }, "0.6.1": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-02" }, "0.6.2": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-27" }, "0.6.3": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-02-18" }, "0.6.4": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-03-10" + }, + "0.6.5": { + "bugs": [], + "released": "2020-04-06" } } \ No newline at end of file diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst index 51db0dfbedeb..7b9f8325613b 100644 --- a/docs/contracts/abstract-contracts.rst +++ b/docs/contracts/abstract-contracts.rst @@ -13,7 +13,7 @@ This can be done by using the ``abstract`` keyword as shown in the following exa defined as abstract, because the function ``utterance()`` was defined, but no implementation was provided (no implementation body ``{ }`` was given).:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; abstract contract Feline { function utterance() public virtual returns (bytes32); diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33511f020d47..7a0a7380a555 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -1,11 +1,49 @@ .. index:: ! constant -************************ -Constant State Variables -************************ +************************************** +Constant and Immutable State Variables +************************************** -State variables can be declared as ``constant``. In this case, they have to be -assigned from an expression which is a constant at compile time. Any expression +State variables can be declared as ``constant`` or ``immutable``. +In both cases, the variables cannot be modified after the contract has been constructed. +For ``constant`` variables, the value has to be fixed at compile-time, while +for ``immutable``, it can still be assigned at construction time. + +The compiler does not reserve a storage slot for these variables, and every occurrence is +replaced by the respective value. + +Not all types for constants and immutables are implemented at this time. The only supported types are +`strings `_ (only for constants) and `value types `_. + +:: + + pragma solidity >0.6.4 <0.7.0; + + contract C { + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); + uint immutable decimals; + uint immutable maxBalance; + address immutable owner = msg.sender; + + constructor(uint _decimals, address _reference) public { + decimals = _decimals; + // Assignments to immutables can even access the environment. + maxBalance = _reference.balance; + } + + function isBalanceTooHigh(address _other) public view returns (bool) { + return _other.balance > maxBalance; + } + } + + +Constant +======== + +For ``constant`` variables, the value has to be a constant at compile time and it has to be +assigned where the variable is declared. Any expression that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or ``block.number``) or execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions @@ -18,18 +56,17 @@ The reason behind allowing side-effects on the memory allocator is that it should be possible to construct complex objects like e.g. lookup-tables. This feature is not yet fully usable. -The compiler does not reserve a storage slot for these variables, and every occurrence is -replaced by the respective constant expression (which might be computed to a single value by the optimizer). - -Not all types for constants are implemented at this time. The only supported types are -value types and strings. +Immutable +========= -:: +Variables declared as ``immutable`` are a bit less restricted than those +declared as ``constant``: Immutable variables can be assigned an arbitrary +value in the constructor of the contract or at the point of their declaration. +They cannot be read during construction time and can only be assigned once. - pragma solidity >=0.4.0 <0.7.0; - - contract C { - uint constant X = 32**22 + 8; - string constant TEXT = "abc"; - bytes32 constant MY_HASH = keccak256("abc"); - } +The contract creation code generated by the compiler will modify the +contract's runtime code before it is returned by replacing all references +to immutables by the values assigned to the them. This is important if +you are comparing the +runtime code generated by the compiler with the one actually stored in the +blockchain. diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 661663028148..8b9e7df6439f 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract Test { // This function is called for all messages sent to diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 18c369df4f8d..d3dd0f1647db 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -154,7 +154,7 @@ A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it explicitly in the final override, but this function will bypass ``Base1.destroy``. The way around this is to use ``super``:: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract owned { constructor() public { owner = msg.sender; } @@ -204,7 +204,7 @@ use the ``override`` keyword in the function header as shown in this example: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base { @@ -227,7 +227,7 @@ bases, it has to explicitly override it: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base1 { @@ -253,7 +253,7 @@ that already overrides all other functions. :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract A { function f() public pure{} } contract B is A {} @@ -293,7 +293,7 @@ of the variable: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract A { @@ -324,7 +324,7 @@ and the ``override`` keyword must be used in the overriding modifier: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base { @@ -342,7 +342,7 @@ explicitly: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base1 { @@ -498,7 +498,7 @@ One area where inheritance linearization is especially important and perhaps not :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract Base1 { constructor() public {} diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst index 727809460127..bce974502f90 100644 --- a/docs/contracts/interfaces.rst +++ b/docs/contracts/interfaces.rst @@ -22,7 +22,7 @@ Interfaces are denoted by their own keyword: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; interface Token { enum TokenType { Fungible, NonFungible } @@ -42,7 +42,7 @@ inheritance. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; interface ParentA { function test() external returns (uint256); diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 803e53f081df..86561ea7b2e4 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -47,12 +47,14 @@ more advanced example to implement a set). :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // We define a new struct datatype that will be used to // hold its data in the calling contract. - struct Data { mapping(uint => bool) flags; } + struct Data { + mapping(uint => bool) flags; + } library Set { // Note that the first parameter is of type "storage @@ -123,7 +125,7 @@ custom types without the overhead of external function calls: :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; struct bigint { uint[] limbs; @@ -237,7 +239,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow :: - pragma solidity >0.5.13 <0.7.0; + pragma solidity >=0.5.14 <0.7.0; library L { function f(uint256) external {} diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst index 32aa71799822..9e63abcf4ed7 100644 --- a/docs/contracts/using-for.rst +++ b/docs/contracts/using-for.rst @@ -29,7 +29,7 @@ may only be used inside a contract, not inside any of its functions. Let us rewrite the set example from the :ref:`libraries` in this way:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // This is the same code as before, just without comments diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 3a0905155f10..5ef105784b08 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -68,7 +68,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint private data; @@ -112,7 +112,7 @@ when they are declared. :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint public data = 42; @@ -151,7 +151,7 @@ to write a function, for example: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract arrayExample { // public state variable diff --git a/docs/control-structures.rst b/docs/control-structures.rst index d5448d0b30ed..7bbe290ba35e 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -41,7 +41,7 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { function g(uint a) public pure returns (uint ret) { return a + f(); } @@ -82,7 +82,7 @@ to the total balance of that contract: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } @@ -160,7 +160,7 @@ Those parameters will still be present on the stack, but they are inaccessible. :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { // omitted name for parameter @@ -183,7 +183,7 @@ is compiled so recursive creation-dependencies are not possible. :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract D { uint public x; @@ -238,7 +238,7 @@ which only need to be created if there is a dispute. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract D { uint public x; @@ -307,7 +307,7 @@ groupings of expressions. :: - pragma solidity >0.4.23 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract C { uint index; @@ -352,7 +352,7 @@ because only a reference and not a copy is passed. :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { uint[20] x; diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 92a7559738f8..f2034272a889 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -24,7 +24,7 @@ to receive their money - contracts cannot activate themselves. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract SimpleAuction { // Parameters of the auction. Times are either @@ -184,7 +184,7 @@ invalid bids. :: - pragma solidity >0.4.23 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract BlindAuction { struct Bid { diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index fe310f37a376..cd4084c84349 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -338,7 +338,7 @@ The full contract :: - pragma solidity >=0.4.24 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract SimplePaymentChannel { address payable public sender; // The account sending payments. diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst index a3d932b099e6..a95a675c61ff 100644 --- a/docs/examples/modular.rst +++ b/docs/examples/modular.rst @@ -19,7 +19,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; library Balances { function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index d79c6526e36f..caafaa7edbbe 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -25,7 +25,7 @@ you can use state machine-like constructs inside a contract. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract Purchase { uint public value; diff --git a/docs/grammar.txt b/docs/grammar.txt deleted file mode 100644 index b8b2893a22c5..000000000000 --- a/docs/grammar.txt +++ /dev/null @@ -1,193 +0,0 @@ -SourceUnit = (PragmaDirective | ImportDirective | ContractDefinition)* - -// Pragma actually parses anything up to the trailing ';' to be fully forward-compatible. -PragmaDirective = 'pragma' Identifier ([^;]+) ';' - -ImportDirective = 'import' StringLiteral ('as' Identifier)? ';' - | 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' - | 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' - -ContractDefinition = 'abstract'? ( 'contract' | 'library' | 'interface' ) Identifier - ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? - '{' ContractPart* '}' - -ContractPart = StateVariableDeclaration | UsingForDeclaration - | StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition - -InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )? - -StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' | OverrideSpecifier )* Identifier ('=' Expression)? ';' -UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' -StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}' - -ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block -ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? - -FunctionDefinition = FunctionDescriptor ParameterList - ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )* - ( 'returns' ParameterList )? ( ';' | Block ) - -FunctionDescriptor = 'function' Identifier | 'constructor' | 'fallback' | 'receive' - -OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )? - -EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';' - -EnumValue = Identifier -EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' - -ParameterList = '(' ( Parameter (',' Parameter)* )? ')' -Parameter = TypeName StorageLocation? Identifier? - -EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')' -EventParameter = TypeName 'indexed'? Identifier? - -FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')' -FunctionTypeParameter = TypeName StorageLocation? - -// semantic restriction: mappings and structs (recursively) containing mappings -// are not allowed in argument lists -VariableDeclaration = TypeName StorageLocation? Identifier - -TypeName = ElementaryTypeName - | UserDefinedTypeName - | Mapping - | ArrayTypeName - | FunctionTypeName - | ( 'address' 'payable' ) - -UserDefinedTypeName = Identifier ( '.' Identifier )* - -Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')' -ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* - ( 'returns' FunctionTypeParameterList )? -StorageLocation = 'memory' | 'storage' | 'calldata' -StateMutability = 'pure' | 'view' | 'payable' - -Block = '{' Statement* '}' -Statement = IfStatement | TryStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | - ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | EmitStatement | SimpleStatement ) ';' - -ExpressionStatement = Expression -IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? -TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+ -CatchClause = 'catch' ( Identifier? ParameterList )? Block -WhileStatement = 'while' '(' Expression ')' Statement -PlaceholderStatement = '_' -SimpleStatement = VariableDefinition | ExpressionStatement -ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock -DoWhileStatement = 'do' Statement 'while' '(' Expression ')' -Continue = 'continue' -Break = 'break' -Return = 'return' Expression? -Throw = 'throw' -EmitStatement = 'emit' FunctionCall -VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )? - -// Precedence by order (see github.com/ethereum/solidity/pull/732) -Expression - = Expression ('++' | '--') - | NewExpression - | IndexAccess - | IndexRangeAccess - | MemberAccess - | FunctionCall - | Expression '{' NameValueList '}' - | '(' Expression ')' - | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression - | Expression '**' Expression - | Expression ('*' | '/' | '%') Expression - | Expression ('+' | '-') Expression - | Expression ('<<' | '>>') Expression - | Expression '&' Expression - | Expression '^' Expression - | Expression '|' Expression - | Expression ('<' | '>' | '<=' | '>=') Expression - | Expression ('==' | '!=') Expression - | Expression '&&' Expression - | Expression '||' Expression - | Expression '?' Expression ':' Expression - | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression - | PrimaryExpression - -PrimaryExpression = BooleanLiteral - | NumberLiteral - | HexLiteral - | StringLiteral - | TupleExpression - | Identifier - | ElementaryTypeNameExpression - -ExpressionList = Expression ( ',' Expression )* -NameValueList = Identifier ':' Expression ( ',' Identifier ':' Expression )* - -FunctionCall = Expression '(' FunctionCallArguments ')' -FunctionCallArguments = '{' NameValueList? '}' - | ExpressionList? - -NewExpression = 'new' TypeName -MemberAccess = Expression '.' Identifier -IndexAccess = Expression '[' Expression? ']' -IndexRangeAccess = Expression '[' Expression? ':' Expression? ']' - -BooleanLiteral = 'true' | 'false' -NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)? -NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' -HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') -StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' -Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]* - -HexNumber = '0x' [0-9a-fA-F]+ -DecimalNumber = [0-9]+ ( '.' [0-9]* )? ( [eE] [0-9]+ )? - -TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')' - | '[' ( Expression ( ',' Expression )* )? ']' - -ElementaryTypeNameExpression = ElementaryTypeName - -ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed - -Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' - -Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' - -Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' - -Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) - -Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) - - -AssemblyBlock = '{' AssemblyStatement* '}' - -AssemblyStatement = AssemblyBlock - | AssemblyFunctionDefinition - | AssemblyVariableDeclaration - | AssemblyAssignment - | AssemblyIf - | AssemblyExpression - | AssemblySwitch - | AssemblyForLoop - | AssemblyBreakContinue - | AssemblyLeave -AssemblyFunctionDefinition = - 'function' Identifier '(' AssemblyIdentifierList? ')' - ( '->' AssemblyIdentifierList )? AssemblyBlock -AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )? -AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression -AssemblyExpression = AssemblyFunctionCall | Identifier | Literal -AssemblyIf = 'if' AssemblyExpression AssemblyBlock -AssemblySwitch = 'switch' AssemblyExpression ( AssemblyCase+ AssemblyDefault? | AssemblyDefault ) -AssemblyCase = 'case' Literal AssemblyBlock -AssemblyDefault = 'default' AssemblyBlock -AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock -AssemblyBreakContinue = 'break' | 'continue' -AssemblyLeave = 'leave' -AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' - -AssemblyIdentifierList = Identifier ( ',' Identifier )* diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index fada5d2916af..a5f056493b6f 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -17,7 +17,7 @@ Storage Example :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract SimpleStorage { uint storedData; diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 1fec22aec7ea..a2629319bd90 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -284,7 +284,7 @@ for the two function parameters and two return variables. :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.21 <0.7.0; /** @title Shape calculator. */ contract ShapeCalculator { diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 085bf99e7638..3c30fec86d1a 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -788,6 +788,7 @@ Modifiers - ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. +- ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - ``virtual`` for functions and modifiers: Allows the function's or modifier's @@ -809,5 +810,5 @@ These keywords are reserved in Solidity. They might become part of the syntax in Language Grammar ================ -.. literalinclude:: grammar.txt - :language: none +.. literalinclude:: Solidity.g4 + :language: antlr diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index ebf54b3a4592..b4737049351b 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -81,7 +81,7 @@ as it uses ``call`` which forwards all remaining gas by default: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -277,7 +277,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Map { mapping (uint => uint)[] array; @@ -547,6 +547,7 @@ not mean loss of proving power. pragma solidity >=0.5.0; pragma experimental SMTChecker; + // This may report a warning if no SMT solver available. contract Recover { @@ -601,6 +602,7 @@ types. pragma solidity >=0.5.0; pragma experimental SMTChecker; // This will report a warning + contract Aliasing { uint[] array; diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 11ddaef07957..c637c8379cb2 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -109,7 +109,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; abstract contract A { function spam() virtual pure public; @@ -326,7 +326,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity ^0.6.0; contract A { @@ -745,7 +745,7 @@ manner as modifiers if the function declaration is long or hard to read. Yes:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Base contracts just to make this compile contract B { @@ -777,7 +777,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Base contracts just to make this compile @@ -1000,7 +1000,7 @@ As shown in the example below, if the contract name is `Congress` and the librar Yes:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Owned.sol @@ -1034,7 +1034,7 @@ and in ``Congress.sol``:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // owned.sol @@ -1138,7 +1138,7 @@ multiline comment starting with `/**` and ending with `*/`. For example, the contract from `a simple smart contract `_ with the comments added looks like the one below:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; /// @author The Solidity Team diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index acb48fb8a268..f69d25b0f6ee 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -66,7 +66,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract MappingExample { @@ -120,7 +120,7 @@ the ``sum`` function iterates over to sum all the values. :: - pragma solidity >=0.5.99 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; struct IndexValue { uint keyIndex; uint value; } struct KeyFlag { uint key; bool deleted; } diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 15cd110ddc1d..f7aca719037c 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -57,7 +57,7 @@ Data locations are not only relevant for persistency of data, but also for the s :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract C { // The data location of x is storage. @@ -124,6 +124,12 @@ Accessing an array past its end causes a failing assertion. Methods ``.push()`` to append a new element at the end of the array, where ``.push()`` appends a zero-initialized element and returns a reference to it. +.. index:: ! string, ! bytes + +.. _strings: + +.. _bytes: + ``bytes`` and ``strings`` as Arrays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +274,7 @@ Array Members :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract ArrayContract { uint[2**20] m_aLotOfIntegers; @@ -381,7 +387,7 @@ If ``start`` is greater than ``end`` or if ``end`` is greater than the length of the array, an exception is thrown. Both ``start`` and ``end`` are optional: ``start`` defaults - to ``0`` and ``end`` defaults to the length of the array. +to ``0`` and ``end`` defaults to the length of the array. Array slices do not have any members. They are implicitly convertible to arrays of their underlying type @@ -400,7 +406,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete :: - pragma solidity >=0.4.99 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Proxy { /// Address of the client contract managed by proxy i.e., this contract @@ -437,7 +443,7 @@ shown in the following example: :: - pragma solidity >=0.4.11 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // Defines a new type with two fields. // Declaring a struct outside of a contract allows @@ -494,7 +500,7 @@ shown in the following example: The contract does not provide the full functionality of a crowdfunding contract, but it contains the basic concepts necessary to understand structs. -Struct types can be used inside mappings and arrays and they can itself +Struct types can be used inside mappings and arrays and they can themselves contain mappings and arrays. It is not possible for a struct to contain a member of its own type, diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 8984a5b8ed97..80b9d548cb4a 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -300,6 +300,11 @@ All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-l The ``gas`` option is available on all three methods, while the ``value`` option is not supported for ``delegatecall``. +.. note:: + It is best to avoid relying on hardcoded gas values in your smart contract code, + regardless of whether state is read from or written to, as this can have many pitfalls. + Also, access to gas might change in the future. + .. note:: All contracts can be converted to ``address`` type, so it is possible to query the balance of the current contract using ``address(this).balance``. @@ -645,7 +650,7 @@ External (or public) functions have the following members: Example that shows how to use the members:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // This will report a warning contract Example { @@ -665,7 +670,6 @@ Example that shows how to use internal function types:: pragma solidity >=0.4.16 <0.7.0; - library ArrayUtils { // internal functions can be used in internal library functions because // they will be part of the same code context diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index eb4ddf0c651a..02218592e687 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -302,7 +302,8 @@ Input Description // evm.bytecode.opcodes - Opcodes list // evm.bytecode.sourceMap - Source mapping (useful for debugging) // evm.bytecode.linkReferences - Link references (if unlinked object) - // evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode) + // evm.deployedBytecode* - Deployed bytecode (has all the options that evm.bytecode has) + // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates // ewasm.wast - eWASM S-expressions format (not supported at the moment) @@ -424,8 +425,14 @@ Output Description } } }, - // The same layout as above. - "deployedBytecode": { }, + "deployedBytecode": { + ..., // The same layout as above. + "immutableReferences": [ + // There are two references to the immutable with AST ID 3, both 32 bytes long. One is + // at bytecode offset 42, the other at bytecode offset 80. + "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }] + ] + }, // The list of function hashes "methodIdentifiers": { "delegate(address)": "5c19a95c" @@ -605,8 +612,8 @@ Assume you have the following contracts you want to update declared in ``Source. .. code-block:: none - // This will not compile - pragma solidity >0.4.23; + // This will not compile after 0.5.0 + pragma solidity >0.4.23 <0.5.0; contract Updateable { function run() public view returns (bool); @@ -687,7 +694,7 @@ The command above applies all changes as shown below. Please review them careful .. code-block:: none - pragma solidity >0.4.23; + pragma solidity >=0.6.0 <0.7.0; abstract contract Updateable { function run() public view virtual returns (bool); diff --git a/docs/yul.rst b/docs/yul.rst index 93bfe48a9f79..c6fb28986d22 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -111,7 +111,7 @@ Stand-Alone Usage ================= You can use Yul in its stand-alone form in the EVM dialect using the Solidity compiler. -This will use the `Yul object notation `_ so that it is possible to refer +This will use the :ref:`Yul object notation ` so that it is possible to refer to code as data to deploy contracts. This Yul mode is available for the commandline compiler (use ``--strict-assembly``) and for the :ref:`standard-json interface `: @@ -146,7 +146,7 @@ so you can e.g. use ``//`` and ``/* */`` to denote comments. There is one exception: Identifiers in Yul can contain dots: ``.``. Yul can specify "objects" that consist of code, data and sub-objects. -Please see `Yul Objects `_ below for details on that. +Please see :ref:`Yul Objects ` below for details on that. In this section, we are only concerned with the code part of such an object. This code part always consists of a curly-braces delimited block. Most tools support specifying just a code block diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 7ed44d236ab0..fa6ea4f16ba3 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -283,6 +283,24 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end) ); break; + case PushImmutable: + collection.append(createJsonValue( + "PUSHIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; + case AssignImmutable: + collection.append(createJsonValue( + "ASSIGNIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; case Tag: collection.append( createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data()))); @@ -333,6 +351,20 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) return AssemblyItem{PushLibraryAddress, h}; } +AssemblyItem Assembly::newPushImmutable(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{PushImmutable, h}; +} + +AssemblyItem Assembly::newImmutableAssignment(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{AssignImmutable, h}; +} + Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; @@ -495,16 +527,44 @@ LinkerObject const& Assembly::assemble() const // Otherwise ensure the object is actually clear. assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references."); + LinkerObject& ret = m_assembledObject; + size_t subTagSize = 1; + map>> immutableReferencesBySub; for (auto const& sub: m_subs) { - sub->assemble(); + auto const& linkerObject = sub->assemble(); + if (!linkerObject.immutableReferences.empty()) + { + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "More than one sub-assembly references immutables." + ); + immutableReferencesBySub = linkerObject.immutableReferences; + } for (size_t tagPos: sub->m_tagPositionsInBytecode) if (tagPos != size_t(-1) && tagPos > subTagSize) subTagSize = tagPos; } - LinkerObject& ret = m_assembledObject; + bool setsImmutables = false; + bool pushesImmutables = false; + + for (auto const& i: m_items) + if (i.type() == AssignImmutable) + { + i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size()); + setsImmutables = true; + } + else if (i.type() == PushImmutable) + pushesImmutables = true; + if (setsImmutables || pushesImmutables) + assertThrow( + setsImmutables != pushesImmutables, + AssemblyException, + "Cannot push and assign immutables in the same assembly subroutine." + ); size_t bytesRequiredForCode = bytesRequired(subTagSize); m_tagPositionsInBytecode = vector(m_usedTags, -1); @@ -598,6 +658,25 @@ LinkerObject const& Assembly::assemble() const ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; + case PushImmutable: + ret.bytecode.push_back(uint8_t(Instruction::PUSH32)); + ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); + ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); + ret.bytecode.resize(ret.bytecode.size() + 32); + break; + case AssignImmutable: + for (auto const& offset: immutableReferencesBySub[i.data()].second) + { + ret.bytecode.push_back(uint8_t(Instruction::DUP1)); + // TODO: should we make use of the constant optimizer methods for pushing the offsets? + bytes offsetBytes = toCompactBigEndian(u256(offset)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH1) - 1 + offsetBytes.size()); + ret.bytecode += offsetBytes; + ret.bytecode.push_back(uint8_t(Instruction::MSTORE)); + } + immutableReferencesBySub.erase(i.data()); + ret.bytecode.push_back(uint8_t(Instruction::POP)); + break; case PushDeployTimeAddress: ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); @@ -615,6 +694,13 @@ LinkerObject const& Assembly::assemble() const } } + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "Some immutables were read from but never assigned." + ); + + if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) // Append an INVALID here to help tests find miscompilation. ret.bytecode.push_back(uint8_t(Instruction::INVALID)); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index a76538375c9b..e9e3630a8af0 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -54,6 +54,8 @@ class Assembly Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushLibraryAddress(std::string const& _identifier); + AssemblyItem newPushImmutable(std::string const& _identifier); + AssemblyItem newImmutableAssignment(std::string const& _identifier); AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } @@ -64,6 +66,8 @@ class Assembly /// after compilation and CODESIZE is not an option. void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); } + void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); } + void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } @@ -166,6 +170,7 @@ class Assembly std::vector> m_subs; std::map m_strings; std::map m_libraries; ///< Identifiers of libraries to be linked. + std::map m_immutables; ///< Identifiers of immutables. mutable LinkerObject m_assembledObject; mutable std::vector m_tagPositionsInBytecode; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index c238a3a3c741..610c6f5dd246 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -80,6 +80,13 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const case PushLibraryAddress: case PushDeployTimeAddress: return 1 + 20; + case PushImmutable: + return 1 + 32; + case AssignImmutable: + if (m_immutableOccurrences) + return 1 + (3 + 32) * *m_immutableOccurrences; + else + return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways. default: break; } @@ -90,6 +97,8 @@ int AssemblyItem::arguments() const { if (type() == Operation) return instructionInfo(instruction()).args; + else if (type() == AssignImmutable) + return 1; else return 0; } @@ -108,6 +117,7 @@ int AssemblyItem::returnValues() const case PushSubSize: case PushProgramSize: case PushLibraryAddress: + case PushImmutable: case PushDeployTimeAddress: return 1; case Tag: @@ -135,6 +145,7 @@ bool AssemblyItem::canBeFunctional() const case PushProgramSize: case PushLibraryAddress: case PushDeployTimeAddress: + case PushImmutable: return true; case Tag: return false; @@ -210,6 +221,12 @@ string AssemblyItem::toAssemblyText() const case PushDeployTimeAddress: text = string("deployTimeAddress()"); break; + case PushImmutable: + text = string("immutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; + case AssignImmutable: + text = string("assignImmutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; case UndefinedItem: assertThrow(false, AssemblyException, "Invalid assembly item."); break; @@ -275,6 +292,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) case PushDeployTimeAddress: _out << " PushDeployTimeAddress"; break; + case PushImmutable: + _out << " PushImmutable"; + break; + case AssignImmutable: + _out << " AssignImmutable"; + break; case UndefinedItem: _out << " ???"; break; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index e506a9fdb3e0..fc8c63c6718b 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -44,7 +44,9 @@ enum AssemblyItemType { Tag, PushData, PushLibraryAddress, ///< Push a currently unknown address of another (library) contract. - PushDeployTimeAddress ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. + AssignImmutable ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. }; class Assembly; @@ -153,6 +155,8 @@ class AssemblyItem size_t m_modifierDepth = 0; + void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared(_n); } + private: AssemblyItemType m_type; Instruction m_instruction; ///< Only valid if m_type == Operation @@ -162,6 +166,8 @@ class AssemblyItem /// Pushed value for operations with data to be determined during assembly stage, /// e.g. PushSubSize, PushTag, PushSub, etc. mutable std::shared_ptr m_pushedValue; + /// Number of PushImmutable's with the same hash. Only used for AssignImmutable. + mutable std::shared_ptr m_immutableOccurrences; }; inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 7e447a9a1fd6..5f1ca736cb23 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -91,6 +91,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool { // can be ignored } + else if (_item.type() == AssignImmutable) + // Since AssignImmutable breaks blocks, it should be fine to only consider its changes to the stack, which + // is the same as POP. + return feedItem(AssemblyItem(Instruction::POP), _copyItem); else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index e7a1e9667b7d..ab0e26507261 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -40,6 +40,10 @@ struct LinkerObject /// need to be replaced by the actual addresses by the linker. std::map linkReferences; + /// Map from hashes of the identifiers of immutable variables to the full identifier of the immutable and + /// to a list of offsets into the bytecode that refer to their values. + std::map>> immutableReferences; + /// Appends the bytecode of @a _other and incorporates its link references. void append(LinkerObject const& _other); diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 4ea9a01aee37..76eeb59566f3 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -36,6 +36,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case UndefinedItem: case Tag: case PushDeployTimeAddress: + case AssignImmutable: return true; case Push: case PushString: @@ -45,6 +46,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case PushProgramSize: case PushData: case PushLibraryAddress: + case PushImmutable: return false; case Operation: { diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index bb54c836f83d..c7a517a0b911 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -14,6 +14,8 @@ set(sources analysis/DeclarationContainer.h analysis/DocStringAnalyser.cpp analysis/DocStringAnalyser.h + analysis/ImmutableValidator.cpp + analysis/ImmutableValidator.h analysis/GlobalContext.cpp analysis/GlobalContext.h analysis/NameAndTypeResolver.cpp @@ -100,6 +102,8 @@ set(sources formal/SMTPortfolio.cpp formal/SMTPortfolio.h formal/SolverInterface.h + formal/Sorts.cpp + formal/Sorts.h formal/SSAVariable.cpp formal/SSAVariable.h formal/SymbolicTypes.cpp diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index e802c4204638..ee0a39163704 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -153,7 +153,7 @@ void DocStringAnalyser::parseDocStrings( appendError( _node.documentation()->location(), "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + - " exceedes the number of return parameters." + " exceeds the number of return parameters." ); else { diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp new file mode 100644 index 000000000000..6e0d0f6c796b --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -0,0 +1,220 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace solidity::frontend; + +void ImmutableValidator::analyze() +{ + m_inConstructionContext = true; + + auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | boost::adaptors::reversed; + + for (ContractDefinition const* contract: linearizedContracts) + for (VariableDeclaration const* stateVar: contract->stateVariables()) + if (stateVar->value()) + { + stateVar->value()->accept(*this); + solAssert(m_initializedStateVariables.emplace(stateVar).second, ""); + } + + for (ContractDefinition const* contract: linearizedContracts) + if (contract->constructor()) + visitCallableIfNew(*contract->constructor()); + + for (ContractDefinition const* contract: linearizedContracts) + for (std::shared_ptr const inheritSpec: contract->baseContracts()) + if (auto args = inheritSpec->arguments()) + ASTNode::listAccept(*args, *this); + + m_inConstructionContext = false; + + for (ContractDefinition const* contract: linearizedContracts) + { + for (auto funcDef: contract->definedFunctions()) + visitCallableIfNew(*funcDef); + + for (auto modDef: contract->functionModifiers()) + visitCallableIfNew(*modDef); + } + + checkAllVariablesInitialized(m_currentContract.location()); +} + +bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition) +{ + return analyseCallable(_functionDefinition); +} + +bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition) +{ + return analyseCallable(_modifierDefinition); +} + +bool ImmutableValidator::visit(MemberAccess const& _memberAccess) +{ + _memberAccess.expression().accept(*this); + + if (auto contractType = dynamic_cast(_memberAccess.expression().annotation().type)) + if (!contractType->isSuper()) + // external access, no analysis needed. + return false; + + if (auto varDecl = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _memberAccess); + else if (auto funcType = dynamic_cast(_memberAccess.annotation().type)) + if (funcType->kind() == FunctionType::Kind::Internal && funcType->hasDeclaration()) + visitCallableIfNew(funcType->declaration()); + + return false; +} + +bool ImmutableValidator::visit(IfStatement const& _ifStatement) +{ + bool prevInBranch = m_inBranch; + + _ifStatement.condition().accept(*this); + + m_inBranch = true; + _ifStatement.trueStatement().accept(*this); + + if (auto falseStatement = _ifStatement.falseStatement()) + falseStatement->accept(*this); + + m_inBranch = prevInBranch; + + return false; +} + +bool ImmutableValidator::visit(WhileStatement const& _whileStatement) +{ + bool prevInLoop = m_inLoop; + m_inLoop = true; + + _whileStatement.condition().accept(*this); + _whileStatement.body().accept(*this); + + m_inLoop = prevInLoop; + + return false; +} + +void ImmutableValidator::endVisit(Identifier const& _identifier) +{ + if (auto const callableDef = dynamic_cast(_identifier.annotation().referencedDeclaration)) + visitCallableIfNew(callableDef->resolveVirtual(m_currentContract)); + if (auto const varDecl = dynamic_cast(_identifier.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _identifier); +} + +void ImmutableValidator::endVisit(Return const& _return) +{ + if (m_currentConstructor != nullptr) + checkAllVariablesInitialized(_return.location()); +} + +bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration) +{ + FunctionDefinition const* prevConstructor = m_currentConstructor; + m_currentConstructor = nullptr; + + if (FunctionDefinition const* funcDef = dynamic_cast(&_callableDeclaration)) + { + ASTNode::listAccept(funcDef->modifiers(), *this); + + if (funcDef->isConstructor()) + m_currentConstructor = funcDef; + + if (funcDef->isImplemented()) + funcDef->body().accept(*this); + } + else if (ModifierDefinition const* modDef = dynamic_cast(&_callableDeclaration)) + modDef->body().accept(*this); + + m_currentConstructor = prevConstructor; + + return false; +} + +void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression) +{ + if (!_variableReference.isStateVariable() || !_variableReference.immutable()) + return; + + if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment) + { + if (!m_currentConstructor) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized inline or assigned directly in the constructor." + ); + else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized in the constructor of the contract they are defined in." + ); + else if (m_inLoop) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized once, not in a while statement." + ); + else if (m_inBranch) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized unconditionally, not in an if statement." + ); + + if (!m_initializedStateVariables.emplace(&_variableReference).second) + m_errorReporter.typeError( + _expression.location(), + "Immutable state variable already initialized." + ); + } + else if (m_inConstructionContext) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables cannot be read during contract creation time, which means " + "they cannot be read in the constructor or any function or modifier called from it." + ); +} + +void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location) +{ + for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts) + for (VariableDeclaration const* varDecl: contract->stateVariables()) + if (varDecl->immutable()) + if (!util::contains(m_initializedStateVariables, varDecl)) + m_errorReporter.typeError( + _location, + solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), + "Construction control flow ends without initializing all immutable state variables." + ); +} + +void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration) +{ + CallableDeclaration const* _callable = dynamic_cast(&_declaration); + solAssert(_callable != nullptr, ""); + + if (m_visitedCallables.emplace(_callable).second) + _declaration.accept(*this); +} diff --git a/libsolidity/analysis/ImmutableValidator.h b/libsolidity/analysis/ImmutableValidator.h new file mode 100644 index 000000000000..5845ba011afd --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.h @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include + +#include + +namespace solidity::frontend +{ + +/** + * Validates access and initialization of immutable variables: + * must be directly initialized in their respective c'tor + * can not be read by any function/modifier called by the c'tor (or the c'tor itself) + * must be initialized outside loops (only one initialization) + * must be initialized outside ifs (must be initialized unconditionally) + * must be initialized exactly once (no multiple statements) + * must be initialized exactly once (no early return to skip initialization) +*/ +class ImmutableValidator: private ASTConstVisitor +{ + using CallableDeclarationSet = std::set; + +public: + ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): + m_currentContract(_contractDefinition), + m_errorReporter(_errorReporter) + { } + + void analyze(); + +private: + bool visit(FunctionDefinition const& _functionDefinition); + bool visit(ModifierDefinition const& _modifierDefinition); + bool visit(MemberAccess const& _memberAccess); + bool visit(IfStatement const& _ifStatement); + bool visit(WhileStatement const& _whileStatement); + void endVisit(Identifier const& _identifier); + void endVisit(Return const& _return); + + bool analyseCallable(CallableDeclaration const& _callableDeclaration); + void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression); + + void checkAllVariablesInitialized(langutil::SourceLocation const& _location); + + void visitCallableIfNew(Declaration const& _declaration); + + ContractDefinition const& m_currentContract; + + CallableDeclarationSet m_visitedCallables; + + std::set m_initializedStateVariables; + langutil::ErrorReporter& m_errorReporter; + + FunctionDefinition const* m_currentConstructor = nullptr; + bool m_inLoop = false; + bool m_inBranch = false; + bool m_inConstructionContext = false; +}; + +} diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 6867f049c50c..94a16d6f1552 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -204,7 +204,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) else { _typeName.annotation().type = TypeProvider::emptyTuple(); - typeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); } } @@ -328,6 +328,8 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) if (_variable.isConstant() && !_variable.isStateVariable()) m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + if (_variable.immutable() && !_variable.isStateVariable()) + m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables."); if (!_variable.typeName()) { @@ -394,7 +396,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) else if (_variable.isStateVariable()) { solAssert(varLoc == Location::Unspecified, ""); - typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage; + typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage; } else if ( dynamic_cast(_variable.scope()) || diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 74fa8d458cfb..ba973bffa110 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -303,7 +303,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) ); } - if (!_function.isImplemented() && !_function.modifiers().empty()) + if (m_isInterface && !_function.modifiers().empty()) + m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers."); + else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); return true; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5cf003bed5b9..2bdc706a2539 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -480,6 +480,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) "Initial value for constant variable has to be compile-time constant." ); } + else if (_variable.immutable()) + { + if (!_variable.type()->isValueType()) + m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type."); + if ( + auto const* functionType = dynamic_cast(_variable.type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + m_errorReporter.typeError(_variable.location(), "Immutable variables of external function type are not yet supported."); + solAssert(_variable.type()->sizeOnStack() == 1 || m_errorReporter.hasErrors(), ""); + } + if (!_variable.isStateVariable()) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) @@ -641,16 +653,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); + if (var->immutable()) + { + m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported."); + return size_t(-1); + } if (var->isConstant()) { - var = rootVariableDeclaration(*var); + var = rootConstVariableDeclaration(*var); - if (!var->value()) + if (var && !var->value()) { m_errorReporter.typeError(_identifier.location, "Constant has no value."); return size_t(-1); } - else if (!type(*var)->isValueType() || ( + else if (!var || !type(*var)->isValueType() || ( dynamic_cast(var->value().get()) == nullptr && type(*var->value())->category() != Type::Category::RationalNumber )) @@ -731,6 +748,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { + m_errorReporter.declarationError(_identifier.location, "Access to functions is not allowed in inline assembly."); + return size_t(-1); } else if (dynamic_cast(declaration)) { @@ -1052,17 +1071,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (!varDecl.annotation().type) m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); - if (auto ref = dynamic_cast(type(varDecl))) - { - if (ref->dataStoredIn(DataLocation::Storage)) - { - string errorText{"Uninitialized storage pointer."}; - solAssert(varDecl.referenceLocation() != VariableDeclaration::Location::Unspecified, "Expected a specified location at this point"); - solAssert(m_scope, ""); - m_errorReporter.declarationError(varDecl.location(), errorText); - } - } - else if (dynamic_cast(type(varDecl))) + if (dynamic_cast(type(varDecl))) m_errorReporter.typeError( varDecl.location(), "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." @@ -1308,11 +1317,11 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& if (auto const* tupleExpression = dynamic_cast(&_expression)) { auto const* tupleType = dynamic_cast(&_type); - auto const& types = tupleType ? tupleType->components() : vector { &_type }; + auto const& types = tupleType && tupleExpression->components().size() > 1 ? tupleType->components() : vector { &_type }; solAssert( tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), - "Array sizes don't match or no errors generated." + "Array sizes don't match and no errors generated." ); for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) @@ -1336,7 +1345,10 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& bool TypeChecker::visit(Assignment const& _assignment) { - requireLValue(_assignment.leftHandSide()); + requireLValue( + _assignment.leftHandSide(), + _assignment.assignmentOperator() == Token::Assign + ); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; @@ -1394,7 +1406,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple) for (auto const& component: components) if (component) { - requireLValue(*component); + requireLValue( + *component, + _tuple.annotation().lValueOfOrdinaryAssignment + ); types.push_back(type(*component)); } else @@ -1479,7 +1494,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) Token op = _operation.getOperator(); bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); if (modifying) - requireLValue(_operation.subExpression()); + requireLValue(_operation.subExpression(), false); else _operation.subExpression().accept(*this); TypePointer const& subExprType = type(_operation.subExpression()); @@ -2987,9 +3002,10 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte return true; } -void TypeChecker::requireLValue(Expression const& _expression) +void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment) { _expression.annotation().lValueRequested = true; + _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.accept(*this); if (_expression.annotation().isLValue) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d428a6ac9754..a26ab81bdcfa 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -158,7 +158,7 @@ class TypeChecker: private ASTConstVisitor /// convertible to @a _expectedType. bool expectType(Expression const& _expression, Type const& _expectedType); /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. - void requireLValue(Expression const& _expression); + void requireLValue(Expression const& _expression, bool _ordinaryAssignment); ContractDefinition const* m_scope = nullptr; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6b03c7b586e9..b711758d68c7 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -319,6 +319,37 @@ FunctionDefinitionAnnotation& FunctionDefinition::annotation() const return initAnnotation(); } +FunctionDefinition const& FunctionDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(!isConstructor(), ""); + // If we are not doing super-lookup and the function is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + FunctionType const* functionType = TypeProvider::function(*this)->asCallableFunction(false); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (FunctionDefinition const* function: c->definedFunctions()) + if ( + function->name() == name() && + !function->isConstructor() && + FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(*functionType) + ) + return *function; + } + solAssert(false, "Virtual function " + name() + " not found."); + return *this; // not reached +} + TypePointer ModifierDefinition::type() const { return TypeProvider::modifier(*this); @@ -329,6 +360,33 @@ ModifierDefinitionAnnotation& ModifierDefinition::annotation() const return initAnnotation(); } +ModifierDefinition const& ModifierDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(_searchStart == nullptr, "Used super in connection with modifiers."); + + // If we are not doing super-lookup and the modifier is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (ModifierDefinition const* modifier: c->functionModifiers()) + if (modifier->name() == name()) + return *modifier; + } + solAssert(false, "Virtual modifier " + name() + " not found."); + return *this; // not reached +} + + TypePointer EventDefinition::type() const { return TypeProvider::function(*this); @@ -508,8 +566,6 @@ set VariableDeclaration::allowedDataLocations() c if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) return set{ Location::Unspecified }; - else if (isStateVariable() && isConstant()) - return set{ Location::Memory }; else if (isExternalCallableParameter()) { set locations{ Location::CallData }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index adb957cfec5b..4979d5d4eec6 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -62,6 +62,24 @@ class ASTConstVisitor; class ASTNode: private boost::noncopyable { public: + struct CompareByID + { + using is_transparent = void; + + bool operator()(ASTNode const* _lhs, ASTNode const* _rhs) const + { + return _lhs->id() < _rhs->id(); + } + bool operator()(ASTNode const* _lhs, int64_t _rhs) const + { + return _lhs->id() < _rhs; + } + bool operator()(int64_t _lhs, ASTNode const* _rhs) const + { + return _lhs < _rhs->id(); + } + }; + using SourceLocation = langutil::SourceLocation; explicit ASTNode(int64_t _id, SourceLocation const& _location); @@ -689,6 +707,18 @@ class CallableDeclaration: public Declaration, public VariableScope CallableDeclarationAnnotation& annotation() const override = 0; + /// Performs virtual or super function/modifier lookup: + /// If @a _searchStart is nullptr, performs virtual function lookup, i.e. + /// searches the inheritance hierarchy of @a _mostDerivedContract towards the base + /// and returns the first function/modifier definition that + /// is overwritten by this callable. + /// If @a _searchStart is non-null, starts searching only from that contract, but + /// still in the hierarchy of @a _mostDerivedContract. + virtual CallableDeclaration const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const = 0; + protected: ASTPointer m_parameters; ASTPointer m_overrides; @@ -799,6 +829,12 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen CallableDeclaration::virtualSemantics() || (annotation().contract && annotation().contract->isInterface()); } + + FunctionDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + private: StateMutability m_stateMutability; Token const m_kind; @@ -879,6 +915,7 @@ class VariableDeclaration: public Declaration bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_constantness == Constantness::Constant; } + bool immutable() const { return m_constantness == Constantness::Immutable; } ASTPointer const& overrides() const { return m_overrides; } Location referenceLocation() const { return m_location; } /// @returns a set of allowed storage locations for the variable. @@ -944,6 +981,12 @@ class ModifierDefinition: public CallableDeclaration, public StructurallyDocumen ModifierDefinitionAnnotation& annotation() const override; + ModifierDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + + private: ASTPointer m_body; }; @@ -1009,6 +1052,14 @@ class EventDefinition: public CallableDeclaration, public StructurallyDocumented EventDefinitionAnnotation& annotation() const override; + CallableDeclaration const& resolveVirtual( + ContractDefinition const&, + ContractDefinition const* + ) const override + { + return *this; + } + private: bool m_anonymous = false; }; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 86636684c159..021a742f5417 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -47,6 +47,14 @@ using TypePointer = Type const*; struct ASTAnnotation { + ASTAnnotation() = default; + + ASTAnnotation(ASTAnnotation const&) = delete; + ASTAnnotation(ASTAnnotation&&) = delete; + + ASTAnnotation& operator=(ASTAnnotation const&) = delete; + ASTAnnotation& operator=(ASTAnnotation&&) = delete; + virtual ~ASTAnnotation() = default; }; @@ -58,7 +66,16 @@ struct DocTag struct StructurallyDocumentedAnnotation { + StructurallyDocumentedAnnotation() = default; + + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation&&) = delete; + + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation&&) = delete; + virtual ~StructurallyDocumentedAnnotation() = default; + /// Mapping docstring tag name -> content. std::multimap docTags; }; @@ -75,6 +92,16 @@ struct SourceUnitAnnotation: ASTAnnotation struct ScopableAnnotation { + ScopableAnnotation() = default; + + ScopableAnnotation(ScopableAnnotation const&) = delete; + ScopableAnnotation(ScopableAnnotation&&) = delete; + + ScopableAnnotation& operator=(ScopableAnnotation const&) = delete; + ScopableAnnotation& operator=(ScopableAnnotation&&) = delete; + + virtual ~ScopableAnnotation() = default; + /// The scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. ASTNode const* scope = nullptr; @@ -208,6 +235,9 @@ struct ExpressionAnnotation: ASTAnnotation bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. bool lValueRequested = false; + /// Whether the expression is an lvalue that is only assigned. + /// Would be false for --, ++, delete, +=, -=, .... + bool lValueOfOrdinaryAssignment = false; /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resoultion diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp index d903a1b4b888..483b75301ac3 100644 --- a/libsolidity/ast/ASTUtils.cpp +++ b/libsolidity/ast/ASTUtils.cpp @@ -21,7 +21,7 @@ namespace solidity::frontend { -VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl) +VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl) { solAssert(_varDecl.isConstant(), "Constant variable expected"); @@ -30,7 +30,8 @@ VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _v while ((identifier = dynamic_cast(rootDecl->value().get()))) { auto referencedVarDecl = dynamic_cast(identifier->annotation().referencedDeclaration); - solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration"); + if (!referencedVarDecl || !referencedVarDecl->isConstant()) + return nullptr; rootDecl = referencedVarDecl; } return rootDecl; diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h index 7624080a9c65..af77b60f11a6 100644 --- a/libsolidity/ast/ASTUtils.h +++ b/libsolidity/ast/ASTUtils.h @@ -22,8 +22,9 @@ namespace solidity::frontend class VariableDeclaration; -/// Find the topmost referenced variable declaration when the given variable +/// Find the topmost referenced constant variable declaration when the given variable /// declaration value is an identifier. Works only for constant variable declarations. -VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl); +/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration. +VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl); } diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 90dad003a18b..db67dc392137 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -41,7 +41,16 @@ namespace solidity::frontend class ASTVisitor { public: + ASTVisitor() = default; + + ASTVisitor(ASTVisitor const&) = delete; + ASTVisitor(ASTVisitor&&) = delete; + + ASTVisitor& operator=(ASTVisitor const&) = delete; + ASTVisitor& operator=(ASTVisitor&&) = delete; + virtual ~ASTVisitor() = default; + virtual bool visit(SourceUnit& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective& _node) { return visitNode(_node); } virtual bool visit(ImportDirective& _node) { return visitNode(_node); } @@ -158,7 +167,16 @@ class ASTVisitor class ASTConstVisitor { public: + ASTConstVisitor() = default; + + ASTConstVisitor(ASTConstVisitor const&) = delete; + ASTConstVisitor(ASTConstVisitor&&) = delete; + + ASTConstVisitor& operator=(ASTConstVisitor const&) = delete; + ASTConstVisitor& operator=(ASTConstVisitor&&) = delete; + virtual ~ASTConstVisitor() = default; + virtual bool visit(SourceUnit const& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); } virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3f54fccc6b9a..3fc649caf204 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1487,11 +1487,19 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const case DataLocation::Memory: return TypeProvider::emptyTuple(); case DataLocation::Storage: - return m_isPointer ? nullptr : TypeProvider::emptyTuple(); + return isPointer() ? nullptr : TypeProvider::emptyTuple(); } return nullptr; } +bool ReferenceType::isPointer() const +{ + if (m_location == DataLocation::Storage) + return m_isPointer; + else + return true; +} + TypePointer ReferenceType::copyForLocationIfReference(Type const* _type) const { return TypeProvider::withLocationIfReference(m_location, _type); @@ -1502,7 +1510,7 @@ string ReferenceType::stringForReferencePart() const switch (m_location) { case DataLocation::Storage: - return string("storage ") + (m_isPointer ? "pointer" : "ref"); + return string("storage ") + (isPointer() ? "pointer" : "ref"); case DataLocation::CallData: return "calldata"; case DataLocation::Memory: @@ -1868,7 +1876,8 @@ u256 ArrayType::memoryDataSize() const std::unique_ptr ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const { auto copy = make_unique(_location); - copy->m_isPointer = _isPointer; + if (_location == DataLocation::Storage) + copy->m_isPointer = _isPointer; copy->m_arrayKind = m_arrayKind; copy->m_baseType = copy->copyForLocationIfReference(m_baseType); copy->m_hasDynamicLength = m_hasDynamicLength; @@ -1988,7 +1997,7 @@ vector> ContractType::stateVar vector variables; for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!(variable->isConstant() || variable->immutable())) variables.push_back(variable); TypePointers types; for (auto variable: variables) @@ -2003,6 +2012,16 @@ vector> ContractType::stateVar return variablesAndOffsets; } +vector ContractType::immutableVariables() const +{ + vector variables; + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) + for (VariableDeclaration const* variable: contract->stateVariables()) + if (variable->immutable()) + variables.push_back(variable); + return variables; +} + vector> ContractType::makeStackItems() const { if (m_super) @@ -2247,7 +2266,8 @@ TypeResult StructType::interfaceType(bool _inLibrary) const std::unique_ptr StructType::copyForLocation(DataLocation _location, bool _isPointer) const { auto copy = make_unique(m_struct, _location); - copy->m_isPointer = _isPointer; + if (_location == DataLocation::Storage) + copy->m_isPointer = _isPointer; return copy; } @@ -2318,7 +2338,7 @@ TypePointers StructType::memoryMemberTypes() const TypePointers types; for (ASTPointer const& variable: m_struct.members()) if (variable->annotation().type->canLiveOutsideStorage()) - types.push_back(variable->annotation().type); + types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type)); return types; } @@ -2954,7 +2974,7 @@ vector> FunctionType::makeStackItems() const if (m_valueSet) slots.emplace_back("value", TypeProvider::uint256()); if (m_saltSet) - slots.emplace_back("salt", TypeProvider::uint256()); + slots.emplace_back("salt", TypeProvider::fixedBytes(32)); if (bound()) for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems()) slots.emplace_back("self_" + boundName, boundType); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 5aada86c3a5f..d0e774cb7cc7 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -701,7 +701,9 @@ class ReferenceType: public Type /// pointer type, state variables are bound references. Assignments to pointers or deleting /// them will not modify storage (that will only change the pointer). Assignment from /// non-storage objects to a variable of storage pointer type is not possible. - bool isPointer() const { return m_isPointer; } + /// For anything other than storage, this always returns true because assignments + /// never change the contents of the original value. + bool isPointer() const; bool operator==(ReferenceType const& _other) const { @@ -893,6 +895,8 @@ class ContractType: public Type /// @returns a list of all state variables (including inherited) of the contract and their /// offsets in storage. std::vector> stateVariables() const; + /// @returns a list of all immutable variables (including inherited) of the contract. + std::vector immutableVariables() const; protected: std::vector> makeStackItems() const override; private: diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index cb195c49fa73..06080afbd4d5 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -71,6 +71,49 @@ void CompilerContext::addStateVariable( m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset); } +void CompilerContext::addImmutable(VariableDeclaration const& _variable) +{ + solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); + solUnimplementedAssert(_variable.annotation().type->isValueType(), "Only immutable variables of value type are supported."); + solAssert(m_runtimeContext, "Attempted to register an immutable variable for runtime code generation."); + m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory; + solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap."); + *m_reservedMemory += _variable.annotation().type->memoryHeadSize(); +} + +size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variable) const +{ + solAssert(m_immutableVariables.count(&_variable), "Memory offset of unknown immutable queried."); + solAssert(m_runtimeContext, "Attempted to fetch the memory offset of an immutable variable during runtime code generation."); + return m_immutableVariables.at(&_variable); +} + +vector CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable) +{ + string baseName = to_string(_variable.id()); + solAssert(_variable.annotation().type->sizeOnStack() > 0, ""); + if (_variable.annotation().type->sizeOnStack() == 1) + return {baseName}; + vector names; + auto collectSlotNames = [&](string const& _baseName, TypePointer type, auto const& _recurse) -> void { + for (auto const& [slot, type]: type->stackItems()) + if (type) + _recurse(_baseName + " " + slot, type, _recurse); + else + names.emplace_back(_baseName); + }; + collectSlotNames(baseName, _variable.annotation().type, collectSlotNames); + return names; +} + +size_t CompilerContext::reservedMemory() +{ + solAssert(m_reservedMemory.has_value(), "Reserved memory was used before "); + size_t reservedMemory = *m_reservedMemory; + m_reservedMemory = std::nullopt; + return reservedMemory; +} + void CompilerContext::startFunction(Declaration const& _function) { m_functionCompilationQueue.startFunction(_function); @@ -223,55 +266,41 @@ evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration con return m_functionCompilationQueue.entryLabelIfExists(_declaration); } -FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function) -{ - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_function.scope())) - if (scope->isLibrary()) - return _function; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin()); -} - FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base) { - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, superContract(_base)); + solAssert(m_mostDerivedContract, "No most derived contract set."); + ContractDefinition const* super = superContract(_base); + solAssert(super, "Super contract not available."); + return _function.resolveVirtual(mostDerivedContract(), super); } FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const { - vector::const_iterator it = superContract(_contract); - for (; it != m_inheritanceHierarchy.end(); ++it) - if ((*it)->constructor()) - return (*it)->constructor(); + ContractDefinition const* next = superContract(_contract); + if (next == nullptr) + return nullptr; + for (ContractDefinition const* c: m_mostDerivedContract->annotation().linearizedBaseContracts) + if (next != nullptr && next != c) + continue; + else + { + next = nullptr; + if (c->constructor()) + return c->constructor(); + } return nullptr; } -Declaration const* CompilerContext::nextFunctionToCompile() const +ContractDefinition const& CompilerContext::mostDerivedContract() const { - return m_functionCompilationQueue.nextFunctionToCompile(); + solAssert(m_mostDerivedContract, "Most derived contract not set."); + return *m_mostDerivedContract; } -ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier( - ModifierDefinition const& _modifier -) const +Declaration const* CompilerContext::nextFunctionToCompile() const { - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_modifier.scope())) - if (scope->isLibrary()) - return _modifier; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ModifierDefinition const* modifier: contract->functionModifiers()) - if (modifier->name() == _modifier.name()) - return *modifier; - solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy."); + return m_functionCompilationQueue.nextFunctionToCompile(); } unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const @@ -500,32 +529,26 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ #endif } -FunctionDefinition const& CompilerContext::resolveVirtualFunction( - FunctionDefinition const& _function, - vector::const_iterator _searchStart -) +LinkerObject const& CompilerContext::assembledObject() const +{ + LinkerObject const& object = m_asm->assemble(); + solAssert(object.immutableReferences.empty(), "Leftover immutables."); + return object; +} + +ContractDefinition const* CompilerContext::superContract(ContractDefinition const& _contract) const { - string name = _function.name(); - FunctionType functionType(_function); - auto it = _searchStart; - for (; it != m_inheritanceHierarchy.end(); ++it) - for (FunctionDefinition const* function: (*it)->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); - return _function; // not reached -} - -vector::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const -{ - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_contract); - solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); - return ++it; + auto const& hierarchy = mostDerivedContract().annotation().linearizedBaseContracts; + auto it = find(hierarchy.begin(), hierarchy.end(), &_contract); + solAssert(it != hierarchy.end(), "Base not found in inheritance hierarchy."); + ++it; + if (it == hierarchy.end()) + return nullptr; + else + { + solAssert(*it != &_contract, ""); + return *it; + } } string CompilerContext::revertReasonIfDebug(string const& _message) diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 8c577523916f..a8e494435321 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -64,6 +64,7 @@ class CompilerContext m_asm(std::make_shared()), m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), + m_reservedMemory{0}, m_runtimeContext(_runtimeContext), m_abiFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector), m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector) @@ -80,6 +81,16 @@ class CompilerContext bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); + void addImmutable(VariableDeclaration const& _declaration); + + /// @returns the reserved memory for storing the value of the immutable @a _variable during contract creation. + size_t immutableMemoryOffset(VariableDeclaration const& _variable) const; + /// @returns a list of slot names referring to the stack slots of an immutable variable. + static std::vector immutableVariableSlotNames(VariableDeclaration const& _variable); + + /// @returns the reserved memory and resets it to mark it as used. + size_t reservedMemory(); + void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(Declaration const& _declaration); /// Removes all local variables currently allocated above _stackHeight. @@ -103,15 +114,14 @@ class CompilerContext /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. evmasm::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const; - /// @returns the entry label of the given function and takes overrides into account. - FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function); /// @returns the function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base); /// @returns the next constructor in the inheritance hierarchy. FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const; - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } + /// Sets the contract currently being compiled - the most derived one. + void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; } + ContractDefinition const& mostDerivedContract() const; /// @returns the next function in the queue of functions that are still to be compiled /// (i.e. that were referenced during compilation but where we did not yet generate code for). @@ -160,7 +170,6 @@ class CompilerContext /// empty return value. std::pair> requestedYulFunctions(); - ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns @@ -217,6 +226,10 @@ class CompilerContext evmasm::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); } /// Appends the address (virtual, will be filled in by linker) of a library. void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); } + /// Appends an immutable variable. The value will be filled in by the constructor. + void appendImmutable(std::string const& _identifier) { m_asm->appendImmutable(_identifier); } + /// Appends an assignment to an immutable variable. Only valid in creation code. + void appendImmutableAssignment(std::string const& _identifier) { m_asm->appendImmutableAssignment(_identifier); } /// Appends a zero-address that can be replaced by something else at deploy time (if the /// position in bytecode is known). void appendDeployTimeAddress() { m_asm->append(evmasm::PushDeployTimeAddress); } @@ -282,7 +295,7 @@ class CompilerContext return m_asm->assemblyJSON(_indicies); } - evmasm::LinkerObject const& assembledObject() const { return m_asm->assemble(); } + evmasm::LinkerObject const& assembledObject() const; evmasm::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } /** @@ -300,14 +313,8 @@ class CompilerContext RevertStrings revertStrings() const { return m_revertStrings; } private: - /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns - /// the first function definition that is overwritten by _function. - FunctionDefinition const& resolveVirtualFunction( - FunctionDefinition const& _function, - std::vector::const_iterator _searchStart - ); - /// @returns an iterator to the contract directly above the given contract. - std::vector::const_iterator superContract(ContractDefinition const& _contract) const; + /// @returns a pointer to the contract directly above the given contract. + ContractDefinition const* superContract(ContractDefinition const& _contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); @@ -355,13 +362,19 @@ class CompilerContext std::map> m_otherCompilers; /// Storage offsets of state variables std::map> m_stateVariables; + /// Memory offsets reserved for the values of immutable variables during contract creation. + std::map m_immutableVariables; + /// Total amount of reserved memory. Reserved memory is used to store immutable variables during contract creation. + /// This has to be finalized before initialiseFreeMemoryPointer() is called. That function + /// will reset the optional to verify that. + std::optional m_reservedMemory = {0}; /// Offsets of local variables on the stack (relative to stack base). /// This needs to be a stack because if a modifier contains a local variable and this /// modifier is applied twice, the position of the variable needs to be restored /// after the nested modifier is left. std::map> m_localVariables; - /// List of current inheritance hierarchy from derived to base. - std::vector m_inheritanceHierarchy; + /// The contract currently being compiled. Virtual function lookup starts from this contarct. + ContractDefinition const* m_mostDerivedContract = nullptr; /// Stack of current visited AST nodes, used for location attachment std::stack m_visitedNodes; /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index c559c0b7c8a5..d4f35ad793e3 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -51,7 +51,9 @@ static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPoi void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(generalPurposeMemoryStart); + size_t reservedMemory = m_context.reservedMemory(); + solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, ""); + m_context << (u256(generalPurposeMemoryStart) + reservedMemory); storeFreeMemoryPointer(); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 8b9061af3d2a..822ce3b667ae 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -129,7 +129,9 @@ void ContractCompiler::initializeContext( { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); m_context.setOtherCompilers(_otherCompilers); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); + if (m_runtimeCompiler) + registerImmutableVariables(_contract); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); m_context.resetVisitedNodes(&_contract); @@ -183,10 +185,26 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); + + ContractType contractType(_contract); + auto const& immutables = contractType.immutableVariables(); + // Push all immutable values on the stack. + for (auto const& immutable: immutables) + CompilerUtils(m_context).loadFromMemory(m_context.immutableMemoryOffset(*immutable), *immutable->annotation().type); m_context.pushSubroutineSize(m_context.runtimeSub()); - m_context << Instruction::DUP1; + if (immutables.empty()) + m_context << Instruction::DUP1; m_context.pushSubroutineOffset(m_context.runtimeSub()); m_context << u256(0) << Instruction::CODECOPY; + // Assign immutable values from stack in reversed order. + for (auto const& immutable: immutables | boost::adaptors::reversed) + { + auto slotNames = m_context.immutableVariableSlotNames(*immutable); + for (auto&& slotName: slotNames | boost::adaptors::reversed) + m_context.appendImmutableAssignment(slotName); + } + if (!immutables.empty()) + m_context.pushSubroutineSize(m_context.runtimeSub()); m_context << u256(0) << Instruction::RETURN; return m_context.runtimeSub(); @@ -521,6 +539,13 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } +void ContractCompiler::registerImmutableVariables(ContractDefinition const& _contract) +{ + solAssert(m_runtimeCompiler, "Attempted to register immutables for runtime code generation."); + for (auto const& var: ContractType(_contract).immutableVariables()) + m_context.addImmutable(*var); +} + void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract) { solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); @@ -662,7 +687,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (FunctionDefinition const* functionDef = dynamic_cast(decl)) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); - functionDef = &m_context.resolveVirtualFunction(*functionDef); + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); solAssert(functionEntryLabel.data() <= std::numeric_limits::max(), ""); _assembly.appendLabelReference(size_t(functionEntryLabel.data())); @@ -680,9 +705,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (auto variable = dynamic_cast(decl)) { + solAssert(!variable->immutable(), ""); if (variable->isConstant()) { - variable = rootVariableDeclaration(*variable); + variable = rootConstVariableDeclaration(*variable); + // If rootConstVariableDeclaration fails and returns nullptr, + // it should have failed in TypeChecker already, causing a compilation error. + // In such case we should not get here. + solAssert(variable, ""); + u256 value; if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) { @@ -1302,10 +1333,9 @@ void ContractCompiler::appendModifierOrFunctionCode() appendModifierOrFunctionCode(); else { - ModifierDefinition const& nonVirtualModifier = dynamic_cast( + ModifierDefinition const& modifier = dynamic_cast( *modifierInvocation->name()->annotation().referencedDeclaration - ); - ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); + ).resolveVirtual(m_context.mostDerivedContract()); CompilerContext::LocationSetter locationSetter(m_context, modifier); std::vector> const& modifierArguments = modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector>(); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 0a2ecf7e8741..0916da281a2b 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -99,6 +99,7 @@ class ContractCompiler: private ASTConstVisitor void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); void registerStateVariables(ContractDefinition const& _contract); + void registerImmutableVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract); bool visit(VariableDeclaration const& _variableDeclaration) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 0fc24d919e68..a02497561279 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -73,7 +73,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c utils().convertType(*type, *_varDecl.annotation().type); type = _varDecl.annotation().type; } - StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + else + StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); } void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl) @@ -93,11 +96,17 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& FunctionType accessorType(_varDecl); TypePointers paramTypes = accessorType.parameterTypes(); + if (_varDecl.immutable()) + solAssert(paramTypes.empty(), ""); + m_context.adjustStackOffset(1 + CompilerUtils::sizeOnStack(paramTypes)); - // retrieve the position of the variable - auto const& location = m_context.storageLocationOfVariable(_varDecl); - m_context << location.first << u256(location.second); + if (!_varDecl.immutable()) + { + // retrieve the position of the variable + auto const& location = m_context.storageLocationOfVariable(_varDecl); + m_context << location.first << u256(location.second); + } TypePointer returnType = _varDecl.annotation().type; @@ -179,6 +188,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& solAssert(returnTypes.size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType)) { + solAssert(!_varDecl.immutable(), ""); // remove offset m_context << Instruction::POP; auto const& names = accessorType.returnParameterNames(); @@ -205,7 +215,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { // simple value or array solAssert(returnTypes.size() == 1, ""); - StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).retrieveValue(SourceLocation()); + else + StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); utils().convertType(*returnType, *returnTypes.front()); retSizeOnStack = returnTypes.front()->sizeOnStack(); } @@ -572,7 +585,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); + utils().pushCombinedFunctionEntryLabel( + functionDef->resolveVirtual(m_context.mostDerivedContract()), + false + ); shortcutTaken = true; } } @@ -992,6 +1008,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Fetch requested length. acceptAndConvert(*arguments[0], *TypeProvider::uint256()); + // Make sure we can allocate memory without overflow + m_context << u256(0xffffffffffffffff); + m_context << Instruction::DUP2; + m_context << Instruction::GT; + m_context.appendConditionalRevert(); + // Stack: requested_length utils().fetchFreeMemoryPointer(); @@ -1800,6 +1822,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) { CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); _indexAccess.baseExpression().accept(*this); + // stack: offset length Type const& baseType = *_indexAccess.baseExpression().annotation().type; @@ -1815,27 +1838,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256()); else m_context << u256(0); + // stack: offset length sliceStart + + m_context << Instruction::SWAP1; + // stack: offset sliceStart length + if (_indexAccess.endExpression()) acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256()); else - m_context << Instruction::DUP2; + m_context << Instruction::DUP1; + // stack: offset sliceStart length sliceEnd - m_context.appendInlineAssembly( - Whiskers(R"({ - if gt(sliceStart, sliceEnd) { } - if gt(sliceEnd, length) { } - - offset := add(offset, mul(sliceStart, )) - length := sub(sliceEnd, sliceStart) - })") - ("stride", toString(arrayType->calldataStride())) - ("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end")) - ("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length")) - .render(), - {"offset", "length", "sliceStart", "sliceEnd"} - ); + m_context << Instruction::SWAP3; + // stack: sliceEnd sliceStart length offset - m_context << Instruction::POP << Instruction::POP; + m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2); return false; } @@ -1866,7 +1883,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) // we want to avoid having a reference to the runtime function entry point in the // constructor context, since this would force the compiler to include unreferenced // internal functions in the runtime contex. - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); + utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); else if (auto contract = dynamic_cast(declaration)) @@ -2438,10 +2455,12 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) { - if (!_variable.isConstant()) - setLValueFromDeclaration(_variable, _expression); - else + if (_variable.isConstant()) acceptAndConvert(*_variable.value(), *_variable.annotation().type); + else if (_variable.immutable()) + setLValue(_expression, _variable); + else + setLValueFromDeclaration(_variable, _expression); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 5fd17bf5c1b7..3a04d6f92529 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -144,9 +144,46 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const m_context << Instruction::POP; } + +ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable): + LValue(_compilerContext, _variable.annotation().type), m_variable(_variable) +{ + solAssert(_variable.immutable(), ""); +} + +void ImmutableItem::retrieveValue(SourceLocation const&, bool) const +{ + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(!m_context.runtimeContext(), "Tried to read immutable at construction time."); + for (auto&& slotName: m_context.immutableVariableSlotNames(m_variable)) + m_context.appendImmutable(slotName); +} + +void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const +{ + CompilerUtils utils(m_context); + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(_sourceType.isValueType(), ""); + + utils.convertType(_sourceType, *m_dataType, true); + m_context << m_context.immutableMemoryOffset(m_variable); + if (_move) + utils.moveIntoStack(m_dataType->sizeOnStack()); + else + utils.copyToStackTop(m_dataType->sizeOnStack() + 1, m_dataType->sizeOnStack()); + utils.storeInMemoryDynamic(*m_dataType, false); + m_context << Instruction::POP; +} + +void ImmutableItem::setToZero(SourceLocation const&, bool) const +{ + solAssert(false, "Attempted to set immutable variable to zero."); +} + StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): StorageItem(_compilerContext, *_declaration.annotation().type) { + solAssert(!_declaration.immutable(), ""); auto const& location = m_context.storageLocationOfVariable(_declaration); m_context << location.first << u256(location.second); } diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index 1cd4def01b50..cd72f37e00a3 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -82,12 +83,12 @@ class StackVariable: public LValue unsigned sizeOnStack() const override { return 0; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -108,12 +109,12 @@ class MemoryItem: public LValue MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true); unsigned sizeOnStack() const override { return 1; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -122,6 +123,30 @@ class MemoryItem: public LValue bool m_padded = false; }; +/** + * Reference to an immutable variable. During contract creation this refers to a location in memory. At the + * end of contract creation the values from these memory locations are copied into all occurrences of the immutable + * variable in the runtime code. + */ +class ImmutableItem: public LValue +{ +public: + ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable); + unsigned sizeOnStack() const override { return 0; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; + void storeValue( + Type const& _sourceType, + langutil::SourceLocation const& _location = {}, + bool _move = false + ) const override; + void setToZero( + langutil::SourceLocation const& _location = {}, + bool _removeReference = true + ) const override; +private: + VariableDeclaration const& m_variable; +}; + /** * Reference to some item in storage. On the stack this is , * where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied @@ -136,12 +161,12 @@ class StorageItem: public LValue StorageItem(CompilerContext& _compilerContext, Type const& _type); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -158,12 +183,12 @@ class StorageByteArrayElement: public LValue StorageByteArrayElement(CompilerContext& _compilerContext); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -180,12 +205,12 @@ class TupleObject: public LValue TupleObject(CompilerContext& _compilerContext, std::vector>&& _lvalues); unsigned sizeOnStack() const override; void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 046769e43169..28a4f69fbbf0 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -658,6 +658,8 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); + solAssert(_type.baseType()->isValueType(), ""); + string functionName = "array_push_zero_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -794,6 +796,7 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) }); } + string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); @@ -933,6 +936,28 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type }); } +string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type) +{ + solAssert(_type.dataStoredIn(DataLocation::CallData), ""); + solAssert(_type.isDynamicallySized(), ""); + string functionName = "calldata_array_index_range_access_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (offset, length, startIndex, endIndex) -> offsetOut, lengthOut { + if gt(startIndex, endIndex) { } + if gt(endIndex, length) { } + offsetOut := add(offset, mul(startIndex, )) + lengthOut := sub(endIndex, startIndex) + } + )") + ("functionName", functionName) + ("stride", to_string(_type.calldataStride())) + ("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end")) + ("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length")) + .render(); + }); +} + string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) { solAssert(_type.isDynamicallyEncoded(), ""); @@ -1156,7 +1181,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, value) - } + } )") ("functionName", functionName) .render(); @@ -1180,7 +1205,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, (value)) - } + } )") ("functionName", functionName) ("cleanup", cleanupFunction(_type)) @@ -1312,28 +1337,112 @@ string YulUtilFunctions::allocationFunction() }); } -string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type) +string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type) { - solUnimplementedAssert(!_type.isByteArray(), ""); + if (_type.baseType()->hasSimpleZeroValueInMemory()) + return zeroMemoryFunction(*_type.baseType()); + return zeroComplexMemoryArrayFunction(_type); +} + +string YulUtilFunctions::zeroMemoryFunction(Type const& _type) +{ + solAssert(_type.hasSimpleZeroValueInMemory(), ""); - string functionName = "allocate_memory_array_" + _type.identifier(); + string functionName = "zero_memory_chunk_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (length) -> memPtr { - memPtr := ((length)) - - mstore(memPtr, length) - + function (dataStart, dataSizeInBytes) { + calldatacopy(dataStart, calldatasize(), dataSizeInBytes) } )") ("functionName", functionName) - ("alloc", allocationFunction()) - ("allocSize", arrayAllocationSizeFunction(_type)) - ("dynamic", _type.isDynamicallySized()) .render(); }); } +string YulUtilFunctions::zeroComplexMemoryArrayFunction(ArrayType const& _type) +{ + solAssert(!_type.baseType()->hasSimpleZeroValueInMemory(), ""); + + string functionName = "zero_complex_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + solAssert(_type.memoryStride() == 32, ""); + return Whiskers(R"( + function (dataStart, dataSizeInBytes) { + for {let i := 0} lt(i, dataSizeInBytes) { i := add(i, ) } { + mstore(add(dataStart, i), ()) + } + } + )") + ("functionName", functionName) + ("stride", to_string(_type.memoryStride())) + ("zeroValue", zeroValueFunction(*_type.baseType(), false)) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType const& _type) +{ + solUnimplementedAssert(!_type.isByteArray(), ""); + + string functionName = "allocate_and_zero_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (length) -> memPtr { + let allocSize := (length) + memPtr := (allocSize) + let dataStart := memPtr + let dataSize := allocSize + + dataStart := add(dataStart, 32) + dataSize := sub(dataSize, 32) + mstore(memPtr, length) + + (dataStart, dataSize) + } + )") + ("functionName", functionName) + ("alloc", allocationFunction()) + ("allocSize", arrayAllocationSizeFunction(_type)) + ("zeroArrayFunction", zeroMemoryArrayFunction(_type)) + ("dynamic", _type.isDynamicallySized()) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type) +{ + string functionName = "allocate_and_initialize_memory_struct_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function () -> memPtr { + let allocSize := () + memPtr := (allocSize) + let offset := memPtr + <#member> + mstore(offset, ()) + offset := add(offset, 32) + + } + )"); + templ("functionName", functionName); + templ("alloc", allocationFunction()); + + TypePointers const& members = _type.memoryMemberTypes(); + templ("allocSize", _type.memoryDataSize().str()); + + vector> memberParams(members.size()); + for (size_t i = 0; i < members.size(); ++i) + { + solAssert(members[i]->memoryHeadSize() == 32, ""); + solAssert(members[i]->dataStoredIn(DataLocation::Memory), ""); + memberParams[i]["zeroValue"] = zeroValueFunction(*members[i], false); + } + templ("member", memberParams); + return templ.render(); + }); +} + string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) { if (_from.category() == Type::Category::Function) @@ -1365,6 +1474,37 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) }); } + if (_from.category() == Type::Category::ArraySlice) + { + solAssert(_from.isDynamicallySized(), ""); + solAssert(_from.dataStoredIn(DataLocation::CallData), ""); + solAssert(_to.category() == Type::Category::Array, ""); + + ArraySliceType const& fromType = dynamic_cast(_from); + ArrayType const& targetType = dynamic_cast(_to); + + solAssert( + *fromType.arrayType().baseType() == *targetType.baseType(), + "Converting arrays of different type is not possible" + ); + + string const functionName = + "convert_" + + _from.identifier() + + "_to_" + + _to.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (offset, length) -> outOffset, outLength { + outOffset := offset + outLength := length + } + )") + ("functionName", functionName) + .render(); + }); + } + if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1) return conversionFunctionSpecial(_from, _to); @@ -1831,23 +1971,58 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) }); } -string YulUtilFunctions::zeroValueFunction(Type const& _type) +string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes) { - solUnimplementedAssert(_type.sizeOnStack() == 1, "Stacksize not yet implemented!"); - solUnimplementedAssert(_type.isValueType(), "Zero value for non-value types not yet implemented"); + solAssert(_type.category() != Type::Category::Mapping, ""); - string const functionName = "zero_value_for_" + _type.identifier(); + string const functionName = "zero_value_for_" + string(_splitFunctionTypes ? "split_" : "") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - return Whiskers(R"( - function () -> ret { - - } - )") + FunctionType const* fType = dynamic_cast(&_type); + if (fType && fType->kind() == FunctionType::Kind::External && _splitFunctionTypes) + return Whiskers(R"( + function () -> retAddress, retFunction { + retAddress := 0 + retFunction := 0 + } + )") ("functionName", functionName) - ("body", "ret := 0x0") .render(); - }); + + Whiskers templ(R"( + function () -> ret { + ret := + } + )"); + templ("functionName", functionName); + + if (_type.isValueType()) + { + solAssert(( + _type.hasSimpleZeroValueInMemory() || + (fType && (fType->kind() == FunctionType::Kind::Internal || fType->kind() == FunctionType::Kind::External)) + ), ""); + templ("zeroValue", "0"); + } + else + { + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + if (auto const* arrayType = dynamic_cast(&_type)) + { + if (_type.isDynamicallySized()) + templ("zeroValue", to_string(CompilerUtils::zeroPointer)); + else + templ("zeroValue", allocateAndInitializeMemoryArrayFunction(*arrayType) + "(" + to_string(unsigned(arrayType->length())) + ")"); + + } + else if (auto const* structType = dynamic_cast(&_type)) + templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()"); + else + solUnimplementedAssert(false, ""); + } + + return templ.render(); + }); } string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index cf50c785f4c2..b902f9f645fc 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -37,6 +37,7 @@ class Type; class ArrayType; class MappingType; class IntegerType; +class StructType; /** * Component that can generate various useful Yul functions. @@ -154,6 +155,7 @@ class YulUtilFunctions /// to store an array in memory given its length (internally encoded, not ABI encoded). /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); + /// @returns the name of a function that converts a storage slot number /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. @@ -175,6 +177,11 @@ class YulUtilFunctions /// signature: (baseRef, index) -> offset[, length] std::string calldataArrayIndexAccessFunction(ArrayType const& _type); + /// @returns the name of a function that returns offset and length for array slice + /// for the given array offset, length and start and end indices for slice + /// signature: (arrayOffset, arrayLength, sliceStart, sliceEnd) -> offset, length + std::string calldataArrayIndexRangeAccess(ArrayType const& _type); + /// @returns the name of a function that follows a calldata tail while performing /// bounds checks. /// signature: (baseRef, tailPointer) -> offset[, length] @@ -245,10 +252,27 @@ class YulUtilFunctions /// Return value: pointer std::string allocationFunction(); - /// @returns the name of a function that allocates a memory array. + /// @returns the name of a function that zeroes an array. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that zeroes a chunk of memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryFunction(Type const& _type); + + /// @returns the name of a function that zeroes an array + /// where the base does not have simple zero value in memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroComplexMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory array. /// For dynamic arrays it adds space for length and stores it. /// signature: (length) -> memPtr - std::string allocateMemoryArrayFunction(ArrayType const& _type); + std::string allocateAndInitializeMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory struct. + /// signature: (members) -> memPtr + std::string allocateAndInitializeMemoryStructFunction(StructType const& _type); /// @returns the name of the function that converts a value of type @a _from /// to a value of type @a _to. The resulting vale is guaranteed to be in range @@ -283,8 +307,9 @@ class YulUtilFunctions std::string negateNumberCheckedFunction(Type const& _type); /// @returns the name of a function that returns the zero value for the - /// provided type - std::string zeroValueFunction(Type const& _type); + /// provided type. + /// @param _splitFunctionTypes if false, returns two zeroes + std::string zeroValueFunction(Type const& _type, bool _splitFunctionTypes = true); /// @returns the name of a function that will set the given storage item to /// zero diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 7184247f4014..26f6f030961a 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -31,6 +32,12 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; +ContractDefinition const& IRGenerationContext::mostDerivedContract() const +{ + solAssert(m_mostDerivedContract, "Most derived contract requested but not set."); + return *m_mostDerivedContract; +} + IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl) { auto const& [it, didInsert] = m_localVariables.emplace( @@ -70,26 +77,9 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl) return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); } -FunctionDefinition const& IRGenerationContext::virtualFunction(FunctionDefinition const& _function) -{ - // @TODO previously, we had to distinguish creation context and runtime context, - // but since we do not work with jump positions anymore, this should not be a problem, right? - string name = _function.name(); - FunctionType functionType(_function); - for (auto const& contract: m_inheritanceHierarchy) - for (FunctionDefinition const* function: contract->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); -} - string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration) { - return functionName(virtualFunction(_functionDeclaration)); + return functionName(_functionDeclaration.resolveVirtual(mostDerivedContract())); } string IRGenerationContext::newYulVariable() @@ -120,12 +110,13 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) templ("arrow", _out > 0 ? "->" : ""); templ("out", suffixedVariableNameList("out_", 0, _out)); vector> functions; - for (auto const& contract: m_inheritanceHierarchy) + for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) for (FunctionDefinition const* function: contract->definedFunctions()) if ( + FunctionType const* functionType = TypeProvider::function(*function)->asCallableFunction(false); !function->isConstructor() && - function->parameters().size() == _in && - function->returnParameters().size() == _out + TupleType(functionType->parameterTypes()).sizeOnStack() == _in && + TupleType(functionType->returnParameterTypes()).sizeOnStack() == _out ) { // 0 is reserved for uninitialized function pointers diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 473b62482d4a..b0fc92cdb8c3 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -61,11 +61,12 @@ class IRGenerationContext MultiUseYulFunctionCollector& functionCollector() { return m_functions; } - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector _hierarchy) + /// Sets the most derived contract (the one currently being compiled)> + void setMostDerivedContract(ContractDefinition const& _mostDerivedContract) { - m_inheritanceHierarchy = std::move(_hierarchy); + m_mostDerivedContract = &_mostDerivedContract; } + ContractDefinition const& mostDerivedContract() const; IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); @@ -81,7 +82,6 @@ class IRGenerationContext std::string functionName(FunctionDefinition const& _function); std::string functionName(VariableDeclaration const& _varDecl); - FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration); std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration); std::string newYulVariable(); @@ -103,7 +103,7 @@ class IRGenerationContext langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; - std::vector m_inheritanceHierarchy; + ContractDefinition const* m_mostDerivedContract = nullptr; std::map m_localVariables; /// Storage offsets of state variables std::map> m_stateVariables; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 8da57605548d..5ae4b71d7408 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -110,7 +110,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) t("functions", m_context.functionCollector().requestedFunctions()); resetContext(_contract); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); t("RuntimeObject", runtimeObjectName(_contract)); t("dispatch", dispatchRoutine(_contract)); for (auto const* contract: _contract.annotation().linearizedBaseContracts) @@ -133,6 +133,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { Whiskers t(R"( function () { + } )"); @@ -142,9 +143,14 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); t("params", params); string retParams; + string retInit; for (auto const& varDecl: _function.returnParameters()) + { retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); + retInit += generateInitialAssignment(*varDecl); + } t("returns", retParams.empty() ? "" : " -> " + retParams); + t("initReturnVariables", retInit); t("body", generate(_function.body())); return t.render(); }); @@ -157,6 +163,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) Type const* type = _varDecl.annotation().type; solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); solAssert(_varDecl.isStateVariable(), ""); if (auto const* mappingType = dynamic_cast(type)) @@ -225,6 +232,13 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } } +string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl) +{ + IRGeneratorForStatements generator(m_context, m_utils); + generator.initializeLocalVar(_varDecl); + return generator.code(); +} + string IRGenerator::constructorCode(ContractDefinition const& _contract) { // Initialization of state variables in base-to-derived order. @@ -249,7 +263,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) IRGeneratorForStatements generator{m_context, m_utils}; for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!variable->isConstant() && !variable->immutable()) generator.initializeStateVar(*variable); out << generator.code(); @@ -258,11 +272,28 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) if (constructor) { - solUnimplementedAssert(constructor->parameters().empty(), ""); + ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); + unsigned paramVars = make_shared(constructor->functionType(false)->parameterTypes())->sizeOnStack(); + + Whiskers t(R"X( + let programSize := datasize("") + let argSize := sub(codesize(), programSize) + + let memoryDataOffset := (argSize) + codecopy(memoryDataOffset, programSize, argSize) + + (memoryDataOffset, add(memoryDataOffset, argSize)) - // TODO base constructors + () + )X"); + t("object", creationObjectName(_contract)); + t("allocate", m_utils.allocationFunction()); + t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "); + t("params", suffixedVariableNameList("param_", 0, paramVars)); + t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true)); + t("constructorName", m_context.functionName(*constructor)); - out << m_context.functionName(*constructor) + "()\n"; + out << t.render(); } return out.str(); @@ -388,7 +419,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract) ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); for (auto const& var: ContractType(_contract).stateVariables()) m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index d1ec580f61d4..e0c1dcd4f9e2 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -61,6 +61,9 @@ class IRGenerator /// Generates a getter for the given declaration and returns its name std::string generateGetter(VariableDeclaration const& _varDecl); + /// Generates code that assigns the initial value of the respective type. + std::string generateInitialAssignment(VariableDeclaration const& _varDecl); + std::string constructorCode(ContractDefinition const& _contract); std::string deployCode(ContractDefinition const& _contract); std::string callValueCheck(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 7ce1785b647c..57c8cb152903 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -140,6 +140,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va { solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable."); solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); if (_varDecl.value()) { _varDecl.value()->accept(*this); @@ -153,6 +154,19 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va } } +void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) +{ + solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); + + auto const* type = _varDecl.type(); + if (auto const* refType = dynamic_cast(type)) + if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) + return; + + IRVariable zero = zeroValue(*type); + assign(m_context.localVariable(_varDecl), zero); +} + void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { if (Expression const* expression = _varDeclStatement.initialValue()) @@ -178,7 +192,10 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var else for (auto const& decl: _varDeclStatement.declarations()) if (decl) + { declare(m_context.addLocalVariable(*decl)); + initializeLocalVar(*decl); + } } bool IRGeneratorForStatements::visit(Conditional const& _conditional) @@ -567,7 +584,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } define(_functionCall) << - m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) << + m_context.internalDispatch( + TupleType(functionType->parameterTypes()).sizeOnStack(), + TupleType(functionType->returnParameterTypes()).sizeOnStack() + ) << "(" << IRVariable(_functionCall.expression()).part("functionIdentifier").name() << joinHumanReadablePrefixed(args) << @@ -666,7 +686,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); define(_functionCall) << - m_utils.allocateMemoryArrayFunction(arrayType) << + m_utils.allocateAndInitializeMemoryArrayFunction(arrayType) << "(" << value.commaSeparatedList() << ")\n"; @@ -739,6 +759,25 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } } +void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) +{ + FunctionType const& previousType = dynamic_cast(*_options.expression().annotation().type); + + solUnimplementedAssert(!previousType.bound(), ""); + + // Copy over existing values. + for (auto const& item: previousType.stackItems()) + define(IRVariable(_options).part(get<0>(item)), IRVariable(_options.expression()).part(get<0>(item))); + + for (size_t i = 0; i < _options.names().size(); ++i) + { + string const& name = *_options.names()[i]; + solAssert(name == "salt" || name == "gas" || name == "value", ""); + + define(IRVariable(_options).part(name), *_options.options()[i]); + } +} + void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.memberName(); @@ -981,9 +1020,16 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } }); } - else if (baseType.category() == Type::Category::Array) + else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice) { - ArrayType const& arrayType = dynamic_cast(baseType); + ArrayType const& arrayType = + baseType.category() == Type::Category::Array ? + dynamic_cast(baseType) : + dynamic_cast(baseType).arrayType(); + + if (baseType.category() == Type::Category::ArraySlice) + solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), ""); + solAssert(_indexAccess.indexExpression(), "Index expression expected."); switch (arrayType.location()) @@ -1066,9 +1112,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) solAssert(false, "Index access only allowed for mappings or arrays."); } -void IRGeneratorForStatements::endVisit(IndexRangeAccess const&) +void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) { - solUnimplementedAssert(false, "Index range accesses not yet implemented."); + Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; + solAssert( + baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, + "Index range accesses is available only on arrays and array slices." + ); + + ArrayType const& arrayType = + baseType.category() == Type::Category::Array ? + dynamic_cast(baseType) : + dynamic_cast(baseType).arrayType(); + + switch (arrayType.location()) + { + case DataLocation::CallData: + { + solAssert(baseType.isDynamicallySized(), ""); + IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()}; + if (_indexRangeAccess.startExpression()) + define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()}); + else + define(sliceStart) << u256(0) << "\n"; + + IRVariable sliceEnd{ + m_context.newYulVariable(), + *TypeProvider::uint256() + }; + if (_indexRangeAccess.endExpression()) + define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()}); + else + define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length")); + + IRVariable range{_indexRangeAccess}; + define(range) << + m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" << + IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " << + sliceStart.name() << ", " << + sliceEnd.name() << ")\n"; + break; + } + default: + solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays."); + } } void IRGeneratorForStatements::endVisit(Identifier const& _identifier) @@ -1097,13 +1184,14 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) return; } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - define(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; + define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n"; else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { // TODO for the constant case, we have to be careful: // If the value is visited twice, `defineExpression` is called twice on // the same expression. solUnimplementedAssert(!varDecl->isConstant(), ""); + solUnimplementedAssert(!varDecl->immutable(), ""); if (m_context.isLocalVariable(*varDecl)) setLValue(_identifier, IRLValue{ *varDecl->annotation().type, @@ -1367,6 +1455,12 @@ std::ostream& IRGeneratorForStatements::define(IRVariable const& _var) return m_code; } +void IRGeneratorForStatements::declare(IRVariable const& _var) +{ + if (_var.type().sizeOnStack() > 0) + m_code << "let " << _var.commaSeparatedList() << "\n"; +} + void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare) { string output; @@ -1386,10 +1480,15 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable _rhs.commaSeparatedList() << ")\n"; } -void IRGeneratorForStatements::declare(IRVariable const& _var) + +IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes) { - if (_var.type().sizeOnStack() > 0) - m_code << "let " << _var.commaSeparatedList() << "\n"; + IRVariable irVar{ + "zero_value_for_type_" + _type.identifier() + m_context.newYulVariable(), + _type + }; + define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n"; + return irVar; } void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr) @@ -1519,7 +1618,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable solAssert(dynamic_cast(&_lvalue.type), ""); auto const* valueReferenceType = dynamic_cast(&_value.type()); solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), ""); - m_code << "mstore(" + _memory.address + ", " + _value.name() + ")\n"; + m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; } }, [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 785b02e098e6..710961cd768b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -46,6 +46,8 @@ class IRGeneratorForStatements: public ASTConstVisitor /// Generates code to initialize the given state variable. void initializeStateVar(VariableDeclaration const& _varDecl); + /// Generates code to initialize the given local variable. + void initializeLocalVar(VariableDeclaration const& _varDecl); void endVisit(VariableDeclarationStatement const& _variableDeclaration) override; bool visit(Conditional const& _conditional) override; @@ -60,6 +62,7 @@ class IRGeneratorForStatements: public ASTConstVisitor void endVisit(UnaryOperation const& _unaryOperation) override; bool visit(BinaryOperation const& _binOp) override; void endVisit(FunctionCall const& _funCall) override; + void endVisit(FunctionCallOptions const& _funCallOptions) override; void endVisit(MemberAccess const& _memberAccess) override; bool visit(InlineAssembly const& _inlineAsm) override; void endVisit(IndexAccess const& _indexAccess) override; @@ -99,6 +102,11 @@ class IRGeneratorForStatements: public ASTConstVisitor void declareAssign(IRVariable const& _var, IRVariable const& _value, bool _define); + /// @returns an IRVariable with the zero + /// value of @a _type. + /// @param _splitFunctionTypes if false, returns two zeroes + IRVariable zeroValue(Type const& _type, bool _splitFunctionTypes = true); + void appendAndOrOperatorCode(BinaryOperation const& _binOp); void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index dd1e5136cd40..141a3eb32d3d 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -432,12 +432,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) m_context.newValue(*param); m_context.setUnknownValue(*param); } - - m_errorReporter.warning( - _funCall.location(), - "Assertion checker does not support recursive function calls.", - SecondarySourceLocation().append("Starting from function:", funDef->location()) - ); } else { diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index af50b27226e8..e10c16d39ec1 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -79,10 +79,9 @@ void CHC::analyze(SourceUnit const& _source) resetSourceAnalysis(); - auto boolSort = make_shared(smt::Kind::Bool); auto genesisSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); addRule(genesis(), "genesis"); @@ -131,12 +130,9 @@ bool CHC::visit(ContractDefinition const& _contract) clearIndices(&_contract); - - // TODO create static instances for Bool/Int sorts in SolverInterface. - auto boolSort = make_shared(smt::Kind::Bool); auto errorFunctionSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); string suffix = _contract.name() + "_" + to_string(_contract.id()); @@ -459,6 +455,8 @@ void CHC::endVisit(FunctionCall const& _funCall) SMTEncoder::endVisit(_funCall); break; case FunctionType::Kind::Internal: + internalFunctionCall(_funCall); + break; case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: @@ -525,6 +523,39 @@ void CHC::visitAssert(FunctionCall const& _funCall) m_context.addAssertion(m_error.currentValue() == previousError); } +void CHC::internalFunctionCall(FunctionCall const& _funCall) +{ + solAssert(m_currentContract, ""); + + auto const* function = functionCallToDefinition(_funCall); + if (function) + { + if (m_currentFunction && !m_currentFunction->isConstructor()) + m_callGraph[m_currentFunction].insert(function); + else + m_callGraph[m_currentContract].insert(function); + auto const* contract = function->annotation().contract; + + // Libraries can have constants as their "state" variables, + // so we need to ensure they were constructed correctly. + if (contract->isLibrary()) + m_context.addAssertion(interface(*contract)); + } + + auto previousError = m_error.currentValue(); + + m_context.addAssertion(predicate(_funCall)); + + connectBlocks( + m_currentBlock, + (m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract), + (m_error.currentValue() > 0) + ); + m_context.addAssertion(m_error.currentValue() == 0); + m_error.increaseIndex(); + m_context.addAssertion(m_error.currentValue() == previousError); +} + void CHC::unknownFunctionCall(FunctionCall const&) { /// Function calls are not handled at the moment, @@ -580,12 +611,7 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c bool CHC::shouldVisit(FunctionDefinition const& _function) const { - if ( - _function.isPublic() && - _function.isImplemented() - ) - return true; - return false; + return _function.isImplemented(); } void CHC::setCurrentBlock( @@ -634,29 +660,25 @@ vector CHC::stateSorts(ContractDefinition const& _contract) smt::SortPointer CHC::constructorSort() { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); return make_shared( - vector{intSort} + m_stateSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts, + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort() { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( m_stateSorts, - boolSort + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( stateSorts(_contract), - boolSort + smt::SortProvider::boolSort ); } @@ -673,8 +695,6 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) /// - 1 set of output variables smt::SortPointer CHC::sort(FunctionDefinition const& _function) { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); @@ -682,8 +702,8 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function) for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, + smt::SortProvider::boolSort ); } @@ -695,13 +715,12 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto fSort = dynamic_pointer_cast(sort(*m_currentFunction)); solAssert(fSort, ""); - auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) varSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( fSort->domain + varSorts, - boolSort + smt::SortProvider::boolSort ); } @@ -710,16 +729,14 @@ smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractD auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); auto sorts = stateSorts(_contract); - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts, outputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + sorts + inputSorts + sorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts, + smt::SortProvider::boolSort ); } @@ -919,6 +936,34 @@ smt::Expression CHC::predicate( return _block(_arguments); } +smt::Expression CHC::predicate(FunctionCall const& _funCall) +{ + auto const* function = functionCallToDefinition(_funCall); + if (!function) + return smt::Expression(true); + + m_error.increaseIndex(); + vector args{m_error.currentValue()}; + auto const* contract = function->annotation().contract; + + args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : currentStateVariables(); + args += symbolicArguments(_funCall); + for (auto const& var: m_stateVariables) + m_context.variable(*var)->increaseIndex(); + args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); + + auto const& returnParams = function->returnParameters(); + for (auto param: returnParams) + if (m_context.knownVariable(*param)) + m_context.variable(*param)->increaseIndex(); + else + createVariable(*param); + for (auto const& var: function->returnParameters()) + args.push_back(m_context.variable(*var)->currentValue()); + + return (*m_summaries.at(contract).at(function))(args); +} + void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) { m_interface->addRule(_rule, _ruleName); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 889c21b6aa35..4f601a2d51b9 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -76,6 +76,7 @@ class CHC: public SMTEncoder void endVisit(Continue const& _node) override; void visitAssert(FunctionCall const& _funCall); + void internalFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); //@} @@ -164,6 +165,8 @@ class CHC: public SMTEncoder smt::Expression predicate(smt::SymbolicFunctionVariable const& _block); /// @returns a predicate application over @param _arguments. smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + /// @returns the summary predicate for the called function. + smt::Expression predicate(FunctionCall const& _funCall); /// @returns a predicate that defines a constructor summary. smt::Expression summary(ContractDefinition const& _contract); /// @returns a predicate that defines a function summary. diff --git a/libsolidity/formal/EncodingContext.cpp b/libsolidity/formal/EncodingContext.cpp index ff193d2e2a4b..1f7b7a5e011c 100644 --- a/libsolidity/formal/EncodingContext.cpp +++ b/libsolidity/formal/EncodingContext.cpp @@ -28,8 +28,8 @@ EncodingContext::EncodingContext(): m_thisAddress(make_unique("this", *this)) { auto sort = make_shared( - make_shared(Kind::Int), - make_shared(Kind::Int) + SortProvider::intSort, + SortProvider::intSort ); m_balances = make_unique(sort, "balances", *this); } diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 3b2f48816589..0efaf90a9bdb 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -673,7 +673,6 @@ void SMTEncoder::visitAssert(FunctionCall const& _funCall) auto const& args = _funCall.arguments(); solAssert(args.size() == 1, ""); solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); - addPathImpliedExpression(expr(*args.front())); } void SMTEncoder::visitRequire(FunctionCall const& _funCall) diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 512be65bb2e0..106ada82092c 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,6 +17,8 @@ #pragma once +#include + #include #include #include @@ -52,94 +54,6 @@ enum class CheckResult SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; -enum class Kind -{ - Int, - Bool, - Function, - Array, - Sort -}; - -struct Sort -{ - Sort(Kind _kind): - kind(_kind) {} - virtual ~Sort() = default; - virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } - - Kind const kind; -}; -using SortPointer = std::shared_ptr; - -struct FunctionSort: public Sort -{ - FunctionSort(std::vector _domain, SortPointer _codomain): - Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherFunction = dynamic_cast(&_other); - solAssert(_otherFunction, ""); - if (domain.size() != _otherFunction->domain.size()) - return false; - if (!std::equal( - domain.begin(), - domain.end(), - _otherFunction->domain.begin(), - [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } - )) - return false; - solAssert(codomain, ""); - solAssert(_otherFunction->codomain, ""); - return *codomain == *_otherFunction->codomain; - } - - std::vector domain; - SortPointer codomain; -}; - -struct ArraySort: public Sort -{ - /// _domain is the sort of the indices - /// _range is the sort of the values - ArraySort(SortPointer _domain, SortPointer _range): - Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherArray = dynamic_cast(&_other); - solAssert(_otherArray, ""); - solAssert(_otherArray->domain, ""); - solAssert(_otherArray->range, ""); - solAssert(domain, ""); - solAssert(range, ""); - return *domain == *_otherArray->domain && *range == *_otherArray->range; - } - - SortPointer domain; - SortPointer range; -}; - -struct SortSort: public Sort -{ - SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherSort = dynamic_cast(&_other); - solAssert(_otherSort, ""); - solAssert(_otherSort->inner, ""); - solAssert(inner, ""); - return *inner == *_otherSort->inner; - } - - SortPointer inner; -}; - // Forward declaration. SortPointer smtSort(Type const& _type); diff --git a/libsolidity/formal/Sorts.cpp b/libsolidity/formal/Sorts.cpp new file mode 100644 index 000000000000..195048e56ee8 --- /dev/null +++ b/libsolidity/formal/Sorts.cpp @@ -0,0 +1,29 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + + +#include + +using namespace std; + +namespace solidity::frontend::smt +{ + +shared_ptr const SortProvider::boolSort{make_shared(Kind::Bool)}; +shared_ptr const SortProvider::intSort{make_shared(Kind::Int)}; + +} diff --git a/libsolidity/formal/Sorts.h b/libsolidity/formal/Sorts.h new file mode 100644 index 000000000000..d266444625ea --- /dev/null +++ b/libsolidity/formal/Sorts.h @@ -0,0 +1,125 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace solidity::frontend::smt +{ + +enum class Kind +{ + Int, + Bool, + Function, + Array, + Sort +}; + +struct Sort +{ + Sort(Kind _kind): + kind(_kind) {} + virtual ~Sort() = default; + virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } + + Kind const kind; +}; +using SortPointer = std::shared_ptr; + +struct FunctionSort: public Sort +{ + FunctionSort(std::vector _domain, SortPointer _codomain): + Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherFunction = dynamic_cast(&_other); + solAssert(_otherFunction, ""); + if (domain.size() != _otherFunction->domain.size()) + return false; + if (!std::equal( + domain.begin(), + domain.end(), + _otherFunction->domain.begin(), + [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } + )) + return false; + solAssert(codomain, ""); + solAssert(_otherFunction->codomain, ""); + return *codomain == *_otherFunction->codomain; + } + + std::vector domain; + SortPointer codomain; +}; + +struct ArraySort: public Sort +{ + /// _domain is the sort of the indices + /// _range is the sort of the values + ArraySort(SortPointer _domain, SortPointer _range): + Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherArray = dynamic_cast(&_other); + solAssert(_otherArray, ""); + solAssert(_otherArray->domain, ""); + solAssert(_otherArray->range, ""); + solAssert(domain, ""); + solAssert(range, ""); + return *domain == *_otherArray->domain && *range == *_otherArray->range; + } + + SortPointer domain; + SortPointer range; +}; + +struct SortSort: public Sort +{ + SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherSort = dynamic_cast(&_other); + solAssert(_otherSort, ""); + solAssert(_otherSort->inner, ""); + solAssert(inner, ""); + return *inner == *_otherSort->inner; + } + + SortPointer inner; +}; + +/** Frequently used sorts.*/ +struct SortProvider +{ + static std::shared_ptr const boolSort; + static std::shared_ptr const intSort; +}; + +} diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index a2475b337f49..1d9655a9c909 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -32,9 +32,9 @@ SortPointer smtSort(frontend::Type const& _type) switch (smtKind(_type.category())) { case Kind::Int: - return make_shared(Kind::Int); + return SortProvider::intSort; case Kind::Bool: - return make_shared(Kind::Bool); + return SortProvider::boolSort; case Kind::Function: { auto fType = dynamic_cast(&_type); @@ -45,10 +45,10 @@ SortPointer smtSort(frontend::Type const& _type) // TODO change this when we support tuples. if (returnTypes.size() == 0) // We cannot declare functions without a return sort, so we use the smallest. - returnSort = make_shared(Kind::Bool); + returnSort = SortProvider::boolSort; else if (returnTypes.size() > 1) // Abstract sort. - returnSort = make_shared(Kind::Int); + returnSort = SortProvider::intSort; else returnSort = smtSort(*returnTypes.front()); return make_shared(parameterSorts, returnSort); @@ -65,20 +65,19 @@ SortPointer smtSort(frontend::Type const& _type) { auto stringLitType = dynamic_cast(&_type); solAssert(stringLitType, ""); - auto intSort = make_shared(Kind::Int); - return make_shared(intSort, intSort); + return make_shared(SortProvider::intSort, SortProvider::intSort); } else { solAssert(isArray(_type.category()), ""); auto arrayType = dynamic_cast(&_type); solAssert(arrayType, ""); - return make_shared(make_shared(Kind::Int), smtSortAbstractFunction(*arrayType->baseType())); + return make_shared(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType())); } } default: // Abstract case. - return make_shared(Kind::Int); + return SortProvider::intSort; } } @@ -93,7 +92,7 @@ vector smtSort(vector const& _types) SortPointer smtSortAbstractFunction(frontend::Type const& _type) { if (isFunction(_type.category())) - return make_shared(Kind::Int); + return SortProvider::intSort; return smtSort(_type); } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 8f285c883227..549fc05e9652 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -218,15 +218,28 @@ SymbolicArrayVariable::SymbolicArrayVariable( solAssert(isArray(m_type->category()), ""); } +SymbolicArrayVariable::SymbolicArrayVariable( + SortPointer _sort, + string _uniqueName, + EncodingContext& _context +): + SymbolicVariable(move(_sort), move(_uniqueName), _context) +{ + solAssert(m_sort->kind == Kind::Array, ""); +} + smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const { if (_targetType) + { + solAssert(m_originalType, ""); // StringLiterals are encoded as SMT arrays in the generic case, // but they can also be compared/assigned to fixed bytes, in which // case they'd need to be encoded as numbers. if (auto strType = dynamic_cast(m_originalType)) if (_targetType->category() == frontend::Type::Category::FixedBytes) return smt::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add))); + } return SymbolicVariable::currentValue(_targetType); } diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index be75931f29af..e1c28a8b5c73 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -47,6 +47,8 @@ class SymbolicVariable EncodingContext& _context ); + SymbolicVariable(SymbolicVariable&&) = default; + virtual ~SymbolicVariable() = default; virtual Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const; @@ -212,6 +214,13 @@ class SymbolicArrayVariable: public SymbolicVariable std::string _uniqueName, EncodingContext& _context ); + SymbolicArrayVariable( + SortPointer _sort, + std::string _uniqueName, + EncodingContext& _context + ); + + SymbolicArrayVariable(SymbolicArrayVariable&&) = default; Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override; }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 49b17cf337fd..fe0268fb1aa0 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -383,6 +384,15 @@ bool CompilerStack::analyze() noErrors = false; } + // Check that immutable variables are never read in c'tors and assigned + // exactly once + if (noErrors) + for (Source const* source: m_sourceOrder) + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + ImmutableValidator(m_errorReporter, *contract).analyze(); + if (noErrors) { // Control flow graph generator and analyzer. It can check for issues such as @@ -899,8 +909,7 @@ h256 const& CompilerStack::Source::swarmHash() const string const& CompilerStack::Source::ipfsUrl() const { if (ipfsUrlCached.empty()) - if (scanner->source().size() < 1024 * 256) - ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); + ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); return ipfsUrlCached; } @@ -1373,10 +1382,7 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen MetadataCBOREncoder encoder; if (m_metadataHash == MetadataHash::IPFS) - { - solAssert(_metadata.length() < 1024 * 256, "Metadata too large."); encoder.pushBytes("ipfs", util::ipfsHash(_metadata)); - } else if (m_metadataHash == MetadataHash::Bzzr1) encoder.pushBytes("bzzr1", util::bzzr1Hash(_metadata).asBytes()); else diff --git a/libsolidity/interface/DebugSettings.h b/libsolidity/interface/DebugSettings.h index 67c6d88109b8..34818889c953 100644 --- a/libsolidity/interface/DebugSettings.h +++ b/libsolidity/interface/DebugSettings.h @@ -54,7 +54,7 @@ inline std::optional revertStringsFromString(std::string const& _ for (auto i: {RevertStrings::Default, RevertStrings::Strip, RevertStrings::Debug, RevertStrings::VerboseDebug}) if (revertStringsToString(i) == _str) return i; - return {}; + return std::nullopt; } } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 77e00df057a8..a97114e832e3 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -234,6 +234,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection) "wast", "wasm", "ewasm.wast", "ewasm.wasm", "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.immutableReferences", "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences", "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" @@ -309,13 +310,36 @@ Json::Value formatLinkReferences(std::map const& linkRefere return ret; } -Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap) +Json::Value formatImmutableReferences(map>> const& _immutableReferences) +{ + Json::Value ret(Json::objectValue); + + for (auto const& immutableReference: _immutableReferences) + { + auto const& [identifier, byteOffsets] = immutableReference.second; + Json::Value array(Json::arrayValue); + for (size_t byteOffset: byteOffsets) + { + Json::Value byteRange(Json::objectValue); + byteRange["start"] = Json::UInt(byteOffset); + byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide + array.append(byteRange); + } + ret[identifier] = array; + } + + return ret; +} + +Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap, bool _runtimeObject) { Json::Value output = Json::objectValue; output["object"] = _object.toHex(); output["opcodes"] = evmasm::disassemble(_object.bytecode); output["sourceMap"] = _sourceMap ? *_sourceMap : ""; output["linkReferences"] = formatLinkReferences(_object.linkReferences); + if (_runtimeObject) + output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences); return output; } @@ -982,19 +1006,21 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting )) evmData["bytecode"] = collectEVMObject( compilerStack.object(contractName), - compilerStack.sourceMapping(contractName) + compilerStack.sourceMapping(contractName), + false ); if (compilationSuccess && isArtifactRequested( _inputsAndSettings.outputSelection, file, name, - { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" }, + { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", "evm.deployedBytecode.immutableReferences" }, wildcardMatchesExperimental )) evmData["deployedBytecode"] = collectEVMObject( compilerStack.runtimeObject(contractName), - compilerStack.runtimeSourceMapping(contractName) + compilerStack.runtimeSourceMapping(contractName), + true ); if (!evmData.empty()) @@ -1081,7 +1107,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) { "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }, wildcardMatchesExperimental )) - output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get()); + output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get(), false); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index edf43377f67c..2f23c8c22dd0 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -730,8 +730,18 @@ ASTPointer Parser::parseVariableDeclaration( { if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; - else if (token == Token::Constant) - constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Constant || token == Token::Immutable) + { + if (constantness != VariableDeclaration::Constantness::Mutable) + parserError( + string("Constantness already set to ") + + (constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"") + ); + else if (token == Token::Constant) + constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Immutable) + constantness = VariableDeclaration::Constantness::Immutable; + } else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) diff --git a/libsolutil/IpfsHash.cpp b/libsolutil/IpfsHash.cpp index d6a511a24011..95605cdc17d1 100644 --- a/libsolutil/IpfsHash.cpp +++ b/libsolutil/IpfsHash.cpp @@ -40,6 +40,21 @@ bytes varintEncoding(size_t _n) return encoded; } +bytes encodeByteArray(bytes const& _data) +{ + return bytes{0x0a} + varintEncoding(_data.size()) + _data; +} + +bytes encodeHash(bytes const& _data) +{ + return bytes{0x12, 0x20} + picosha2::hash256(_data); +} + +bytes encodeLinkData(bytes const& _data) +{ + return bytes{0x12} + varintEncoding(_data.size()) + _data; +} + string base58Encode(bytes const& _data) { static string const alphabet{"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}; @@ -53,36 +68,132 @@ string base58Encode(bytes const& _data) reverse(output.begin(), output.end()); return output; } + +struct Chunk +{ + Chunk() = default; + Chunk(bytes _hash, size_t _size, size_t _blockSize): + hash(std::move(_hash)), + size(_size), + blockSize(_blockSize) + {} + + bytes hash = {}; + size_t size = 0; + size_t blockSize = 0; +}; + +using Chunks = vector; + +Chunk combineLinks(Chunks& _links) +{ + bytes data = {}; + bytes lengths = {}; + Chunk chunk = {}; + for (Chunk& link: _links) + { + chunk.size += link.size; + chunk.blockSize += link.blockSize; + + data += encodeLinkData( + bytes {0x0a} + + varintEncoding(link.hash.size()) + + std::move(link.hash) + + bytes{0x12, 0x00, 0x18} + + varintEncoding(link.blockSize) + ); + + lengths += bytes{0x20} + varintEncoding(link.size); + } + + bytes blockData = data + encodeByteArray(bytes{0x08, 0x02, 0x18} + varintEncoding(chunk.size) + lengths); + + chunk.blockSize += blockData.size(); + chunk.hash = encodeHash(blockData); + + return chunk; +} + +Chunks buildNextLevel(Chunks& _currentLevel) +{ + size_t const maxChildNum = 174; + + Chunks nextLevel; + Chunks links; + + for (Chunk& chunk: _currentLevel) + { + links.emplace_back(std::move(chunk.hash), chunk.size, chunk.blockSize); + if (links.size() == maxChildNum) + { + nextLevel.emplace_back(combineLinks(links)); + links = {}; + } + } + if (!links.empty()) + nextLevel.emplace_back(combineLinks(links)); + + return nextLevel; +} + +/// Builds a tree starting from the bottom level where nodes are data nodes. +/// Data nodes should be calculated and passed as the only level in chunk levels +/// Each next level is calculated as following: +/// - Pick up to maxChildNum (174) nodes until a whole level is added, group them and pass to the node in the next level +/// - Do this until the current level has only one node, return the hash in that node +bytes groupChunksBottomUp(Chunks _currentLevel) +{ + // when we reach root it will be the only node in that level + while (_currentLevel.size() != 1) + _currentLevel = buildNextLevel(_currentLevel); + + // top level's only node stores the hash for file + return _currentLevel.front().hash; +} } bytes solidity::util::ipfsHash(string _data) { - assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); + size_t const maxChunkSize = 1024 * 256; + size_t chunkCount = _data.length() / maxChunkSize + (_data.length() % maxChunkSize > 0 ? 1 : 0); + chunkCount = chunkCount == 0 ? 1 : chunkCount; - bytes lengthAsVarint = varintEncoding(_data.size()); + Chunks allChunks; - bytes protobufEncodedData; - // Type: File - protobufEncodedData += bytes{0x08, 0x02}; - if (!_data.empty()) + for (unsigned long chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) { - // Data (length delimited bytes) - protobufEncodedData += bytes{0x12}; - protobufEncodedData += lengthAsVarint; - protobufEncodedData += asBytes(std::move(_data)); + bytes chunkBytes = asBytes( + _data.substr(chunkIndex * maxChunkSize, min(maxChunkSize, _data.length() - chunkIndex * maxChunkSize)) + ); + + bytes lengthAsVarint = varintEncoding(chunkBytes.size()); + + bytes protobufEncodedData; + // Type: File + protobufEncodedData += bytes{0x08, 0x02}; + if (!chunkBytes.empty()) + { + // Data (length delimited bytes) + protobufEncodedData += bytes{0x12}; + protobufEncodedData += lengthAsVarint; + protobufEncodedData += chunkBytes; + } + // filesize: length as varint + protobufEncodedData += bytes{0x18} + lengthAsVarint; + + // PBDag: + // Data: (length delimited bytes) + bytes blockData = encodeByteArray(protobufEncodedData); + + // Multihash: sha2-256, 256 bits + allChunks.emplace_back( + encodeHash(blockData), + chunkBytes.size(), + blockData.size() + ); } - // filesize: length as varint - protobufEncodedData += bytes{0x18} + lengthAsVarint; - - // PBDag: - // Data: (length delimited bytes) - size_t protobufLength = protobufEncodedData.size(); - bytes blockData = bytes{0x0a} + varintEncoding(protobufLength) + std::move(protobufEncodedData); - // TODO Handle "large" files with multiple blocks - - // Multihash: sha2-256, 256 bits - bytes hash = bytes{0x12, 0x20} + picosha2::hash256(std::move(blockData)); - return hash; + + return groupChunksBottomUp(std::move(allChunks)); } string solidity::util::ipfsHashBase58(string _data) diff --git a/libsolutil/vector_ref.h b/libsolutil/vector_ref.h index 3bb71f35e8de..46901a46e5c6 100644 --- a/libsolutil/vector_ref.h +++ b/libsolutil/vector_ref.h @@ -44,7 +44,6 @@ class vector_ref std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(T)); } - template explicit operator vector_ref() const { assert(m_count * sizeof(T) / sizeof(T2) * sizeof(T2) / sizeof(T) == m_count); return vector_ref(reinterpret_cast(m_data), m_count * sizeof(T) / sizeof(T2)); } operator vector_ref() const { return vector_ref(m_data, m_count); } T* data() const { return m_data; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 708e76339b50..758b11d92806 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -324,6 +324,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() case Token::Byte: case Token::Bool: case Token::Address: + case Token::Var: { YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) @@ -513,6 +514,7 @@ YulString Parser::expectAsmIdentifier() case Token::Address: case Token::Bool: case Token::Identifier: + case Token::Var: break; default: expectToken(Token::Identifier); diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 7c0eca27621c..e64420bd91fd 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -203,6 +203,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const EthAssemblyAdapter adapter(assembly); compileEVM(adapter, false, m_optimiserSettings.optimizeStackAllocation); object.bytecode = make_shared(assembly.assemble()); + yulAssert(object.bytecode->immutableReferences.empty(), "Leftover immutables."); object.assembly = assembly.assemblyString(); object.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh new file mode 100644 index 000000000000..1d21fe75c415 --- /dev/null +++ b/scripts/common_cmdline.sh @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------------ +# vim:ts=4:et +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016-2019 solidity contributors. +# ------------------------------------------------------------------------------ + +FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +OLDARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +function compileFull() +{ + local expected_exit_code=0 + local expect_output=0 + if [[ $1 = '-e' ]]; then + expected_exit_code=1 + expect_output=1 + shift; + fi + if [[ $1 = '-w' ]]; then + expect_output=1 + shift; + fi + if [[ $1 = '-o' ]]; then + expect_output=2 + shift; + fi + local args=$FULLARGS + if [[ $1 = '-v' ]]; then + if (echo $2 | grep -Po '(?<=0.4.)\d+' >/dev/null); then + patch=$(echo $2 | grep -Po '(?<=0.4.)\d+') + if (( patch < 22 )); then + args=$OLDARGS + fi + fi + shift 2 + fi + + local files="$*" + local output + + local stderr_path=$(mktemp) + + set +e + "$SOLC" ${args} ${files} >/dev/null 2>"$stderr_path" + local exit_code=$? + local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") + set -e + rm "$stderr_path" + + if [[ \ + ("$exit_code" -ne "$expected_exit_code" || \ + ( $expect_output -eq 0 && -n "$errors" ) || \ + ( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" )) + ]] + then + printError "Unexpected compilation result:" + printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" + printError "Was failure: $exit_code" + echo "$errors" + printError "While calling:" + echo "\"$SOLC\" $ARGS $files" + printError "Inside directory:" + pwd + false + fi +} diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh new file mode 100755 index 000000000000..c56bdc6c98c7 --- /dev/null +++ b/scripts/docs_version_pragma_check.sh @@ -0,0 +1,187 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +# This script verifies that the examples compile with the oldest version mentioned in the pragma. +# It does not verify that it cannot be compiled with an older version +# and it also does not verify that it can be compiled with the newest version compatible with the pragma. + +set -e + +## GLOBAL VARIABLES + +REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) +SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} +source "${REPO_ROOT}/scripts/common.sh" +source "${REPO_ROOT}/scripts/common_cmdline.sh" + +function versionGreater() +{ + v1=$1 + v2=$2 + ver1=( ${v1//./ } ) + ver2=( ${v2//./ } ) + + if (( ${ver1[0]} > ${ver2[0]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} > ${ver2[1]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} == ${ver2[1]} )) && (( ${ver1[2]} > ${ver2[2]} )) + then + return 0 + fi + return 1 +} + +function versionEqual() +{ + if [ "$1" == "$2" ] + then + return 0 + fi + return 1 +} + +function getAllAvailableVersions() +{ + allVersions=() + local allListedVersions=( $( + wget -q -O- https://ethereum.github.io/solc-bin/bin/list.txt | + grep -Po '(?<=soljson-v)\d+.\d+.\d+(?=\+commit)' | + sort -V + ) ) + for listed in "${allListedVersions[@]}" + do + if versionGreater "$listed" "0.4.10" + then + allVersions+=( $listed ) + fi + done +} + +function findMinimalVersion() +{ + local f=$1 + local greater=false + local pragmaVersion + + # Get minimum compiler version defined by pragma + if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")" + sign=">=" + elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")" + sign="^" + elif (grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f")" + sign=">" + greater=true; + else + printError "No valid pragma statement in file. Skipping..." + return + fi + + version="" + for ver in "${allVersions[@]}" + do + if versionGreater "$ver" "$pragmaVersion" + then + minVersion="$ver" + break + elif ([ $greater == false ]) && versionEqual "$ver" "$pragmaVersion" + then + version="$ver" + break + fi + done + + if [ -z version ] + then + printError "No release $sign$pragmaVersion was listed in available releases!" + fi +} + +printTask "Verifying that all examples from the documentation have the correct version range..." +SOLTMPDIR=$(mktemp -d) +( + set -e + cd "$SOLTMPDIR" + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + + getAllAvailableVersions + + for f in *.sol + do + # The contributors guide uses syntax tests, but we cannot + # really handle them here. + if grep -E 'DeclarationError:|// ----' "$f" >/dev/null + then + continue + fi + echo "$f" + + opts='' + # We expect errors if explicitly stated, or if imports + # are used (in the style guide) + if ( ! grep -E "This will not compile after" "$f" >/dev/null && \ + grep -E "This will not compile|import \"" "$f" >/dev/null ) + then + opts="-e" + fi + + # ignore warnings in this case + opts="$opts -o" + + findMinimalVersion $f + if [ -z "$version" ] + then + continue + fi + + opts="$opts -v $version" + + solc_bin="solc-$version" + echo "$solc_bin" + if [[ ! -f "$solc_bin" ]] + then + echo "Downloading release from github..." + if wget -q https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux >/dev/null + then + mv solc-static-linux $solc_bin + else + printError "No release $version was found on github!" + continue + fi + fi + + ln -sf "$solc_bin" "solc" + chmod a+x solc + + SOLC="$SOLTMPDIR/solc" + compileFull $opts "$SOLTMPDIR/$f" + done +) +rm -rf "$SOLTMPDIR" +echo "Done." \ No newline at end of file diff --git a/scripts/endToEndExtraction/create_traces.sh b/scripts/endToEndExtraction/create_traces.sh new file mode 100755 index 000000000000..f167fee5ec10 --- /dev/null +++ b/scripts/endToEndExtraction/create_traces.sh @@ -0,0 +1,22 @@ +BASE_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )" + +mkdir -p build +cd build || exit +cmake ../../../ +make soltest +cd test/ || exit +echo "running soltest on 'semanticTests/extracted'..." +./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --testpath ${BASE_PATH}/../../test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/extracted-tests.trace +echo "running soltest on 'semanticTests/extracted'... done" + +cd $BASE_PATH || exit +git clone git@github.com:ethereum/solidity.git solidity-develop +cd solidity-develop || exit +mkdir -p build +cd build || exit +cmake .. +make soltest +cd test/ || exit +echo "running soltest on 'SolidityEndToEndTest'..." +./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --testpath ${BASE_PATH}/solidity-develop/test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/endToEndExtraction-tests.trace +echo "running soltest on 'SolidityEndToEndTest'... done" diff --git a/scripts/endToEndExtraction/remove-testcases.py b/scripts/endToEndExtraction/remove-testcases.py new file mode 100755 index 000000000000..89f50d0e1bd3 --- /dev/null +++ b/scripts/endToEndExtraction/remove-testcases.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# pylint: disable=consider-using-enumerate, import-error + +import re +import os +import sys +import getopt +import tempfile +from getkey import getkey + + +def parse_call(call): + function = '' + arguments = "" + results = "" + search = re.search(r'// (.*):(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL) + if search: + function = search.group(1) + arguments = search.group(2) + results = search.group(3) + if results.find("#") != -1: + results = results[:results.find("#")] + else: + search = re.search(r'// (.*)(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL) + if search: + function = search.group(1) + arguments = search.group(2) + results = search.group(3) + if results.find("#") != -1: + results = results[:results.find("#")] + if function.find("wei") >= 0: + function = function[:function.find(",")] + return function.strip(), arguments.strip(), results.strip() + + +def colorize(left, right, id): + red = "\x1b[31m" + yellow = "\x1b[33m" + reset = "\x1b[0m" + colors = [red, yellow] + color = colors[id % len(colors)] + function, arguments, results = parse_call(right) + left = left.replace("compileAndRun", color + "compileAndRun" + reset) + right = right.replace("constructor", color + "constructor" + reset) + if function: + left = left.replace(function, color + function + reset) + right = right.replace(function, color + function + reset) + if left.find(function): + bottom = " " * (left.find(function) - 4) + right + else: + bottom = " " + right + return " " + left + "\n" + bottom # " {:<90} {:<90}\n{}".format(left, right, bottom) + + +def get_checks(content, sol_file_path): + constructors = [] + checks = [] + for line in content.split("\n"): + line = line.strip() + if line.startswith("compileAndRun"): + constructors.append(line) + if line.startswith("ABI_CHECK") or line.startswith("BOOST_REQUIRE"): + checks.append(line) + sol_file = open(sol_file_path, "r") + sol_constructors = [] + sol_checks = [] + inside_expectations = False + for line in sol_file.readlines(): + if line.startswith("// constructor()"): + sol_constructors.append(line) + elif inside_expectations and line.startswith("// "): + sol_checks.append(line) + if line.startswith("// ----"): + inside_expectations = True + sol_file.close() + if len(constructors) == len(sol_constructors) == 1: + checks.insert(0, constructors[0]) + sol_checks.insert(0, sol_constructors[0]) + return checks, sol_checks + + +def show_test(name, content, sol_file_path, current_test, test_count): + cpp_file = tempfile.NamedTemporaryFile(delete=False) + cpp_file.write(content.encode()) + cpp_file.close() + + os.system("clear") + print(str(current_test) + " / " + str(test_count) + " - " + name + "\n") + diff_env = os.getenv('DIFF', "/usr/local/bin/colordiff -a -d -w -y -W 200 ") + os.system(diff_env + " " + cpp_file.name + " " + sol_file_path) + os.unlink(cpp_file.name) + print("\n") + + checks, sol_checks = get_checks(content, sol_file_path) + + if len(checks) == len(sol_checks): + for i in range(0, len(checks)): + print(colorize(checks[i].strip(), sol_checks[i].strip(), i)) + else: + print("warning: check count not matching. this should not happen!") + + what = "" + print("\nContinue? (ENTER) Abort? (ANY OTHER KEY)") + while what != '\n': + what = getkey() + if what != '\n': + sys.exit(0) + print() + + +def get_tests(e2e_path): + tests = [] + for f in os.listdir(e2e_path): + if f.endswith(".sol"): + tests.append(f.replace(".sol", "")) + return tests + + +def process_input_file(e2e_path, input_file, interactive): + tests = get_tests(e2e_path) + cpp_file = open(input_file, "r") + inside_test = False + test_name = "" + inside_extracted_test = False + new_lines = 0 + count = 0 + test_content = "" + for line in cpp_file.readlines(): + test = re.search(r'BOOST_AUTO_TEST_CASE\((.*)\)', line, re.M | re.I) + if test: + test_name = test.group(1) + inside_test = True + inside_extracted_test = inside_test & (test_name in tests) + if inside_extracted_test: + count = count + 1 + + if interactive and inside_extracted_test: + test_content = test_content + line + + if not inside_extracted_test: + if line == "\n": + new_lines = new_lines + 1 + else: + new_lines = 0 + if not interactive and new_lines <= 1: + sys.stdout.write(line) + + if line == "}\n": + if interactive and inside_extracted_test: + show_test(test_name, test_content.strip(), e2e_path + "/" + test_name + ".sol", count, len(tests)) + test_content = "" + inside_test = False + cpp_file.close() + sys.stdout.flush() + + +def main(argv): + interactive = False + input_file = None + try: + opts, args = getopt.getopt(argv, "if:") + except getopt.GetoptError: + print("./remove-testcases.py [-i] [-f ]") + sys.exit(1) + + for opt, arg in opts: + if opt == '-i': + interactive = True + elif opt in '-f': + input_file = arg + + base_path = os.path.dirname(__file__) + + if not input_file: + input_file = base_path + "/../../test/libsolidity/SolidityEndToEndTest.cpp" + + e2e_path = base_path + "/../../test/libsolidity/semanticTests/extracted" + + process_input_file(e2e_path, input_file, interactive) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/scripts/endToEndExtraction/verify-testcases.py b/scripts/endToEndExtraction/verify-testcases.py new file mode 100755 index 000000000000..87dc309d8ceb --- /dev/null +++ b/scripts/endToEndExtraction/verify-testcases.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# +# - SolidityEndToEndTest.trace was created with soltest with the following command on +# ./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --no-smt +# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > SolidityEndToEndTest.trace +# - a trace of the semantic tests can be created by using +# ./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --no-smt +# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > semanticTests.trace +# +# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests where +# identical with the tests specified in SolidityEndToEndTest.cpp. +# +# pylint: disable=too-many-instance-attributes + +import re +import os +import sys +import getopt +import json + + +class Trace: + def __init__(self, kind, parameter): + self.kind = kind + self.parameter = parameter + self._input = "" + self._output = "" + self.value = "" + self.result = "" + self.gas = "" + + def get_input(self): + return self._input + + def set_input(self, input): + if self.kind == "create": + # remove cbor encoded metadata from bytecode + length = int(input[-4:], 16) * 2 + self._input = input[:len(input) - length - 4] + + def get_output(self): + return self._output + + def set_output(self, output): + if self.kind == "create": + # remove cbor encoded metadata from bytecode + length = int(output[-4:], 16) * 2 + self._output = output[:len(output) - length - 4] + + def __str__(self): + # we ignore the used gas + result = str( + "kind='" + self.kind + "' parameter='" + self.parameter + "' input='" + self._input + + "' output='" + self._output + "' value='" + self.value + "' result='" + self.result + "'" + ) + return result + + +class TestCase: + def __init__(self, name): + self.name = name + self.metadata = None + self.traces = [] + + def add_trace(self, kind, parameter): + trace = Trace(kind, parameter) + self.traces.append(trace) + return trace + + +class TraceAnalyser: + def __init__(self, file): + self.file = file + self.tests = {} + self.ready = False + + def analyse(self): + trace_file = open(self.file, "r") + trace = None + test_case = None + for line in trace_file.readlines(): + test = re.search(r'Entering test case "(.*)"', line, re.M | re.I) + if test: + test_name = test.group(1) + test_case = TestCase(test_name) + self.tests[test_name] = test_case + + metadata = re.search(r'\s*metadata:\s*(.*)$', line, re.M | re.I) + if metadata: + test_case.metadata = json.loads(metadata.group(1)) + del test_case.metadata["sources"] + del test_case.metadata["compiler"]["version"] + + create = re.search(r'CREATE\s*([a-fA-F0-9]*):', line, re.M | re.I) + if create: + trace = test_case.add_trace("create", create.group(1)) + + call = re.search(r'CALL\s*([a-fA-F0-9]*)\s*->\s*([a-fA-F0-9]*):', line, re.M | re.I) + if call: + trace = test_case.add_trace("call", call.group(1)) # + "->" + call.group(2)) + + if not create and not call: + self.parse_parameters(line, trace) + + trace_file.close() + + print(self.file + ":", len(self.tests), "test-cases.") + + self.ready = True + + @staticmethod + def parse_parameters(line, trace): + input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if input: + trace.input = input.group(1) + output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if output: + trace.output = output.group(1) + result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if result: + trace.result = result.group(1) + gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if gas_used: + trace.gas = gas_used.group(1) + value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if value: + trace.value = value.group(1) + + def diff(self, analyser): + if not self.ready: + self.analyse() + if not analyser.ready: + analyser.analyse() + + intersection = set(self.tests.keys()) & set(analyser.tests.keys()) + mismatches = set() + + for test_name in intersection: + left = self.tests[test_name] + right = analyser.tests[test_name] + if json.dumps(left.metadata) != json.dumps(right.metadata): + mismatches.add( + (test_name, "metadata where different: " + json.dumps(left.metadata) + " != " + json.dumps( + right.metadata))) + if len(left.traces) != len(right.traces): + mismatches.add((test_name, "trace count are different: " + str(len(left.traces)) + + " != " + str(len(right.traces)))) + else: + self.check_traces(test_name, left, right, mismatches) + + for mismatch in mismatches: + print(mismatch[0]) + print(mismatch[1]) + + print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)") + + def check_traces(self, test_name, left, right, mismatches): + for trace_id in range(0, len(left.traces)): + left_trace = left.traces[trace_id] + right_trace = right.traces[trace_id] + assert (left_trace.kind == right_trace.kind) + if str(left_trace) != str(right_trace): + mismatch_info = " " + str(left_trace) + "\n" + mismatch_info += " " + str(right_trace) + "\n" + mismatch_info += " " + for ch in range(0, len(str(left_trace))): + if ch < len(str(left_trace)) and ch < len(str(right_trace)): + if str(left_trace)[ch] != str(right_trace)[ch]: + mismatch_info += "|" + else: + mismatch_info += " " + else: + mismatch_info += "|" + mismatch_info += "\n" + mismatches.add((test_name, mismatch_info)) + + +def main(argv): + extracted_tests_trace_file = None + end_to_end_trace_file = None + try: + opts, args = getopt.getopt(argv, "s:e:") + except getopt.GetoptError: + print("verify-testcases.py [-s ] [-e ]") + sys.exit(2) + + for opt, arg in opts: + if opt in '-s': + extracted_tests_trace_file = arg + elif opt in '-e': + end_to_end_trace_file = arg + + base_path = os.path.dirname(__file__) + if not extracted_tests_trace_file: + extracted_tests_trace_file = base_path + "/extracted-tests.trace" + if not end_to_end_trace_file: + end_to_end_trace_file = base_path + "/endToEndExtraction-tests.trace" + + for f in [extracted_tests_trace_file, end_to_end_trace_file]: + if not os.path.isfile(f): + print("trace file '" + f + "' not found. aborting.") + sys.exit(1) + + if not os.path.isfile(extracted_tests_trace_file): + print("semantic trace file '" + extracted_tests_trace_file + "' not found. aborting.") + sys.exit(1) + + semantic_trace = TraceAnalyser(extracted_tests_trace_file) + end_to_end_trace = TraceAnalyser(end_to_end_trace_file) + + semantic_trace.diff(end_to_end_trace) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh new file mode 100755 index 000000000000..97cbe4ab415d --- /dev/null +++ b/scripts/test_antlr_grammar.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +set -e + +ROOT_DIR="$(dirname "$0")"/.. +WORKDIR="${ROOT_DIR}/build/antlr" +ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar" +GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")" + +SGR_RESET="\033[0m" +SGR_BOLD="\033[1m" +SGR_GREEN="\033[32m" +SGR_RED="\033[31m" +SGR_BLUE="\033[34m" + +vt_cursor_up() { echo -ne "\033[A"; } +vt_cursor_begin_of_line() { echo -ne "\r"; } + +download_antlr4() +{ + if [[ ! -e "$ANTLR_JAR" ]] + then + curl -o "${ANTLR_JAR}" "${ANTLR_JAR_URI}" + fi +} + +prepare_workdir() +{ + mkdir -p "${ROOT_DIR}/build/deps" + mkdir -p "${WORKDIR}" + mkdir -p "${WORKDIR}/src" + mkdir -p "${WORKDIR}/target" +} + +prepare_workdir +download_antlr4 + +if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ + [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] +then + echo "Creating parser" + # Create lexer/parser from grammar + java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + + # Compile lexer/parser sources + javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" +fi + +# Run tests +failed_count=0 +test_file() +{ + local SOL_FILE + SOL_FILE="$(readlink -m "${1}")" + local cur=${2} + local max=${3} + + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." + local output + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + vt_cursor_up + vt_cursor_begin_of_line + if [[ "${output}" == "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi +} + +# we only want to use files that do not contain errors or multi-source files. +SOL_FILES=() +while IFS='' read -r line +do + SOL_FILES+=("$line") +done < <( + grep -riL -E \ + "^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \ + "${ROOT_DIR}/test/libsolidity/syntaxTests" \ + "${ROOT_DIR}/test/libsolidity/semanticTests" \ +) + +test_count=0 +for SOL_FILE in "${SOL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} +done + +echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +exit ${failed_count} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e91f6446cc87..90c2931c56ab 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -147,6 +148,7 @@ static string const g_strOptimizeYul = "optimize-yul"; static string const g_strOutputDir = "output-dir"; static string const g_strOverwrite = "overwrite"; static string const g_strRevertStrings = "revert-strings"; +static string const g_strStorageLayout = "storage-layout"; /// Possible arguments to for --revert-strings static set const g_revertStringsArgs @@ -207,6 +209,7 @@ static string const g_argOptimizeRuns = g_strOptimizeRuns; static string const g_argOutputDir = g_strOutputDir; static string const g_argSignatureHashes = g_strSignatureHashes; static string const g_argStandardJSON = g_strStandardJSON; +static string const g_argStorageLayout = g_strStorageLayout; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; @@ -231,7 +234,8 @@ static set const g_combinedJsonArgs g_strOpcodes, g_strSignatureHashes, g_strSrcMap, - g_strSrcMapRuntime + g_strSrcMapRuntime, + g_strStorageLayout }; /// Possible arguments to for --machine @@ -293,7 +297,8 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) g_argNatspecUser, g_argNatspecDev, g_argOpcodes, - g_argSignatureHashes + g_argSignatureHashes, + g_argStorageLayout }) if (_args.count(arg)) return true; @@ -433,6 +438,18 @@ void CommandLineInterface::handleABI(string const& _contract) sout() << "Contract JSON ABI" << endl << data << endl; } +void CommandLineInterface::handleStorageLayout(string const& _contract) +{ + if (!m_args.count(g_argStorageLayout)) + return; + + string data = jsonCompactPrint(m_compiler->storageLayout(_contract)); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); + else + sout() << "Contract Storage Layout:" << endl << data << endl; +} + void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { std::string argName; @@ -833,7 +850,8 @@ Allowed options)", (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain."); + (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables."); desc.add(outputComponents); po::options_description allOptions = desc; @@ -1276,6 +1294,8 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); + if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + contractData[g_strStorageLayout] = jsonCompactPrint(m_compiler->storageLayout(contractName)); if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); @@ -1653,6 +1673,7 @@ void CommandLineInterface::outputCompilationResults() handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); + handleStorageLayout(contract); handleNatspec(true, contract); handleNatspec(false, contract); } // end of contracts iteration diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 417501907b1d..f6972cf04823 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -74,6 +74,7 @@ class CommandLineInterface void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); + void handleStorageLayout(std::string const& _contract); /// Fills @a m_sourceCodes initially and @a m_redirects. bool readInputFilesAndConfigureRemappings(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3ea080686771..d63d9aec5f81 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,8 @@ set(sources Metadata.h TestCase.cpp TestCase.h + TestCaseReader.cpp + TestCaseReader.h ) detect_stray_source_files("${sources}" ".") @@ -139,25 +141,37 @@ set(libyul_sources detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources - yulPhaser/Common.h + yulPhaser/TestHelpers.h + yulPhaser/TestHelpers.cpp + yulPhaser/TestHelpersTest.cpp yulPhaser/Common.cpp - yulPhaser/CommonTest.cpp yulPhaser/Chromosome.cpp yulPhaser/FitnessMetrics.cpp + yulPhaser/AlgorithmRunner.cpp yulPhaser/GeneticAlgorithms.cpp + yulPhaser/Mutations.cpp + yulPhaser/PairSelections.cpp + yulPhaser/Phaser.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp + yulPhaser/ProgramCache.cpp yulPhaser/Selections.cpp yulPhaser/SimulationRNG.cpp # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces # unnecessary duplication. Create a library or find a way to reuse the list in both places. + ../tools/yulPhaser/AlgorithmRunner.cpp + ../tools/yulPhaser/Common.cpp ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/FitnessMetrics.cpp ../tools/yulPhaser/GeneticAlgorithms.cpp + ../tools/yulPhaser/Mutations.cpp + ../tools/yulPhaser/PairSelections.cpp + ../tools/yulPhaser/Phaser.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp + ../tools/yulPhaser/ProgramCache.cpp ../tools/yulPhaser/Selections.cpp ../tools/yulPhaser/SimulationRNG.cpp ) diff --git a/test/Common.cpp b/test/Common.cpp index 709840bbcef9..0ed7c9881854 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -96,7 +96,8 @@ CommonOptions::CommonOptions(std::string _caption): ("optimize", po::bool_switch(&optimize), "enables optimization") ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") - ("show-messages", po::bool_switch(&showMessages), "enables message output"); + ("show-messages", po::bool_switch(&showMessages), "enables message output") + ("show-metadata", po::bool_switch(&showMetadata), "enables metadata output"); } void CommonOptions::validate() const diff --git a/test/Common.h b/test/Common.h index 63437da13d9f..a65c95539328 100644 --- a/test/Common.h +++ b/test/Common.h @@ -50,6 +50,7 @@ struct CommonOptions: boost::noncopyable bool disableSMT = false; bool useABIEncoderV2 = false; bool showMessages = false; + bool showMetadata = false; langutil::EVMVersion evmVersion() const; diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp index 6d72ce010ff3..e3edc3fb0e0d 100644 --- a/test/CommonSyntaxTest.cpp +++ b/test/CommonSyntaxTest.cpp @@ -56,37 +56,37 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end) } -CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) +CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): + EVMVersionRestrictedTestCase(_filename), + m_evmVersion(_evmVersion) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_sources = parseSourcesAndSettings(file); - - m_expectations = parseExpectations(file); + m_sources = m_reader.sources(); + m_expectations = parseExpectations(m_reader.stream()); } TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { parseAndAnalyze(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } -bool CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted) { - if (m_expectations != m_errorList) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); - return false; - } - return true; + if (m_expectations == m_errorList) + return TestResult::Success; + + printExpectationAndError(_stream, _linePrefix, _formatted); + return TestResult::Failure; +} + +void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +{ + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); } void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const diff --git a/test/CommonSyntaxTest.h b/test/CommonSyntaxTest.h index 22acd5413a14..e761e1487e82 100644 --- a/test/CommonSyntaxTest.h +++ b/test/CommonSyntaxTest.h @@ -73,7 +73,8 @@ class CommonSyntaxTest: public frontend::test::EVMVersionRestrictedTestCase bool _formatted = false ); - virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); + TestResult conclude(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); + void printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); static std::vector parseExpectations(std::istream& _stream); diff --git a/test/TestCase.cpp b/test/TestCase.cpp index 6b0a16e27cd3..f952e40bdaa4 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -18,16 +18,9 @@ #include #include -#include - -#include -#include #include -#include -#include #include - #include using namespace std; @@ -35,13 +28,14 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::test; -void TestCase::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool) +void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const bool) { - if (m_validatedSettings.empty()) + auto& settings = m_reader.settings(); + if (settings.empty()) return; _stream << _linePrefix << "// ====" << endl; - for (auto const& setting: m_validatedSettings) + for (auto const& setting: settings) _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; } @@ -53,108 +47,12 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename) !boost::starts_with(_filename.string(), "."); } -void TestCase::validateSettings() -{ - if (!m_settings.empty()) - throw runtime_error( - "Unknown setting(s): " + - util::joinHumanReadable(m_settings | boost::adaptors::map_keys) - ); -} - bool TestCase::shouldRun() { + m_reader.ensureAllSettingsRead(); return m_shouldRun; } -pair, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream) -{ - map sources; - string currentSourceName; - string currentSource; - string line; - size_t lineNumber = 1; - static string const sourceDelimiterStart("==== Source:"); - static string const sourceDelimiterEnd("===="); - static string const comment("// "); - static string const settingsDelimiter("// ===="); - static string const delimiter("// ----"); - bool sourcePart = true; - while (getline(_stream, line)) - { - lineNumber++; - - if (boost::algorithm::starts_with(line, delimiter)) - break; - else if (boost::algorithm::starts_with(line, settingsDelimiter)) - sourcePart = false; - else if (sourcePart) - { - if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) - { - if (!(currentSourceName.empty() && currentSource.empty())) - sources[currentSourceName] = std::move(currentSource); - currentSource = {}; - currentSourceName = boost::trim_copy(line.substr( - sourceDelimiterStart.size(), - line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() - )); - if (sources.count(currentSourceName)) - throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."); - } - else - currentSource += line + "\n"; - } - else if (boost::algorithm::starts_with(line, comment)) - { - size_t colon = line.find(':'); - if (colon == string::npos) - throw runtime_error(string("Expected \":\" inside setting.")); - string key = line.substr(comment.size(), colon - comment.size()); - string value = line.substr(colon + 1); - boost::algorithm::trim(key); - boost::algorithm::trim(value); - m_settings[key] = value; - } - else - throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); - } - sources[currentSourceName] = currentSource; - return {sources, lineNumber}; -} - -map TestCase::parseSourcesAndSettings(istream& _stream) -{ - return get<0>(parseSourcesAndSettingsWithLineNumbers(_stream)); -} - -pair TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream) -{ - auto [sourceMap, lineOffset] = parseSourcesAndSettingsWithLineNumbers(_stream); - if (sourceMap.size() != 1) - BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); - return {std::move(sourceMap.begin()->second), lineOffset}; -} - -string TestCase::parseSourceAndSettings(istream& _stream) -{ - return parseSourceAndSettingsWithLineNumbers(_stream).first; -} - -string TestCase::parseSimpleExpectations(std::istream& _file) -{ - string result; - string line; - while (getline(_file, line)) - if (boost::algorithm::starts_with(line, "// ")) - result += line.substr(3) + "\n"; - else if (line == "//") - result += "\n"; - else - BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \".")); - return result; -} - void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c) { if (_it == _end || *_it != _c) @@ -162,18 +60,11 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu ++_it; } -void EVMVersionRestrictedTestCase::validateSettings() +EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename): + TestCase(_filename) { - if (!m_settings.count("EVMVersion")) - return; - - string versionString = m_settings["EVMVersion"]; - m_validatedSettings["EVMVersion"] = versionString; - m_settings.erase("EVMVersion"); - - TestCase::validateSettings(); - - if (versionString.empty()) + string versionString = m_reader.stringSetting("EVMVersion", "any"); + if (versionString == "any") return; string comparator; diff --git a/test/TestCase.h b/test/TestCase.h index d6afff8b8262..0add6947b6b4 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -17,16 +17,13 @@ #pragma once +#include + #include #include -#include -#include -#include #include -#include -#include namespace solidity::frontend::test { @@ -60,31 +57,27 @@ class TestCase /// If @arg _formatted is true, color-coding may be used to indicate /// error locations in the contract, if applicable. virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0; - /// Outputs the updated settings. - virtual void printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); + /// Outputs settings. + virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); /// Outputs test expectations to @arg _stream that match the actual results of the test. /// Each line of output is prefixed with @arg _linePrefix. virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0; static bool isTestFilename(boost::filesystem::path const& _filename); - /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. - /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). - virtual void validateSettings(); - /// Returns true, if the test case is supported in the current environment and false /// otherwise which causes this test to be skipped. /// This might check e.g. for restrictions on the EVM version. + /// The function throws an exception if there are unread settings. bool shouldRun(); protected: - std::pair, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file); - std::map parseSourcesAndSettings(std::istream& _file); - std::pair parseSourceAndSettingsWithLineNumbers(std::istream& _file); - std::string parseSourceAndSettings(std::istream& _file); - static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); + // Used by ASTJSONTest, the only TestCase class with a custom parser of the test files. + TestCase() = default; + + TestCase(std::string const& _filename): m_reader(_filename) {} - static std::string parseSimpleExpectations(std::istream& _file); + static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); template static void skipWhitespace(IteratorType& _it, IteratorType _end) @@ -100,18 +93,14 @@ class TestCase ++_it; } - /// Parsed settings. - std::map m_settings; - /// Updated settings after validation. - std::map m_validatedSettings; - + TestCaseReader m_reader; bool m_shouldRun = true; }; class EVMVersionRestrictedTestCase: public TestCase { -public: - void validateSettings() override; +protected: + EVMVersionRestrictedTestCase(std::string const& _filename); }; } diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp new file mode 100644 index 000000000000..0f49a640181b --- /dev/null +++ b/test/TestCaseReader.cpp @@ -0,0 +1,164 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::frontend::test; + +TestCaseReader::TestCaseReader(string const& _filename): + m_file(_filename) +{ + if (!m_file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\".")); + m_file.exceptions(ios::badbit); + + tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file); + m_unreadSettings = m_settings; +} + +string const& TestCaseReader::source() +{ + if (m_sources.size() != 1) + BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); + return m_sources.begin()->second; +} + +string TestCaseReader::simpleExpectations() +{ + return parseSimpleExpectations(m_file); +} + +bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + string value = m_settings.at(_name); + if (value == "false") + return false; + if (value == "true") + return true; + + BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + ".")); +} + +size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + + static_assert(sizeof(unsigned long) <= sizeof(size_t)); + return stoul(m_settings.at(_name)); +} + +string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + return m_settings.at(_name); +} + +void TestCaseReader::ensureAllSettingsRead() const +{ + if (!m_unreadSettings.empty()) + throw runtime_error( + "Unknown setting(s): " + + util::joinHumanReadable(m_unreadSettings | boost::adaptors::map_keys) + ); +} + +pair, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) +{ + map sources; + string currentSourceName; + string currentSource; + string line; + size_t lineNumber = 1; + static string const sourceDelimiterStart("==== Source:"); + static string const sourceDelimiterEnd("===="); + static string const comment("// "); + static string const settingsDelimiter("// ===="); + static string const delimiter("// ----"); + bool sourcePart = true; + while (getline(_stream, line)) + { + lineNumber++; + + if (boost::algorithm::starts_with(line, delimiter)) + break; + else if (boost::algorithm::starts_with(line, settingsDelimiter)) + sourcePart = false; + else if (sourcePart) + { + if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) + { + if (!(currentSourceName.empty() && currentSource.empty())) + sources[currentSourceName] = std::move(currentSource); + currentSource = {}; + currentSourceName = boost::trim_copy(line.substr( + sourceDelimiterStart.size(), + line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() + )); + if (sources.count(currentSourceName)) + throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."); + } + else + currentSource += line + "\n"; + } + else if (boost::algorithm::starts_with(line, comment)) + { + size_t colon = line.find(':'); + if (colon == string::npos) + throw runtime_error(string("Expected \":\" inside setting.")); + string key = line.substr(comment.size(), colon - comment.size()); + string value = line.substr(colon + 1); + boost::algorithm::trim(key); + boost::algorithm::trim(value); + m_settings[key] = value; + } + else + throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); + } + sources[currentSourceName] = currentSource; + return { sources, lineNumber }; +} + +string TestCaseReader::parseSimpleExpectations(istream& _file) +{ + string result; + string line; + while (getline(_file, line)) + if (boost::algorithm::starts_with(line, "// ")) + result += line.substr(3) + "\n"; + else if (line == "//") + result += "\n"; + else + BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \".")); + return result; +} diff --git a/test/TestCaseReader.h b/test/TestCaseReader.h new file mode 100644 index 000000000000..5ab2268261d0 --- /dev/null +++ b/test/TestCaseReader.h @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include + +#pragma once + +namespace solidity::frontend::test +{ +/** + * A reader for test case data file, which parses source, settings and (optionally) simple expectations. + */ +class TestCaseReader +{ +public: + TestCaseReader() = default; + explicit TestCaseReader(std::string const& _filename); + + std::map const& sources() { return m_sources; } + std::string const& source(); + std::size_t lineNumber() { return m_lineNumber; } + std::map const& settings() { return m_settings; } + std::ifstream& stream() { return m_file; } + + std::string simpleExpectations(); + + bool boolSetting(std::string const& _name, bool _defaultValue); + size_t sizetSetting(std::string const& _name, size_t _defaultValue); + std::string stringSetting(std::string const& _name, std::string const& _defaultValue); + + void ensureAllSettingsRead() const; + +private: + std::pair, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file); + static std::string parseSimpleExpectations(std::istream& _file); + + std::ifstream m_file; + std::map m_sources; + std::size_t m_lineNumber = 0; + std::map m_settings; + std::map m_unreadSettings; ///< tracks which settings are left unread +}; +} diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 3137a50854f5..fd7bca132372 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -94,7 +94,6 @@ int registerTests( { stringstream errorStream; auto testCase = _testCaseCreator(config); - testCase->validateSettings(); if (testCase->shouldRun()) switch (testCase->run(errorStream)) { @@ -188,7 +187,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) if (solidity::test::CommonOptions::get().disableSMT) removeTestSuite("SMTChecker"); - return 0; + return nullptr; } // BOOST_TEST_DYN_LINK should be defined if user want to link against shared boost test library diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 393f3da8bafc..9001c02f4817 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -33,6 +33,7 @@ set -e REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} source "${REPO_ROOT}/scripts/common.sh" +source "${REPO_ROOT}/scripts/common_cmdline.sh" case "$OSTYPE" in msys) @@ -45,6 +46,7 @@ case "$OSTYPE" in SOLC="$REPO_ROOT/${SOLIDITY_BUILD_DIR}/solc/solc" ;; esac +echo "${SOLC}" INTERACTIVE=true if ! tty -s || [ "$CI" ] @@ -52,8 +54,6 @@ then INTERACTIVE="" fi -FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" - # extend stack size in case we run via ASAN if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]]; then ulimit -s 16384 @@ -62,52 +62,6 @@ fi ## FUNCTIONS -function compileFull() -{ - local expected_exit_code=0 - local expect_output=0 - if [[ $1 = '-e' ]] - then - expected_exit_code=1 - expect_output=1 - shift; - fi - if [[ $1 = '-w' ]] - then - expect_output=1 - shift; - fi - - local files="$*" - local output - - local stderr_path=$(mktemp) - - set +e - "$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path" - local exit_code=$? - local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") - set -e - rm "$stderr_path" - - if [[ \ - "$exit_code" -ne "$expected_exit_code" || \ - ( $expect_output -eq 0 && -n "$errors" ) || \ - ( $expect_output -ne 0 && -z "$errors" ) \ - ]] - then - printError "Unexpected compilation result:" - printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" - printError "Was failure: $exit_code" - echo "$errors" - printError "While calling:" - echo "\"$SOLC\" $FULLARGS $files" - printError "Inside directory:" - pwd - false - fi -} - function ask_expectation_update() { if [ $INTERACTIVE ] @@ -350,6 +304,10 @@ SOLTMPDIR=$(mktemp -d) then opts="$opts -w" fi + if grep "This may report a warning" "$f" >/dev/null + then + opts="$opts -o" + fi compileFull $opts "$SOLTMPDIR/$f" done ) diff --git a/test/cmdlineTests/standard_immutable_references/input.json b/test/cmdlineTests/standard_immutable_references/input.json new file mode 100644 index 000000000000..15213be74eb1 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "contract A { uint256 immutable x = 1; function f() public view returns (uint256) { return x; } }" + } + }, + "settings": { + "evmVersion": "petersburg", + "outputSelection": { + "*": { + "A": [ + "evm.deployedBytecode.immutableReferences" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_immutable_references/output.json b/test/cmdlineTests/standard_immutable_references/output.json new file mode 100644 index 000000000000..2788a8e732f8 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/output.json @@ -0,0 +1,2 @@ +{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[{"length":32,"start":77}]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"0:96:0:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;0:96:0;;;;;;;;;;;;;;;;12:1:-1;9;2:12;38:56:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;72:7;90:1;83:8;;38:56;:::o"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 82e974025a54..cb89a6911d0b 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -20,6 +20,7 @@ object \"C_6\" { function fun_f_5() { + } } @@ -69,6 +70,7 @@ object \"C_6\" { function fun_f_5() { + } function shift_right_224_unsigned(value) -> newValue { diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index fc88acf50039..24a2a10aa056 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -35,11 +35,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -131,6 +138,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave @@ -147,6 +157,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 428c7911eff9..8028e5dd6e08 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -23,11 +23,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -88,6 +95,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave @@ -100,6 +110,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 4a348343b510..a9588d145575 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 4302701bdcd3..552712154dcc 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -39,11 +39,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -139,6 +146,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave @@ -155,6 +165,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 0b92fceea4ea..48c1146faeac 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 2d7057f7c050..a402101c4ee2 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -61,6 +61,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) Assembly _subAsm; auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); + // PushImmutable + _subAsm.appendImmutable("someImmutable"); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); @@ -86,6 +88,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) _assembly.pushSubroutineOffset(size_t(sub.data())); // PushDeployTimeAddress _assembly.append(PushDeployTimeAddress); + // AssignImmutable. + // Note that since there is no reference to "someOtherImmutable", this will compile to a simple POP in the hex output. + _assembly.appendImmutableAssignment("someOtherImmutable"); + _assembly.append(u256(2)); + _assembly.appendImmutableAssignment("someImmutable"); // Operation _assembly.append(Instruction::STOP); _assembly.appendAuxiliaryDataToEnd(bytes{0x42, 0x66}); @@ -95,8 +102,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), - "5b6001600220604673__$bf005014d9d0f534b8fcb268bd84c491a2$__" - "600056603e6001603d73000000000000000000000000000000000000000000fe" + "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" + "60005660676022604573000000000000000000000000000000000000000050" + "60028060015250" + "00fe" + "7f0000000000000000000000000000000000000000000000000000000000000000" "fe010203044266eeaa" ); BOOST_CHECK_EQUAL( @@ -111,12 +121,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) " dataSize(sub_0)\n" " dataOffset(sub_0)\n" " deployTimeAddress()\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " 0x02\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " stop\n" "stop\n" "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n" "\n" "sub_0: assembly {\n" " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " invalid\n" "}\n" "\n" @@ -138,9 +152,104 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}" - "],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]}," - "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}" + "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + ); +} + +BOOST_AUTO_TEST_CASE(immutable) +{ + map indices = { + { "root.asm", 0 }, + { "sub.asm", 1 } + }; + Assembly _assembly; + auto root_asm = make_shared("lorem ipsum", "root.asm"); + _assembly.setSourceLocation({1, 3, root_asm}); + + Assembly _subAsm; + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + _subAsm.setSourceLocation({6, 8, sub_asm}); + _subAsm.appendImmutable("someImmutable"); + _subAsm.appendImmutable("someOtherImmutable"); + _subAsm.appendImmutable("someImmutable"); + shared_ptr _subAsmPtr = make_shared(_subAsm); + + _assembly.append(u256(42)); + _assembly.appendImmutableAssignment("someImmutable"); + _assembly.append(u256(23)); + _assembly.appendImmutableAssignment("someOtherImmutable"); + + auto sub = _assembly.appendSubroutine(_subAsmPtr); + _assembly.pushSubroutineOffset(size_t(sub.data())); + + checkCompilation(_assembly); + + BOOST_CHECK_EQUAL( + _assembly.assemble().toHex(), + // root.asm + // assign "someImmutable" + "602a" // PUSH1 42 - value for someImmutable + "80" // DUP1 + "6001" // PUSH1 1 - offset of first someImmutable in sub_0 + "52" // MSTORE + "80" // DUP1 + "6043" // PUSH1 67 - offset of second someImmutable in sub_0 + "52" // MSTORE + "50" // POP + // assign "someOtherImmutable" + "6017" // PUSH1 23 - value for someOtherImmutable + "80" // DUP1 + "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0 + "52" // MSTORE + "50" // POP + "6063" // PUSH1 0x63 - dataSize(sub_0) + "6017" // PUSH1 0x17 - dataOffset(sub_0) + "fe" // INVALID + // end of root.asm + // sub.asm + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 1 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someOtherImmutable - data at offset 34 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 67 + ); + BOOST_CHECK_EQUAL( + _assembly.assemblyString(), + " /* \"root.asm\":1:3 */\n" + " 0x2a\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " 0x17\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " dataSize(sub_0)\n" + " dataOffset(sub_0)\n" + "stop\n" + "\n" + "sub_0: assembly {\n" + " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " immutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + "}\n" + ); + BOOST_CHECK_EQUAL( + util::jsonCompactPrint(_assembly.assemblyJSON(indices)), + "{\".code\":[" + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}" + "]}}}" ); } diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 4d93f4e76914..782b687fc0d3 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -112,6 +112,44 @@ namespace BOOST_AUTO_TEST_SUITE(Optimiser) +BOOST_AUTO_TEST_CASE(cse_push_immutable_same) +{ + AssemblyItem pushImmutable{PushImmutable, 0x1234}; + checkCSE({pushImmutable, pushImmutable}, {pushImmutable, Instruction::DUP1}); +} + +BOOST_AUTO_TEST_CASE(cse_push_immutable_different) +{ + AssemblyItems input{{PushImmutable, 0x1234},{PushImmutable, 0xABCD}}; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_assign_immutable) +{ + { + AssemblyItems input{u256(0x42), {AssignImmutable, 0x1234}}; + checkCSE(input, input); + } + { + AssemblyItems input{{AssignImmutable, 0x1234}}; + checkCSE(input, input); + } +} + + +BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks) +{ + AssemblyItems input = addDummyLocations(AssemblyItems{ + u256(0x42), + {AssignImmutable, 0x1234}, + Instruction::ORIGIN + }); + + evmasm::CommonSubexpressionEliminator cse{evmasm::KnownState()}; + // Make sure CSE breaks after AssignImmutable. + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { evmasm::KnownState state; @@ -798,6 +836,68 @@ BOOST_AUTO_TEST_CASE(block_deduplicator) BOOST_CHECK_EQUAL(pushTags.size(), 2); } +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same) +{ + AssemblyItems blocks{ + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + + AssemblyItems input = AssemblyItems{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + } + blocks; + AssemblyItems output = AssemblyItems{ + AssemblyItem(PushTag, 1), + AssemblyItem(PushTag, 1), + } + blocks; + BlockDeduplicator dedup(input); + dedup.deduplicate(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_value) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(23), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0xABCD}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + BOOST_AUTO_TEST_CASE(block_deduplicator_loops) { AssemblyItems input{ diff --git a/test/libsolidity/ABIJsonTest.cpp b/test/libsolidity/ABIJsonTest.cpp index 1da0193ab42d..e7a0cce6dd7c 100644 --- a/test/libsolidity/ABIJsonTest.cpp +++ b/test/libsolidity/ABIJsonTest.cpp @@ -36,15 +36,11 @@ using namespace solidity::util; using namespace solidity::frontend; using namespace solidity::frontend::test; -ABIJsonTest::ABIJsonTest(string const& _filename) +ABIJsonTest::ABIJsonTest(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index 0aa3d298440a..501184c9c4d1 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -54,7 +54,7 @@ "storageLocation": "memory", "typeDescriptions": { - "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr", + "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr", "typeString": "uint256[][]" }, "typeName": diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index b1b0d56f23f7..694839b99ef3 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -36,35 +36,14 @@ using namespace std; namespace fs = boost::filesystem; using namespace boost::unit_test; -GasTest::GasTest(string const& _filename) +GasTest::GasTest(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - - if (m_settings.count("optimize")) - { - m_optimise = true; - m_validatedSettings["optimize"] = "true"; - m_settings.erase("optimize"); - } - if (m_settings.count("optimize-yul")) - { - m_optimiseYul = true; - m_validatedSettings["optimize-yul"] = "true"; - m_settings.erase("optimize-yul"); - } - if (m_settings.count("optimize-runs")) - { - m_optimiseRuns = stoul(m_settings["optimize-runs"]); - m_validatedSettings["optimize-runs"] = m_settings["optimize-runs"]; - m_settings.erase("optimize-runs"); - } - - parseExpectations(file); + m_source = m_reader.source(); + m_optimise = m_reader.boolSetting("optimize", false); + m_optimiseYul = m_reader.boolSetting("optimize-yul", false); + m_optimiseRuns = m_reader.sizetSetting("optimize-runs", 200); + parseExpectations(m_reader.stream()); } void GasTest::parseExpectations(std::istream& _stream) diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index 82c2b446b168..a1c23662ad0c 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -17,14 +17,18 @@ #include #include + +#include #include #include #include + #include #include #include #include #include + #include #include #include @@ -50,6 +54,9 @@ SMTCheckerJSONTest::SMTCheckerJSONTest(string const& _filename, langutil::EVMVer !m_smtResponses.isObject() ) BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); + + if (ModelChecker::availableSolvers().none()) + m_shouldRun = false; } TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) @@ -128,7 +135,7 @@ TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _li } } - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } vector SMTCheckerJSONTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib) diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 912349182784..28c98ffab3a6 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -28,22 +28,17 @@ using namespace solidity::frontend::test; SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion) { - if (m_settings.count("SMTSolvers")) - { - auto const& choice = m_settings.at("SMTSolvers"); - if (choice == "any") - m_enabledSolvers = smt::SMTSolverChoice::All(); - else if (choice == "z3") - m_enabledSolvers = smt::SMTSolverChoice::Z3(); - else if (choice == "cvc4") - m_enabledSolvers = smt::SMTSolverChoice::CVC4(); - else if (choice == "none") - m_enabledSolvers = smt::SMTSolverChoice::None(); - else - BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); - } - else + auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); + if (choice == "any") m_enabledSolvers = smt::SMTSolverChoice::All(); + else if (choice == "z3") + m_enabledSolvers = smt::SMTSolverChoice::Z3(); + else if (choice == "cvc4") + m_enabledSolvers = smt::SMTSolverChoice::CVC4(); + else if (choice == "none") + m_enabledSolvers = smt::SMTSolverChoice::None(); + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); auto available = ModelChecker::availableSolvers(); if (!available.z3) @@ -62,5 +57,5 @@ TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePr parseAndAnalyze(); filterObtainedErrors(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 97c83a3e5eee..ded01f8dd2a3 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -37,59 +37,42 @@ namespace fs = boost::filesystem; SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion): - SolidityExecutionFramework(_evmVersion) + SolidityExecutionFramework(_evmVersion), + EVMVersionRestrictedTestCase(_filename) { - ifstream file(_filename); - soltestAssert(file, "Cannot open test contract: \"" + _filename + "\"."); - file.exceptions(ios::badbit); + m_source = m_reader.source(); + m_lineOffset = m_reader.lineNumber(); - std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file); - - if (m_settings.count("compileViaYul")) + string choice = m_reader.stringSetting("compileViaYul", "false"); + if (choice == "also") { - if (m_settings["compileViaYul"] == "also") - { - m_validatedSettings["compileViaYul"] = m_settings["compileViaYul"]; - m_runWithYul = true; - m_runWithoutYul = true; - } - else - { - m_validatedSettings["compileViaYul"] = "only"; - m_runWithYul = true; - m_runWithoutYul = false; - } - m_settings.erase("compileViaYul"); + m_runWithYul = true; + m_runWithoutYul = true; } - if (m_settings.count("ABIEncoderV1Only")) + else if (choice == "true") { - if (m_settings["ABIEncoderV1Only"] == "true") - { - m_validatedSettings["ABIEncoderV1Only"] = "true"; - m_runWithABIEncoderV1Only = true; - } - m_settings.erase("ABIEncoderV1Only"); + m_runWithYul = true; + m_runWithoutYul = false; } + else if (choice == "false") + { + m_runWithYul = false; + m_runWithoutYul = true; + } + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); + m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) m_shouldRun = false; - if (m_settings.count("revertStrings")) - { - auto revertStrings = revertStringsFromString(m_settings["revertStrings"]); - if (revertStrings) - m_revertStrings = *revertStrings; - m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings); - m_settings.erase("revertStrings"); - } + auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default")); + soltestAssert(revertStrings, "Invalid revertStrings setting."); + m_revertStrings = revertStrings.value(); - if (m_settings.count("allowNonExistingFunctions")) - { - m_validatedSettings["allowNonExistingFunctions"] = true; - m_settings.erase("allowNonExistingFunctions"); - } + m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false); - parseExpectations(file); + parseExpectations(m_reader.stream()); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } @@ -152,7 +135,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref else { soltestAssert( - m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), + m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), "The function " + test.call().signature + " is not known to the compiler" ); diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 0ea486cad327..94c29e193efd 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -65,6 +65,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict bool m_runWithYul = false; bool m_runWithoutYul = true; bool m_runWithABIEncoderV1Only = false; + bool m_allowNonExistingFunctions = false; }; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 2c3dfebace16..69e645494cec 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -154,7 +154,6 @@ BOOST_AUTO_TEST_CASE(while_loop) ) } - BOOST_AUTO_TEST_CASE(do_while_loop) { char const* sourceCode = R"( @@ -1003,22 +1002,6 @@ BOOST_AUTO_TEST_CASE(constructor) ) } -BOOST_AUTO_TEST_CASE(balance) -{ - char const* sourceCode = R"( - contract test { - constructor() public payable {} - function getBalance() public returns (uint256 balance) { - return address(this).balance; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 23); - ABI_CHECK(callContractFunction("getBalance()"), encodeArgs(23)); - ) -} - BOOST_AUTO_TEST_CASE(blockchain) { char const* sourceCode = R"( @@ -1041,39 +1024,6 @@ BOOST_AUTO_TEST_CASE(blockchain) ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); } -BOOST_AUTO_TEST_CASE(msg_sig) -{ - char const* sourceCode = R"( - contract test { - function foo(uint256 a) public returns (bytes4 value) { - return msg.sig; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - -BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) -{ - char const* sourceCode = R"( - contract test { - function boo() public returns (bytes4 value) { - return msg.sig; - } - function foo(uint256 a) public returns (bytes4 value) { - return boo(); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - BOOST_AUTO_TEST_CASE(now) { char const* sourceCode = R"( @@ -1660,54 +1610,6 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls) ABI_CHECK(callContractFunction("callHelper(bytes2,bool)", string("\0a", 2), true), encodeArgs(string("\0a\0\0\0", 5))); } -BOOST_AUTO_TEST_CASE(constructor_arguments_internal) -{ - char const* sourceCode = R"( - contract Helper { - bytes3 name; - bool flag; - - constructor(bytes3 x, bool f) public { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - contract Main { - Helper h; - constructor() public { - h = new Helper("abc", true); - } - function getFlag() public returns (bool ret) { return h.getFlag(); } - function getName() public returns (bytes3 ret) { return h.getName(); } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - ABI_CHECK(callContractFunction("getFlag()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("getName()"), encodeArgs("abc")); -} - -BOOST_AUTO_TEST_CASE(constructor_arguments_external) -{ - char const* sourceCode = R"( - contract Main { - bytes3 name; - bool flag; - - constructor(bytes3 x, bool f) public { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - )"; - compileAndRun(sourceCode, 0, "Main", encodeArgs("abc", true)); - ABI_CHECK(callContractFunction("getFlag()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("getName()"), encodeArgs("abc")); -} - BOOST_AUTO_TEST_CASE(constructor_with_long_arguments) { char const* sourceCode = R"( @@ -1736,63 +1638,6 @@ BOOST_AUTO_TEST_CASE(constructor_with_long_arguments) ABI_CHECK(callContractFunction("b()"), encodeDyn(b)); } -BOOST_AUTO_TEST_CASE(constructor_static_array_argument) -{ - char const* sourceCode = R"( - contract C { - uint public a; - uint[3] public b; - - constructor(uint _a, uint[3] memory _b) public { - a = _a; - b = _b; - } - } - )"; - compileAndRun(sourceCode, 0, "C", encodeArgs(u256(1), u256(2), u256(3), u256(4))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("b(uint256)", u256(0)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("b(uint256)", u256(1)), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("b(uint256)", u256(2)), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(constant_var_as_array_length) -{ - char const* sourceCode = R"( - contract C { - uint constant LEN = 3; - uint[LEN] public a; - - constructor(uint[LEN] memory _a) public { - a = _a; - } - } - )"; - compileAndRun(sourceCode, 0, "C", encodeArgs(u256(1), u256(2), u256(3))); - ABI_CHECK(callContractFunction("a(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("a(uint256)", u256(1)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("a(uint256)", u256(2)), encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(functions_called_by_constructor) -{ - char const* sourceCode = R"( - contract Test { - bytes3 name; - bool flag; - constructor() public { - setName("abc"); - } - function getName() public returns (bytes3 ret) { return name; } - function setName(bytes3 _name) private { name = _name; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); - ) -} - BOOST_AUTO_TEST_CASE(contracts_as_addresses) { char const* sourceCode = R"( @@ -1813,97 +1658,6 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); } -BOOST_AUTO_TEST_CASE(gas_and_value_basic) -{ - char const* sourceCode = R"( - contract helper { - bool flag; - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - function setFlag() public { flag = true; } - function getFlag() public returns (bool fl) { return flag; } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - return h.getBalance.value(amount)(); - } - function outOfGas() public returns (bool ret) { - h.setFlag.gas(2)(); // should fail due to OOG - return true; - } - function checkState() public returns (bool flagAfter, uint myBal) { - flagAfter = h.getFlag(); - myBal = address(this).balance; - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); - // call to helper should not succeed but amount should be transferred anyway - BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes()); - BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); -} - -BOOST_AUTO_TEST_CASE(gas_and_value_brace_syntax) -{ - char const* sourceCode = R"( - contract helper { - bool flag; - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - function setFlag() public { flag = true; } - function getFlag() public returns (bool fl) { return flag; } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - return h.getBalance{value: amount}(); - } - function outOfGas() public returns (bool ret) { - h.setFlag{gas: 2}(); // should fail due to OOG - return true; - } - function checkState() public returns (bool flagAfter, uint myBal) { - flagAfter = h.getFlag(); - myBal = address(this).balance; - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); - // call to helper should not succeed but amount should be transferred anyway - BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes()); - BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); -} - -BOOST_AUTO_TEST_CASE(gasleft_decrease) -{ - char const* sourceCode = R"( - contract C { - uint v; - function f() public returns (bool) { - uint startGas = gasleft(); - v++; - assert(startGas > gasleft()); - return true; - } - function g() public returns (bool) { - uint startGas = gasleft(); - assert(startGas > gasleft()); - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(gaslimit) { char const* sourceCode = R"( @@ -1966,12115 +1720,4842 @@ BOOST_AUTO_TEST_CASE(blockhash) ABI_CHECK(callContractFunction("f()"), encodeDyn(hashes)); } -BOOST_AUTO_TEST_CASE(value_complex) +BOOST_AUTO_TEST_CASE(internal_constructor) { char const* sourceCode = R"( - contract helper { - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - uint someStackElement = 20; - return h.getBalance.value(amount).gas(1000).value(amount + 3)(); - } + contract C { + constructor() internal {} } )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8)); + BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); } -BOOST_AUTO_TEST_CASE(value_insane) +BOOST_AUTO_TEST_CASE(default_fallback_throws) { - char const* sourceCode = R"( - contract helper { - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; + char const* sourceCode = R"YY( + contract A { + function f() public returns (bool) { + (bool success,) = address(this).call(""); + return success; } } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public returns (uint256 bal) { - return h.getBalance.value(amount).gas(1000).value(amount + 3)();// overwrite value + )YY"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract A { + function f() public returns (bool) { + (bool success, bytes memory data) = address(this).staticcall(""); + assert(data.length == 0); + return success; + } } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8)); + )YY"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + } } -BOOST_AUTO_TEST_CASE(value_for_constructor) +BOOST_AUTO_TEST_CASE(event) { char const* sourceCode = R"( - contract Helper { - bytes3 name; - bool flag; - constructor(bytes3 x, bool f) public payable { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - contract Main { - Helper h; - constructor() public payable { - h = (new Helper).value(10)("abc", true); + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id, bool _manually) public payable { + if (_manually) { + bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f; + log3(bytes32(msg.value), s, bytes32(uint256(msg.sender)), _id); + } else { + emit Deposit(msg.sender, _id, msg.value); + } } - function getFlag() public returns (bool ret) { return h.getFlag(); } - function getName() public returns (bytes3 ret) { return h.getName(); } - function getBalances() public returns (uint me, uint them) { me = address(this).balance; them = address(h).balance;} } )"; - compileAndRun(sourceCode, 22, "Main"); - BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); - BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10)); + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + for (bool manually: {true, false}) + { + callContractFunctionWithValue("deposit(bytes32,bool)", value, id, manually); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(value))); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(logTopic(0, 2), h256(id)); + } } -BOOST_AUTO_TEST_CASE(virtual_function_calls) +BOOST_AUTO_TEST_CASE(event_emit) { char const* sourceCode = R"( - contract Base { - function f() public returns (uint i) { return g(); } - function g() public virtual returns (uint i) { return 1; } - } - contract Derived is Base { - function g() public override returns (uint i) { return 2; } + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) public payable { + emit Deposit(msg.sender, _id, msg.value); + } } )"; ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(2)); + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(value))); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(logTopic(0, 2), h256(id)); ) } -BOOST_AUTO_TEST_CASE(access_base_storage) +BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( - contract Base { - uint dataBase; - function getViaBase() public returns (uint i) { return dataBase; } - } - contract Derived is Base { - uint dataDerived; - function setData(uint base, uint derived) public returns (bool r) { - dataBase = base; - dataDerived = derived; - return true; - } - function getViaDerived() public returns (uint base, uint derived) { - base = dataBase; - derived = dataDerived; + contract ClientReceipt { + event Deposit(); + function deposit() public { + emit Deposit(); } } )"; + ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("setData(uint256,uint256)", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("getViaBase()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("getViaDerived()"), encodeArgs(1, 2)); + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0).empty()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); ) } -BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) { char const* sourceCode = R"( - contract Base { - uint data; - function setData(uint i) public { data = i; } - function getViaBase() public returns (uint i) { return data; } + contract A { + event x(); } - contract A is Base { function setViaA(uint i) public { setData(i); } } - contract B is Base { function getViaB() public returns (uint i) { return getViaBase(); } } - contract Derived is Base, B, A { } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getViaB()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("setViaA(uint256)", 23), encodeArgs()); - ABI_CHECK(callContractFunction("getViaB()"), encodeArgs(23)); - ) + contract B is A { + function f() public returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0).empty()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("x()"))); } -BOOST_AUTO_TEST_CASE(explicit_base_class) +BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( - contract BaseBase { function g() public virtual returns (uint r) { return 1; } } - contract Base is BaseBase { function g() public virtual override returns (uint r) { return 2; } } - contract Derived is Base { - function f() public returns (uint r) { return BaseBase.g(); } - function g() public override returns (uint r) { return 3; } + contract ClientReceipt { + event Deposit(); + event Deposit(address _addr); + event Deposit(address _addr, uint _amount); + event Deposit(address _addr, bool _flag); + function deposit() public returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) public returns (uint) { + emit Deposit(_addr); + return 2; + } + function deposit(address _addr, uint _amount) public returns (uint) { + emit Deposit(_addr, _amount); + return 3; + } + function deposit(address _addr, bool _flag) public returns (uint) { + emit Deposit(_addr, _flag); + return 4; + } } )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + u160 const c_loggedAddress = m_contractAddress; + + ALSO_VIA_YUL( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0).empty()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(2))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + ABI_CHECK(logData(0), encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(3))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,uint256)"))); + + ABI_CHECK(callContractFunction("deposit(address,bool)", c_loggedAddress, false), encodeArgs(u256(4))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, false)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bool)"))); + ) } -BOOST_AUTO_TEST_CASE(base_constructor_arguments) +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) { char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; - } + contract A { + event Deposit(); } - contract Base is BaseBase(7) { - constructor() public { - m_a *= m_a; - } + + contract B { + event Deposit(address _addr); } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() public returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) public returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) public returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } } )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(7 * 7)); + u160 const c_loggedAddress = m_contractAddress; + + ALSO_VIA_YUL( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0).empty()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,uint256)"))); + ) } -BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments) +BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; + contract ClientReceipt { + event Deposit() anonymous; + function deposit() public { + emit Deposit(); } - function g() public returns (uint r) { return 2; } - } - contract Base is BaseBase(BaseBase.g()) { - } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } } )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(2)); + ALSO_VIA_YUL( + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 0); + ) } -BOOST_AUTO_TEST_CASE(virtual_function_usage_in_constructor_arguments) +BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) { char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous; + function deposit(bytes32 _id) public payable { + emit Deposit(msg.sender, _id, msg.value, 2, "abc"); } - function overridden() public virtual returns (uint r) { return 1; } - function g() public returns (uint r) { return overridden(); } - } - contract Base is BaseBase(BaseBase.g()) { - } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } - function overridden() public override returns (uint r) { return 2; } } )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(2)); + ALSO_VIA_YUL( + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs("abc")); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 4); + BOOST_CHECK_EQUAL(logTopic(0, 0), h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(logTopic(0, 1), h256(id)); + BOOST_CHECK_EQUAL(logTopic(0, 2), h256(value)); + BOOST_CHECK_EQUAL(logTopic(0, 3), h256(2)); + ) } -BOOST_AUTO_TEST_CASE(internal_constructor) +BOOST_AUTO_TEST_CASE(event_lots_of_data) { char const* sourceCode = R"( - contract C { - constructor() internal {} + contract ClientReceipt { + event Deposit(address _from, bytes32 _id, uint _value, bool _flag); + function deposit(bytes32 _id) public payable { + emit Deposit(msg.sender, _id, msg.value, true); + } } )"; - BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); + ALSO_VIA_YUL( + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs((u160)m_sender, id, value, true)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256,bool)"))); + ) } -BOOST_AUTO_TEST_CASE(function_modifier) +BOOST_AUTO_TEST_CASE(event_really_lots_of_data) { char const* sourceCode = R"( - contract C { - function getOne() payable nonFree public returns (uint r) { return 1; } - modifier nonFree { if (msg.value > 0) _; } + contract ClientReceipt { + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() public { + emit Deposit(10, msg.data, 15); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getOne()"), encodeArgs(0)); - ABI_CHECK(callContractFunctionWithValue("getOne()", 1), encodeArgs(1)); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK_EQUAL(toHex(logData(0)), toHex(encodeArgs(10, 0x60, 15, 4, asString(FixedHash<4>(util::keccak256("deposit()")).asBytes())))); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); } -BOOST_AUTO_TEST_CASE(function_modifier_local_variables) +BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) { char const* sourceCode = R"( - contract C { - modifier mod1 { uint8 a = 1; uint8 b = 2; _; } - modifier mod2(bool a) { if (a) return; else _; } - function f(bool a) mod1 mod2(a) public returns (uint r) { return 3; } + contract ClientReceipt { + bytes x; + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() public { + x.push("A"); + x.push("B"); + x.push("C"); + emit Deposit(10, x, 15); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(0)); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(3)); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK_EQUAL(toHex(logData(0)), toHex(encodeArgs(10, 0x60, 15, 3, string("ABC")))); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); } -BOOST_AUTO_TEST_CASE(function_modifier_loop) +BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) { char const* sourceCode = R"( - contract C { - modifier repeat(uint count) { uint i; for (i = 0; i < count; ++i) _; } - function f() repeat(10) public returns (uint r) { r += 1; } + contract ClientReceipt { + bytes x; + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() public { + x = new bytes(31); + x[0] = "A"; + x[1] = "B"; + x[2] = "C"; + x[30] = "Z"; + emit Deposit(10, x, 15); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(10)); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(10, 0x60, 15, 31, string("ABC") + string(27, 0) + "Z")); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); } -BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) +BOOST_AUTO_TEST_CASE(event_struct_memory_v2) { char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { - modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) public returns (uint r) { r += 1; } + struct S { uint a; } + event E(S); + function createEvent(uint x) public { + emit E(S(x)); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(2)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(x)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint256))"))); } -BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) +BOOST_AUTO_TEST_CASE(event_struct_storage_v2) { - // Note that return sets the return variable and jumps to the end of the current function or - // modifier code block. char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { - modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) public returns (uint r) { r += 1; return r; } + struct S { uint a; } + event E(S); + S s; + function createEvent(uint x) public { + s.a = x; + emit E(s); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(2)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(x)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint256))"))); } -BOOST_AUTO_TEST_CASE(function_modifier_overriding) +BOOST_AUTO_TEST_CASE(event_dynamic_array_memory) { char const* sourceCode = R"( - contract A { - function f() mod public returns (bool r) { return true; } - modifier mod virtual { _; } - } - contract C is A { - modifier mod override { if (false) _; } + contract C { + event E(uint[]); + function createEvent(uint x) public { + uint[] memory arr = new uint[](3); + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(false)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); } -BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context) +BOOST_AUTO_TEST_CASE(event_dynamic_array_memory_v2) { char const* sourceCode = R"( - contract A { - uint data; - constructor() mod1 public { f1(); } - function f1() mod2 public { data |= 0x1; } - function f2() public { data |= 0x20; } - function f3() public virtual { } - modifier mod1 virtual { f2(); _; } - modifier mod2 { f3(); if (false) _; } - function getData() public returns (uint r) { return data; } - } - contract C is A { - modifier mod1 override { f4(); _; } - function f3() public override { data |= 0x300; } - function f4() public { data |= 0x4000; } + pragma experimental ABIEncoderV2; + contract C { + event E(uint[]); + function createEvent(uint x) public { + uint[] memory arr = new uint[](3); + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(0x4300)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); } -BOOST_AUTO_TEST_CASE(function_modifier_for_constructor) +BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_memory_v2) { char const* sourceCode = R"( - contract A { - uint data; - constructor() mod1 public { data |= 2; } - modifier mod1 virtual { data |= 1; _; } - function getData() public returns (uint r) { return data; } - } - contract C is A { - modifier mod1 override { data |= 4; _; } + pragma experimental ABIEncoderV2; + contract C { + event E(uint[][]); + function createEvent(uint x) public { + uint[][] memory arr = new uint[][](2); + arr[0] = new uint[](2); + arr[1] = new uint[](2); + arr[0][0] = x; + arr[0][1] = x + 1; + arr[1][0] = x + 2; + arr[1][1] = x + 3; + emit E(arr); + } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(4 | 2)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[][])"))); } -BOOST_AUTO_TEST_CASE(function_modifier_multiple_times) +BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) { char const* sourceCode = R"( contract C { - uint public a; - modifier mod(uint x) { a += x; _; } - function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } + event E(uint[]); + uint[] arr; + function createEvent(uint x) public { + while (arr.length < 3) + arr.push(); + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(2 + 5 + 3)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(2 + 5 + 3)); + ALSO_VIA_YUL( + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); + ) } -BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars) +BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) { char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { - uint public a; - modifier mod(uint x) { uint b = x; a += b; _; a -= b; assert(b == x); } - function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } + event E(uint[]); + uint[] arr; + function createEvent(uint x) public { + while (arr.length < 3) + arr.push(); + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(2 + 5 + 3)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(0)); + ALSO_VIA_YUL( + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); + ) } -BOOST_AUTO_TEST_CASE(function_modifier_library) +BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2) { char const* sourceCode = R"( - library L { - struct S { uint v; } - modifier mod(S storage s) { s.v++; _; } - function libFun(S storage s) mod(s) internal { s.v += 0x100; } - } - - contract Test { - using L for *; - L.S s; - - function f() public returns (uint) { - s.libFun(); - L.libFun(s); - return s.v; + pragma experimental ABIEncoderV2; + contract C { + event E(uint[][]); + uint[][] arr; + function createEvent(uint x) public { + arr.push(new uint[](2)); + arr.push(new uint[](2)); + arr[0][0] = x; + arr[0][1] = x + 1; + arr[1][0] = x + 2; + arr[1][1] = x + 3; + emit E(arr); } } )"; + /// TODO enable again after push(..) via yul is implemented for nested arrays. + /// ALSO_VIA_YUL() compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[][])"))); } -BOOST_AUTO_TEST_CASE(function_modifier_library_inheritance) +BOOST_AUTO_TEST_CASE(event_indexed_string) { - // Tests that virtual lookup for modifiers in libraries does not consider - // the current inheritance hierarchy. - char const* sourceCode = R"( - library L { - struct S { uint v; } - modifier mod(S storage s) { s.v++; _; } - function libFun(S storage s) mod(s) internal { s.v += 0x100; } - } - - contract Test { - using L for *; - L.S s; - modifier mod(L.S storage) { revert(); _; } - - function f() public returns (uint) { - s.libFun(); - L.libFun(s); - return s.v; + contract C { + string x; + uint[4] y; + event E(string indexed r, uint[4] indexed t); + function deposit() public { + for (uint i = 0; i < 90; i++) + bytes(x).push(0); + for (uint8 i = 0; i < 90; i++) + bytes(x)[i] = byte(i); + y[0] = 4; + y[1] = 5; + y[2] = 6; + y[3] = 7; + emit E(x, y); } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + string dynx(90, 0); + for (size_t i = 0; i < dynx.size(); ++i) + dynx[i] = i; + BOOST_CHECK(logData(0) == bytes()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(dynx)); + BOOST_CHECK_EQUAL(logTopic(0, 2), util::keccak256( + encodeArgs(u256(4), u256(5), u256(6), u256(7)) + )); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string,uint256[4])"))); } -BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) +BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* sourceCode = R"( - contract C { - function f() public returns (uint r) { - uint; uint; uint; uint; - int x = -7; - return uint(x); + contract test { + function f(uint, uint k) public returns(uint ret_k, uint ret_g){ + uint g = 8; + ret_k = k; + ret_g = g; } } )"; ALSO_VIA_YUL( compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(-7))); - ) -} - -BOOST_AUTO_TEST_CASE(super) -{ - char const* sourceCode = R"( - contract A { function f() public virtual returns (uint r) { return 1; } } - contract B is A { function f() public virtual override returns (uint r) { return super.f() | 2; } } - contract C is A { function f() public virtual override returns (uint r) { return super.f() | 4; } } - contract D is B, C { function f() public override(B, C) returns (uint r) { return super.f() | 8; } } - )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); -} - -BOOST_AUTO_TEST_CASE(super_in_constructor) -{ - char const* sourceCode = R"( - contract A { function f() public virtual returns (uint r) { return 1; } } - contract B is A { function f() public virtual override returns (uint r) { return super.f() | 2; } } - contract C is A { function f() public virtual override returns (uint r) { return super.f() | 4; } } - contract D is B, C { uint data; constructor() public { data = super.f() | 8; } function f() public override (B, C) returns (uint r) { return data; } } - )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); -} - -BOOST_AUTO_TEST_CASE(super_alone) -{ - char const* sourceCode = R"( - contract A { function f() public { super; } } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "A"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); + ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8)); ) } -BOOST_AUTO_TEST_CASE(default_fallback_throws) +BOOST_AUTO_TEST_CASE(generic_call) { - char const* sourceCode = R"YY( - contract A { - function f() public returns (bool) { - (bool success,) = address(this).call(""); - return success; + char const* sourceCode = R"**( + contract receiver { + uint public received; + function recv(uint256 x) public payable { received = x; } } - } - )YY"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); - - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - char const* sourceCode = R"YY( - contract A { - function f() public returns (bool) { - (bool success, bytes memory data) = address(this).staticcall(""); - assert(data.length == 0); - return success; + contract sender { + constructor() public payable {} + function doSend(address rec) public returns (uint d) + { + bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)"))); + rec.call.value(2)(abi.encodeWithSelector(signature, 23)); + return receiver(rec).received(); } } - )YY"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); - } + )**"; + compileAndRun(sourceCode, 0, "receiver"); + u160 const c_receiverAddress = m_contractAddress; + compileAndRun(sourceCode, 50, "sender"); + BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23)); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 50 - 2); } -BOOST_AUTO_TEST_CASE(event) +BOOST_AUTO_TEST_CASE(generic_delegatecall) { - char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(address indexed _from, bytes32 indexed _id, uint _value); - function deposit(bytes32 _id, bool _manually) public payable { - if (_manually) { - bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f; - log3(bytes32(msg.value), s, bytes32(uint256(msg.sender)), _id); - } else { - emit Deposit(msg.sender, _id, msg.value); - } - } - } - )"; - compileAndRun(sourceCode); - u256 value(18); - u256 id(0x1234); - for (bool manually: {true, false}) + char const* sourceCode = R"**( + contract Receiver { + uint public received; + address public sender; + uint public value; + constructor() public payable {} + function recv(uint256 x) public payable { received = x; sender = msg.sender; value = msg.value; } + } + contract Sender { + uint public received; + address public sender; + uint public value; + constructor() public payable {} + function doSend(address rec) public payable + { + bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)"))); + (bool success,) = rec.delegatecall(abi.encodeWithSelector(signature, 23)); + success; + } + } + )**"; + + for (auto v2: {false, true}) { - callContractFunctionWithValue("deposit(bytes32,bool)", value, id, manually); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(value))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight)); - BOOST_CHECK_EQUAL(logTopic(0, 2), h256(id)); + string source = (v2 ? "pragma experimental ABIEncoderV2;\n" : "") + string(sourceCode); + + compileAndRun(source, 0, "Receiver"); + u160 const c_receiverAddress = m_contractAddress; + compileAndRun(source, 50, "Sender"); + u160 const c_senderAddress = m_contractAddress; + BOOST_CHECK(m_sender != c_senderAddress); // just for sanity + ABI_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress), encodeArgs()); + ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(23))); + ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("value()"), encodeArgs(u256(11))); + m_contractAddress = c_receiverAddress; + ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("sender()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("value()"), encodeArgs(u256(0))); + BOOST_CHECK(storageEmpty(c_receiverAddress)); + BOOST_CHECK(!storageEmpty(c_senderAddress)); + BOOST_CHECK_EQUAL(balanceAt(c_receiverAddress), 0); + BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11); } } -BOOST_AUTO_TEST_CASE(event_emit) +BOOST_AUTO_TEST_CASE(generic_staticcall) +{ + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"**( + contract A { + uint public x; + constructor() public { x = 42; } + function pureFunction(uint256 p) public pure returns (uint256) { return p; } + function viewFunction(uint256 p) public view returns (uint256) { return p + x; } + function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; } + function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; } + } + contract C { + function f(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23)); + } + function g(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23)); + } + function h(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23)); + } + function i(address a, uint256 v) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v)); + } + } + )**"; + compileAndRun(sourceCode, 0, "A"); + u160 const c_addressA = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23)); + ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23 + 42)); + ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false, 0x40, 0x00)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true, 0x40, 0x20, 42)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false, 0x40, 0x00)); + } +} + +BOOST_AUTO_TEST_CASE(library_call_in_homestead) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(address indexed _from, bytes32 indexed _id, uint _value); - function deposit(bytes32 _id) public payable { - emit Deposit(msg.sender, _id, msg.value); + library Lib { function m() public returns (address) { return msg.sender; } } + contract Test { + address public sender; + function f() public { + sender = Lib.m(); } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - u256 value(18); - u256 id(0x1234); - callContractFunctionWithValue("deposit(bytes32)", value, id); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(value))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight)); - BOOST_CHECK_EQUAL(logTopic(0, 2), h256(id)); - ) + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); } -BOOST_AUTO_TEST_CASE(event_no_arguments) +BOOST_AUTO_TEST_CASE(library_call_protection) { + // This tests code that reverts a call if it is a direct call to a library + // as opposed to a delegatecall. char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(); - function deposit() public { - emit Deposit(); - } + library Lib { + struct S { uint x; } + // a direct call to this should revert + function np(S storage s) public returns (address) { s.x = 3; return msg.sender; } + // a direct call to this is fine + function v(S storage) public view returns (address) { return msg.sender; } + // a direct call to this is fine + function pu() public pure returns (uint) { return 2; } + } + contract Test { + Lib.S public s; + function np() public returns (address) { return Lib.np(s); } + function v() public view returns (address) { return Lib.v(s); } + function pu() public pure returns (uint) { return Lib.pu(); } } )"; + compileAndRun(sourceCode, 0, "Lib"); + ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("np()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("s()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("v()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); +} - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0).empty()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); - ) +BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) +{ + char const* sourceCode = R"( + library Lib { + function x() public view returns (uint) { + return 1; + } + } + contract Test { + uint t; + function f() public returns (uint) { + t = 2; + return this.g(); + } + function g() public view returns (uint) { + return Lib.x(); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); } -BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) { char const* sourceCode = R"( - contract A { - event x(); - } - contract B is A { - function f() public returns (uint) { - emit A.x(); - return 1; + contract C { + function f() public returns (bytes32) { + return keccak256(abi.encodePacked("abc", msg.data)); } } )"; compileAndRun(sourceCode); - callContractFunction("f()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0).empty()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("x()"))); + bytes calldata1 = FixedHash<4>(util::keccak256("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); + sendMessage(calldata1, false); + BOOST_CHECK(m_transactionSuccessful); + BOOST_CHECK(m_output == encodeArgs(util::keccak256(bytes{'a', 'b', 'c'} + calldata1))); } -BOOST_AUTO_TEST_CASE(events_with_same_name) +BOOST_AUTO_TEST_CASE(call_forward_bytes) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(); - event Deposit(address _addr); - event Deposit(address _addr, uint _amount); - event Deposit(address _addr, bool _flag); - function deposit() public returns (uint) { - emit Deposit(); - return 1; - } - function deposit(address _addr) public returns (uint) { - emit Deposit(_addr); - return 2; - } - function deposit(address _addr, uint _amount) public returns (uint) { - emit Deposit(_addr, _amount); - return 3; - } - function deposit(address _addr, bool _flag) public returns (uint) { - emit Deposit(_addr, _flag); - return 4; - } + contract receiver { + uint public received; + function recv(uint x) public { received += x + 1; } + fallback() external { received = 0x80; } + } + contract sender { + constructor() public { rec = new receiver(); } + fallback() external { savedData = msg.data; } + function forward() public returns (bool) { address(rec).call(savedData); return true; } + function clear() public returns (bool) { delete savedData; return true; } + function val() public returns (uint) { return rec.received(); } + receiver rec; + bytes savedData; } )"; - u160 const c_loggedAddress = m_contractAddress; - - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0).empty()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); - - ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(2))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - ABI_CHECK(logData(0), encodeArgs(c_loggedAddress)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address)"))); - - ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(3))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, 100)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,uint256)"))); - - ABI_CHECK(callContractFunction("deposit(address,bool)", c_loggedAddress, false), encodeArgs(u256(4))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, false)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bool)"))); - ) + compileAndRun(sourceCode, 0, "sender"); + ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes()); + ABI_CHECK(callContractFunction("val()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("forward()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); + ABI_CHECK(callContractFunction("clear()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); + ABI_CHECK(callContractFunction("forward()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); } -BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +BOOST_AUTO_TEST_CASE(call_forward_bytes_length) { char const* sourceCode = R"( - contract A { - event Deposit(); - } - - contract B { - event Deposit(address _addr); + contract receiver { + uint public calledLength; + fallback() external { calledLength = msg.data.length; } } - - contract ClientReceipt is A, B { - event Deposit(address _addr, uint _amount); - function deposit() public returns (uint) { - emit Deposit(); - return 1; + contract sender { + receiver rec; + constructor() public { rec = new receiver(); } + function viaCalldata() public returns (uint) { + (bool success,) = address(rec).call(msg.data); + require(success); + return rec.calledLength(); } - function deposit(address _addr) public returns (uint) { - emit Deposit(_addr); - return 1; + function viaMemory() public returns (uint) { + bytes memory x = msg.data; + (bool success,) = address(rec).call(x); + require(success); + return rec.calledLength(); } - function deposit(address _addr, uint _amount) public returns (uint) { - emit Deposit(_addr, _amount); - return 1; + bytes s; + function viaStorage() public returns (uint) { + s = msg.data; + (bool success,) = address(rec).call(s); + require(success); + return rec.calledLength(); } } )"; - u160 const c_loggedAddress = m_contractAddress; - - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0).empty()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit()"))); + compileAndRun(sourceCode, 0, "sender"); - ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(c_loggedAddress)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address)"))); + // No additional data, just function selector + ABI_CHECK(callContractFunction("viaCalldata()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("viaMemory()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("viaStorage()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - ABI_CHECK(logData(0), encodeArgs(c_loggedAddress, 100)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,uint256)"))); - ) + // Some additional unpadded data + bytes unpadded = asBytes(string("abc")); + ABI_CHECK(callContractFunctionNoEncoding("viaCalldata()", unpadded), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("viaMemory()", unpadded), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("viaStorage()", unpadded), encodeArgs(7)); } -BOOST_AUTO_TEST_CASE(event_anonymous) +BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit() anonymous; - function deposit() public { - emit Deposit(); + contract receiver { + uint public received; + function recv(uint x) public { received += x + 1; } + fallback() external { received = 0x80; } + } + contract sender { + constructor() public { rec = new receiver(); } + fallback() external { savedData1 = savedData2 = msg.data; } + function forward(bool selector) public returns (bool) { + if (selector) { address(rec).call(savedData1); delete savedData1; } + else { address(rec).call(savedData2); delete savedData2; } + return true; } + function val() public returns (uint) { return rec.received(); } + receiver rec; + bytes savedData1; + bytes savedData2; } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 0); - ) + compileAndRun(sourceCode, 0, "sender"); + ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes()); + ABI_CHECK(callContractFunction("val()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("forward(bool)", true), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); + ABI_CHECK(callContractFunction("forward(bool)", false), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(16)); + ABI_CHECK(callContractFunction("forward(bool)", true), encodeArgs(true)); + ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); } -BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) +BOOST_AUTO_TEST_CASE(delete_removes_bytes_data) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous; - function deposit(bytes32 _id) public payable { - emit Deposit(msg.sender, _id, msg.value, 2, "abc"); - } + contract c { + fallback() external { data = msg.data; } + function del() public returns (bool) { delete data; return true; } + bytes data; } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - u256 value(18); - u256 id(0x1234); - callContractFunctionWithValue("deposit(bytes32)", value, id); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs("abc")); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 4); - BOOST_CHECK_EQUAL(logTopic(0, 0), h256(m_sender, h256::AlignRight)); - BOOST_CHECK_EQUAL(logTopic(0, 1), h256(id)); - BOOST_CHECK_EQUAL(logTopic(0, 2), h256(value)); - BOOST_CHECK_EQUAL(logTopic(0, 3), h256(2)); - ) + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("---", 7), bytes()); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(event_lots_of_data) +BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(address _from, bytes32 _id, uint _value, bool _flag); - function deposit(bytes32 _id) public payable { - emit Deposit(msg.sender, _id, msg.value, true); - } + contract c { + function set() public returns (bool) { data = msg.data; return true; } + fallback() external { data = msg.data; } + bytes data; } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - u256 value(18); - u256 id(0x1234); - callContractFunctionWithValue("deposit(bytes32)", value, id); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs((u160)m_sender, id, value, true)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256,bool)"))); - ) + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + sendMessage(bytes(), false); + BOOST_CHECK(m_transactionSuccessful); + BOOST_CHECK(m_output.empty()); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(event_really_lots_of_data) +BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) { char const* sourceCode = R"( - contract ClientReceipt { - event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() public { - emit Deposit(10, msg.data, 15); - } + contract c { + function set() public returns (bool) { data1 = msg.data; return true; } + function reset() public returns (bool) { data1 = data2; return true; } + bytes data1; + bytes data2; } )"; compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(toHex(logData(0)), toHex(encodeArgs(10, 0x60, 15, 4, asString(FixedHash<4>(util::keccak256("deposit()")).asBytes())))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); + ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("reset()"), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) +BOOST_AUTO_TEST_CASE(bytes_inside_mappings) { char const* sourceCode = R"( - contract ClientReceipt { - bytes x; - event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() public { - x.push("A"); - x.push("B"); - x.push("C"); - emit Deposit(10, x, 15); - } + contract c { + function set(uint key) public returns (bool) { data[key] = msg.data; return true; } + function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } + mapping(uint => bytes) data; } )"; compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(toHex(logData(0)), toHex(encodeArgs(10, 0x60, 15, 3, string("ABC")))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); -} - -BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) -{ - char const* sourceCode = R"( - contract ClientReceipt { - bytes x; - event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() public { - x = new bytes(31); - x[0] = "A"; - x[1] = "B"; - x[2] = "C"; - x[30] = "Z"; - emit Deposit(10, x, 15); + // store a short byte array at 1 and a longer one at 2 + ABI_CHECK(callContractFunction("set(uint256)", 1, 2), encodeArgs(true)); + ABI_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + // copy shorter to longer + ABI_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + // copy empty to both + ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) +{ + char const* sourceCode = R"( + contract c { + struct Struct { uint a; bytes data; uint b; } + Struct data1; + Struct data2; + function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { + data1.a = _a; + data1.b = _b; + data1.data = _data; + return true; + } + function copy() public returns (bool) { + data1 = data2; + return true; + } + function del() public returns (bool) { + delete data1; + return true; } } )"; compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(10, 0x60, 15, 31, string("ABC") + string(27, 0) + "Z")); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(uint256,bytes,uint256)"))); + string data = "123456789012345678901234567890123"; + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("copy()"), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("del()"), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(event_struct_memory_v2) +BOOST_AUTO_TEST_CASE(storing_invalid_boolean) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; contract C { - struct S { uint a; } - event E(S); - function createEvent(uint x) public { - emit E(S(x)); + event Ev(bool); + bool public perm; + function set() public returns(uint) { + bool tmp; + assembly { + tmp := 5 + } + perm = tmp; + return 1; + } + function ret() public returns(bool) { + bool tmp; + assembly { + tmp := 5 + } + return tmp; + } + function ev() public returns(uint) { + bool tmp; + assembly { + tmp := 5 + } + emit Ev(tmp); + return 1; } } )"; compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); + ABI_CHECK(callContractFunction("set()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("perm()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("ret()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("ev()"), encodeArgs(1)); BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(x)); + BOOST_CHECK(logData(0) == encodeArgs(1)); BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint256))"))); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Ev(bool)"))); } -BOOST_AUTO_TEST_CASE(event_struct_storage_v2) +BOOST_AUTO_TEST_CASE(struct_referencing) { - char const* sourceCode = R"( + static char const* sourceCode = R"( pragma experimental ABIEncoderV2; - contract C { + interface I { struct S { uint a; } - event E(S); - S s; - function createEvent(uint x) public { - s.a = x; - emit E(s); + } + library L { + struct S { uint b; uint a; } + function f() public pure returns (S memory) { + S memory s; + s.a = 3; + return s; + } + function g() public pure returns (I.S memory) { + I.S memory s; + s.a = 4; + return s; + } + // argument-dependant lookup tests + function a(I.S memory) public pure returns (uint) { return 1; } + function a(S memory) public pure returns (uint) { return 2; } + } + contract C is I { + function f() public pure returns (S memory) { + S memory s; + s.a = 1; + return s; + } + function g() public pure returns (I.S memory) { + I.S memory s; + s.a = 2; + return s; + } + function h() public pure returns (L.S memory) { + L.S memory s; + s.a = 5; + return s; + } + function x() public pure returns (L.S memory) { + return L.f(); + } + function y() public pure returns (I.S memory) { + return L.g(); } + function a1() public pure returns (uint) { S memory s; return L.a(s); } + function a2() public pure returns (uint) { L.S memory s; return L.a(s); } } )"; - compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(x)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint256))"))); + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(4)); + compileAndRun(sourceCode, 0, "C", bytes(), map{ {"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 5)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("a1()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("a2()"), encodeArgs(2)); } -BOOST_AUTO_TEST_CASE(event_dynamic_array_memory) +BOOST_AUTO_TEST_CASE(enum_referencing) { char const* sourceCode = R"( - contract C { - event E(uint[]); - function createEvent(uint x) public { - uint[] memory arr = new uint[](3); - arr[0] = x; - arr[1] = x + 1; - arr[2] = x + 2; - emit E(arr); + interface I { + enum Direction { A, B, Left, Right } + } + library L { + enum Direction { Left, Right } + function f() public pure returns (Direction) { + return Direction.Right; + } + function g() public pure returns (I.Direction) { + return I.Direction.Right; + } + } + contract C is I { + function f() public pure returns (Direction) { + return Direction.Right; + } + function g() public pure returns (I.Direction) { + return I.Direction.Right; + } + function h() public pure returns (L.Direction) { + return L.Direction.Right; + } + function x() public pure returns (L.Direction) { + return L.f(); + } + function y() public pure returns (I.Direction) { + return L.g(); } } )"; - compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); } -BOOST_AUTO_TEST_CASE(event_dynamic_array_memory_v2) +BOOST_AUTO_TEST_CASE(bytes_in_arguments) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - event E(uint[]); - function createEvent(uint x) public { - uint[] memory arr = new uint[](3); - arr[0] = x; - arr[1] = x + 1; - arr[2] = x + 2; - emit E(arr); + contract c { + uint result; + function f(uint a, uint b) public { result += a + b; } + function g(uint a) public { result *= a; } + function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { + r_a = a; + address(this).call(data1); + address(this).call(data2); + r = result; + r_b = b; + l = data1.length; } } )"; compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); -} -BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_memory_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - event E(uint[][]); - function createEvent(uint x) public { - uint[][] memory arr = new uint[][](2); - arr[0] = new uint[](2); - arr[1] = new uint[](2); - arr[0][0] = x; - arr[0][1] = x + 1; - arr[1][0] = x + 2; - arr[1][1] = x + 3; - emit E(arr); - } - } - )"; - compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[][])"))); + string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); + string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); + bytes calldata = encodeArgs( + 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, + u256(innercalldata1.length()), innercalldata1, + u256(innercalldata2.length()), innercalldata2); + ABI_CHECK( + callContractFunction("test(uint256,bytes,bytes,uint256)", calldata), + encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length())) + ); } -BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) +BOOST_AUTO_TEST_CASE(fixed_array_cleanup) { char const* sourceCode = R"( - contract C { - event E(uint[]); - uint[] arr; - function createEvent(uint x) public { - while (arr.length < 3) - arr.push(); - arr[0] = x; - arr[1] = x + 1; - arr[2] = x + 2; - emit E(arr); + contract c { + uint spacer1; + uint spacer2; + uint[20] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; } + function clear() public { delete data; } } )"; ALSO_VIA_YUL( compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); - ) + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("fill()"), bytes()); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("clear()"), bytes()); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ); } -BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) +BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - event E(uint[]); - uint[] arr; - function createEvent(uint x) public { - while (arr.length < 3) - arr.push(); - arr[0] = x; - arr[1] = x + 1; - arr[2] = x + 2; - emit E(arr); + contract c { + uint spacer1; + uint spacer2; + uint[3] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; } + function clear() public { delete data; } } )"; ALSO_VIA_YUL( compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 3, x, x + 1, x + 2)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[])"))); - ) -} - -BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - event E(uint[][]); - uint[][] arr; - function createEvent(uint x) public { - arr.push(new uint[](2)); - arr.push(new uint[](2)); - arr[0][0] = x; - arr[0][1] = x + 1; - arr[1][0] = x + 2; - arr[1][1] = x + 3; - emit E(arr); - } - } - )"; - /// TODO enable again after push(..) via yul is implemented for nested arrays. - /// ALSO_VIA_YUL() - compileAndRun(sourceCode); - u256 x(42); - callContractFunction("createEvent(uint256)", x); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(uint256[][])"))); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("fill()"), bytes()); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("clear()"), bytes()); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ); } -BOOST_AUTO_TEST_CASE(event_indexed_string) +BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) { char const* sourceCode = R"( - contract C { - string x; - uint[4] y; - event E(string indexed r, uint[4] indexed t); - function deposit() public { - for (uint i = 0; i < 90; i++) - bytes(x).push(0); - for (uint8 i = 0; i < 90; i++) - bytes(x)[i] = byte(i); - y[0] = 4; - y[1] = 5; - y[2] = 6; - y[3] = 7; - emit E(x, y); + contract c { + uint[20] spacer; + uint[] dynamic; + function fill() public { + for (uint i = 0; i < 21; ++i) + dynamic.push(i + 1); } - } - )"; - compileAndRun(sourceCode); - callContractFunction("deposit()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - string dynx(90, 0); - for (size_t i = 0; i < dynx.size(); ++i) - dynx[i] = i; - BOOST_CHECK(logData(0) == bytes()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(dynx)); - BOOST_CHECK_EQUAL(logTopic(0, 2), util::keccak256( - encodeArgs(u256(4), u256(5), u256(6), u256(7)) - )); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string,uint256[4])"))); -} - -BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) -{ - char const* sourceCode = R"( - contract test { - function f(uint, uint k) public returns(uint ret_k, uint ret_g){ - uint g = 8; - ret_k = k; - ret_g = g; + function halfClear() public { + while (dynamic.length > 5) + dynamic.pop(); } + function fullClear() public { delete dynamic; } } )"; ALSO_VIA_YUL( compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); - ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8)); - ) + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("fill()"), bytes()); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("halfClear()"), bytes()); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("fullClear()"), bytes()); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ); } -BOOST_AUTO_TEST_CASE(empty_name_return_parameter) +BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) { char const* sourceCode = R"( - contract test { - function f(uint k) public returns(uint){ - return k; - } + contract c { + struct s { uint[][] d; } + s[] data; + function fill() public returns (uint) { + while (data.length < 3) + data.push(); + while (data[2].d.length < 4) + data[2].d.push(); + while (data[2].d[3].length < 5) + data[2].d[3].push(); + data[2].d[3][4] = 8; + return data[2].d[3][4]; + } + function clear() public { delete data; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", 9), encodeArgs(9)); - ) + compileAndRun(sourceCode); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("fill()"), encodeArgs(8)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("clear()"), bytes()); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(sha256_empty) +BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) { char const* sourceCode = R"( - contract C { - function f() public returns (bytes32) { - return sha256(""); + contract c { + uint[] data1; + uint[] data2; + function setData1(uint length, uint index, uint value) public { + data1 = new uint[](length); + if (index < length) + data1[index] = value; + } + function copyStorageStorage() public { data2 = data1; } + function getData2(uint index) public returns (uint len, uint val) { + len = data2.length; if (index < len) val = data2[index]; } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4), bytes()); + ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); + ABI_CHECK(callContractFunction("getData2(uint256)", 5), encodeArgs(10, 4)); + ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0), bytes()); + ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); + ABI_CHECK(callContractFunction("getData2(uint256)", 0), encodeArgs(0, 0)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(ripemd160_empty) +BOOST_AUTO_TEST_CASE(array_copy_target_leftover) { + // test that leftover elements in the last slot of target are correctly cleared during assignment char const* sourceCode = R"( - contract C { - function f() public returns (bytes20) { - return ripemd160(""); + contract c { + byte[10] data1; + bytes2[32] data2; + function test() public returns (uint check, uint res1, uint res2) { + uint i; + for (i = 0; i < data2.length; ++i) + data2[i] = 0xffff; + check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); + for (i = 0; i < data1.length; ++i) + data1[i] = byte(uint8(1 + i)); + data2 = data1; + for (i = 0; i < 16; ++i) + res1 |= uint(uint16(data2[i])) * 0x10000**i; + for (i = 0; i < 16; ++i) + res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000")); + ABI_CHECK(callContractFunction("test()"), encodeArgs(u256("0xffffffff"), asString(fromHex("0000000000000000000000000a00090008000700060005000400030002000100")), asString(fromHex("0000000000000000000000000000000000000000000000000000000000000000")))); } -BOOST_AUTO_TEST_CASE(keccak256_empty) +BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) { char const* sourceCode = R"( - contract C { - function f() public returns (bytes32) { - return keccak256(""); + contract c { + struct Data { uint x; uint y; } + Data[] data1; + Data[] data2; + function test() public returns (uint x, uint y) { + while (data1.length < 9) + data1.push(); + data1[8].x = 4; + data1[8].y = 5; + data2 = data1; + x = data2[8].x; + y = data2[8].y; + while (data1.length > 0) + data1.pop(); + data2 = data1; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ) + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(4, 5)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) +BOOST_AUTO_TEST_CASE(array_copy_storage_abi) { + // NOTE: This does not really test copying from storage to ABI directly, + // because it will always copy to memory first. char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract c { - function foo(uint a, uint b, uint c) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, c)); + uint8[] x; + uint16[] y; + uint24[] z; + uint24[][] w; + function test1() public returns (uint8[] memory) { + for (uint i = 0; i < 101; ++i) + x.push(uint8(i)); + return x; + } + function test2() public returns (uint16[] memory) { + for (uint i = 0; i < 101; ++i) + y.push(uint16(i)); + return y; + } + function test3() public returns (uint24[] memory) { + for (uint i = 0; i < 101; ++i) + z.push(uint24(i)); + return z; + } + function test4() public returns (uint24[][] memory) { + w = new uint24[][](5); + for (uint i = 0; i < 5; ++i) + for (uint j = 0; j < 101; ++j) + w[i].push(uint24(j)); + return w; } } )"; compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - toBigEndian(u256(12)) + - toBigEndian(u256(13)) - ) - )); + bytes valueSequence; + for (size_t i = 0; i < 101; ++i) + valueSequence += toBigEndian(u256(i)); + ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test4()"), + encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + ); } -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_numeric_literals) +BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) { char const* sourceCode = R"( contract c { - function foo(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145))); + uint16[] data; + function test() public returns (uint16 x, uint16 y, uint16 z) { + for (uint i = 1; i <= 48; i++) + data.push(uint16(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1]; + for (uint m = 1; m <= 18; m++) + data.pop(); } } )"; compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) - ) - )); + ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_string_literals) +BOOST_AUTO_TEST_CASE(array_pop_uint24_transition) { char const* sourceCode = R"( contract c { - function foo() public returns (bytes32 d) - { - d = keccak256("foo"); - } - function bar(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); + uint256 a; + uint256 b; + uint256 c; + uint24[] data; + function test() public returns (uint24 x, uint24 y) { + for (uint i = 1; i <= 30; i++) + data.push(uint24(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); } } )"; compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} - ABI_CHECK(callContractFunction("foo()"), encodeArgs(util::keccak256("foo"))); - - ABI_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) + - bytes{0x66, 0x6f, 0x6f} - ) - )); +BOOST_AUTO_TEST_CASE(array_pop_array_transition) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + uint16[][] data; + function test() public returns (uint x, uint y, uint z) { + for (uint i = 1; i <= 48; i++) + data.push(inner); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1][0]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1][1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1][2]; + for (uint m = 1; m <= 18; m++) + data.pop(); + delete inner; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(keccak256_with_bytes) +BOOST_AUTO_TEST_CASE(array_pop_storage_empty) { char const* sourceCode = R"( contract c { - bytes data; - function foo() public returns (bool) - { - data.push("f"); - data.push("o"); - data.push("o"); - return keccak256(data) == keccak256("foo"); + uint[] data; + function test() public { + data.push(7); + data.pop(); } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes) +BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) { - char const* sourceCode = R"ABC( + char const* sourceCode = R"( contract c { bytes data; - function foo() public returns (bytes32) - { - data.push("x"); - data.push("y"); - data.push("z"); - return keccak256(abi.encodePacked("b", keccak256(data), "a")); + function test() public { + data.push(0x07); + data.push(0x05); + data.push(0x03); + data.pop(); + data.pop(); + data.pop(); } } - )ABC"; + )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo()"), encodeArgs( - u256(util::keccak256(bytes{'b'} + util::keccak256("xyz").asBytes() + bytes{'a'})) - )); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(generic_call) +BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty) { - char const* sourceCode = R"**( - contract receiver { - uint public received; - function recv(uint256 x) public payable { received = x; } - } - contract sender { - constructor() public payable {} - function doSend(address rec) public returns (uint d) - { - bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)"))); - rec.call.value(2)(abi.encodeWithSelector(signature, 23)); - return receiver(rec).received(); + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + function test() public returns (bool) { + for (uint8 i = 0; i <= 40; i++) + data.push(byte(i+1)); + for (int8 j = 40; j >= 0; j--) { + require(data[uint8(j)] == byte(j+1)); + require(data.length == uint8(j+1)); + data.pop(); } + return true; } - )**"; - compileAndRun(sourceCode, 0, "receiver"); - u160 const c_receiverAddress = m_contractAddress; - compileAndRun(sourceCode, 50, "sender"); - BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23)); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 50 - 2); + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(generic_delegatecall) +BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) { - char const* sourceCode = R"**( - contract Receiver { - uint public received; - address public sender; - uint public value; - constructor() public payable {} - function recv(uint256 x) public payable { received = x; sender = msg.sender; value = msg.value; } - } - contract Sender { - uint public received; - address public sender; - uint public value; - constructor() public payable {} - function doSend(address rec) public payable - { - bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)"))); - (bool success,) = rec.delegatecall(abi.encodeWithSelector(signature, 23)); - success; + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + bytes data; + function test() public { + for (uint8 i = 0; i <= 40; i++) + data.push(0x03); + for (uint8 j = 0; j <= 40; j++) { + assembly { + mstore(0, "garbage") + } + data.pop(); } } - )**"; - - for (auto v2: {false, true}) - { - string source = (v2 ? "pragma experimental ABIEncoderV2;\n" : "") + string(sourceCode); + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} - compileAndRun(source, 0, "Receiver"); - u160 const c_receiverAddress = m_contractAddress; - compileAndRun(source, 50, "Sender"); - u160 const c_senderAddress = m_contractAddress; - BOOST_CHECK(m_sender != c_senderAddress); // just for sanity - ABI_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress), encodeArgs()); - ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(23))); - ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); - ABI_CHECK(callContractFunction("value()"), encodeArgs(u256(11))); - m_contractAddress = c_receiverAddress; - ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("sender()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("value()"), encodeArgs(u256(0))); - BOOST_CHECK(storageEmpty(c_receiverAddress)); - BOOST_CHECK(!storageEmpty(c_senderAddress)); - BOOST_CHECK_EQUAL(balanceAt(c_receiverAddress), 0); - BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11); - } -} - -BOOST_AUTO_TEST_CASE(generic_staticcall) -{ - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - char const* sourceCode = R"**( - contract A { - uint public x; - constructor() public { x = 42; } - function pureFunction(uint256 p) public pure returns (uint256) { return p; } - function viewFunction(uint256 p) public view returns (uint256) { return p + x; } - function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; } - function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; } - } - contract C { - function f(address a) public view returns (bool, bytes memory) - { - return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23)); - } - function g(address a) public view returns (bool, bytes memory) - { - return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23)); - } - function h(address a) public view returns (bool, bytes memory) - { - return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23)); - } - function i(address a, uint256 v) public view returns (bool, bytes memory) - { - return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v)); - } - } - )**"; - compileAndRun(sourceCode, 0, "A"); - u160 const c_addressA = m_contractAddress; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23)); - ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23 + 42)); - ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false, 0x40, 0x00)); - ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true, 0x40, 0x20, 42)); - ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false, 0x40, 0x00)); - } -} - -BOOST_AUTO_TEST_CASE(library_call_in_homestead) +BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( - library Lib { function m() public returns (address) { return msg.sender; } } - contract Test { - address public sender; - function f() public { - sender = Lib.m(); + contract c { + function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) + external returns (uint av, uint bv, uint cv) { + av = a[a_index]; + bv = b[b_index]; + cv = c[c_index]; } } )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); + compileAndRun(sourceCode); + bytes params = encodeArgs( + 1, 2, 3, 4, 5, 6, 7, 8, // a + 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b + 21, 22, 23, 24, 25, // c + 0, 1, 2, // (a,b,c)_index + 3, // b.length + 11, 12, 13 // b + ); + ABI_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params), encodeArgs(1, 12, 23)); } -BOOST_AUTO_TEST_CASE(library_call_protection) +BOOST_AUTO_TEST_CASE(bytes_index_access) { - // This tests code that reverts a call if it is a direct call to a library - // as opposed to a delegatecall. char const* sourceCode = R"( - library Lib { - struct S { uint x; } - // a direct call to this should revert - function np(S storage s) public returns (address) { s.x = 3; return msg.sender; } - // a direct call to this is fine - function v(S storage) public view returns (address) { return msg.sender; } - // a direct call to this is fine - function pu() public pure returns (uint) { return 2; } - } - contract Test { - Lib.S public s; - function np() public returns (address) { return Lib.np(s); } - function v() public view returns (address) { return Lib.v(s); } - function pu() public pure returns (uint) { return Lib.pu(); } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); - ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("np()"), encodeArgs(u160(m_sender))); - ABI_CHECK(callContractFunction("s()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("v()"), encodeArgs(u160(m_sender))); - ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); -} - + contract c { + bytes data; + function direct(bytes calldata arg, uint index) external returns (uint) { + return uint(uint8(arg[index])); + } + function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { + data = arg; + return uint(uint8(data[index])); + } + function storageWrite() external returns (uint) { + data = new bytes(35); + data[31] = 0x77; + data[32] = 0x14; -BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) -{ - char const* sourceCode = R"( - library Lib { - function x() public view returns (uint) { - return 1; - } - } - contract Test { - uint t; - function f() public returns (uint) { - t = 2; - return this.g(); - } - function g() public view returns (uint) { - return Lib.x(); - } - } + data[31] = 0x01; + data[31] |= 0x08; + data[30] = 0x01; + data[32] = 0x03; + return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); + } + } )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + compileAndRun(sourceCode); + string array{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33}; + ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); + ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); + ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193)); } -BOOST_AUTO_TEST_CASE(store_bytes) +BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) { - // this test just checks that the copy loop does not mess up the stack char const* sourceCode = R"( - contract C { - function save() public returns (uint r) { - r = 23; - savedData = msg.data; - r = 24; + contract c { + uint[9] m_data; + uint[] m_data_dyn; + uint8[][] m_byte_data; + function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { + m_data = a; + m_data_dyn = a; + m_byte_data = b; + return b[3][1]; // note that access and declaration are reversed to each other + } + function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { + a = m_data.length; + b = m_data[7]; + c = m_data_dyn.length; + d = m_data_dyn[7]; + e = m_byte_data.length; + f = m_byte_data[3].length; + g = m_byte_data[3][1]; } - bytes savedData; } )"; compileAndRun(sourceCode); - // empty copy loop - ABI_CHECK(callContractFunction("save()"), encodeArgs(24)); - ABI_CHECK(callContractFunction("save()", "abcdefg"), encodeArgs(24)); + ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(21, 22, 23, 24, 25, 26, 27, 28, 29, u256(32 * (9 + 1)), 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 )), encodeArgs(32)); + ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32)); } -BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) +BOOST_AUTO_TEST_CASE(array_copy_including_mapping) { char const* sourceCode = R"( - contract C { - function f() public returns (bytes32) { - return keccak256(abi.encodePacked("abc", msg.data)); + contract c { + mapping(uint=>uint)[90][] large; + mapping(uint=>uint)[3][] small; + function test() public returns (uint r) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + large[3][2][0] = 2; + large[1] = large[3]; + small[3][2][0] = 2; + small[1] = small[2]; + r = (( + small[3][2][0] * 0x100 | + small[1][2][0]) * 0x100 | + large[3][2][0]) * 0x100 | + large[1][2][0]; + delete small; + delete large; + + } + function clear() public returns (uint, uint) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + small[3][2][0] = 0; + large[3][2][0] = 0; + while (small.length > 0) + small.pop(); + while (large.length > 0) + large.pop(); + return (small.length, large.length); } } )"; compileAndRun(sourceCode); - bytes calldata1 = FixedHash<4>(util::keccak256("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); - sendMessage(calldata1, false); - BOOST_CHECK(m_transactionSuccessful); - BOOST_CHECK(m_output == encodeArgs(util::keccak256(bytes{'a', 'b', 'c'} + calldata1))); + ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200)); + // storage is not empty because we cannot delete the mappings + BOOST_CHECK(!storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(call_forward_bytes) -{ - char const* sourceCode = R"( - contract receiver { - uint public received; - function recv(uint x) public { received += x + 1; } - fallback() external { received = 0x80; } - } - contract sender { - constructor() public { rec = new receiver(); } - fallback() external { savedData = msg.data; } - function forward() public returns (bool) { address(rec).call(savedData); return true; } - function clear() public returns (bool) { delete savedData; return true; } - function val() public returns (uint) { return rec.received(); } - receiver rec; - bytes savedData; - } - )"; - compileAndRun(sourceCode, 0, "sender"); - ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes()); - ABI_CHECK(callContractFunction("val()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("forward()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("clear()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("forward()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); -} +//BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) +//{ +// char const* sourceCode = R"( +// contract C { +// uint[3] constant x = [uint(1), 2, 3]; +// uint constant y = x[0] + x[1] + x[2]; +// function f() public returns (uint) { return y; } +// } +// )"; +// compileAndRun(sourceCode); +// ABI_CHECK(callContractFunction("f()"), encodeArgs(1 + 2 + 3)); +//} -BOOST_AUTO_TEST_CASE(call_forward_bytes_length) +// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented +//BOOST_AUTO_TEST_CASE(constant_struct) +//{ +// char const* sourceCode = R"( +// contract C { +// struct S { uint x; uint[] y; } +// S constant x = S(5, new uint[](4)); +// function f() public returns (uint) { return x.x; } +// } +// )"; +// compileAndRun(sourceCode); +// ABI_CHECK(callContractFunction("f()"), encodeArgs(5)); +//} + +BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) { char const* sourceCode = R"( - contract receiver { - uint public calledLength; - fallback() external { calledLength = msg.data.length; } - } - contract sender { - receiver rec; - constructor() public { rec = new receiver(); } - function viaCalldata() public returns (uint) { - (bool success,) = address(rec).call(msg.data); - require(success); - return rec.calledLength(); - } - function viaMemory() public returns (uint) { - bytes memory x = msg.data; - (bool success,) = address(rec).call(x); - require(success); - return rec.calledLength(); - } - bytes s; - function viaStorage() public returns (uint) { - s = msg.data; - (bool success,) = address(rec).call(s); - require(success); - return rec.calledLength(); + contract C { + struct str { uint8 a; uint16 b; uint8 c; } + uint8 x; + uint16 y; + str data; + function test() public returns (uint) { + x = 1; + y = 2; + data.a = 2; + data.b = 0xabcd; + data.c = 0xfa; + if (x != 1 || y != 2 || data.a != 2 || data.b != 0xabcd || data.c != 0xfa) + return 2; + delete y; + delete data.b; + if (x != 1 || y != 0 || data.a != 2 || data.b != 0 || data.c != 0xfa) + return 3; + delete x; + delete data; + return 1; } } )"; - compileAndRun(sourceCode, 0, "sender"); - - // No additional data, just function selector - ABI_CHECK(callContractFunction("viaCalldata()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("viaMemory()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("viaStorage()"), encodeArgs(4)); - - // Some additional unpadded data - bytes unpadded = asBytes(string("abc")); - ABI_CHECK(callContractFunctionNoEncoding("viaCalldata()", unpadded), encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("viaMemory()", unpadded), encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("viaStorage()", unpadded), encodeArgs(7)); + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); + BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) +BOOST_AUTO_TEST_CASE(invalid_enum_logged) { char const* sourceCode = R"( - contract receiver { - uint public received; - function recv(uint x) public { received += x + 1; } - fallback() external { received = 0x80; } - } - contract sender { - constructor() public { rec = new receiver(); } - fallback() external { savedData1 = savedData2 = msg.data; } - function forward(bool selector) public returns (bool) { - if (selector) { address(rec).call(savedData1); delete savedData1; } - else { address(rec).call(savedData2); delete savedData2; } - return true; + contract C { + enum X { A, B } + event Log(X); + + function test_log() public returns (uint) { + X garbled = X.A; + assembly { + garbled := 5 + } + emit Log(garbled); + return 1; + } + function test_log_ok() public returns (uint) { + X x = X.A; + emit Log(x); + return 1; } - function val() public returns (uint) { return rec.received(); } - receiver rec; - bytes savedData1; - bytes savedData2; } - )"; - compileAndRun(sourceCode, 0, "sender"); - ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes()); - ABI_CHECK(callContractFunction("val()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("forward(bool)", true), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("forward(bool)", false), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(16)); - ABI_CHECK(callContractFunction("forward(bool)", true), encodeArgs(true)); - ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test_log_ok()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); + BOOST_REQUIRE_EQUAL(logTopic(0, 0), util::keccak256(string("Log(uint8)"))); + BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(0))); + + // should throw + ABI_CHECK(callContractFunction("test_log()"), encodeArgs()); } -BOOST_AUTO_TEST_CASE(delete_removes_bytes_data) +BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) { char const* sourceCode = R"( - contract c { - fallback() external { data = msg.data; } - function del() public returns (bool) { delete data; return true; } - bytes data; + contract A { + uint public test = 1; + uint[3] arr; + constructor() public + { + uint index = 5; + test = arr[index]; + ++test; + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("---", 7), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A"), encodeArgs()); + BOOST_CHECK(!m_transactionSuccessful); } -BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) +BOOST_AUTO_TEST_CASE(failing_send) { char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data = msg.data; return true; } - fallback() external { data = msg.data; } - bytes data; + contract Helper { + uint[] data; + fallback () external { + data[9]; // trigger exception + } + } + contract Main { + constructor() public payable {} + function callHelper(address payable _a) public returns (bool r, uint bal) { + r = !_a.send(5); + bal = address(this).balance; + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - sendMessage(bytes(), false); - BOOST_CHECK(m_transactionSuccessful); - BOOST_CHECK(m_output.empty()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Helper"); + u160 const c_helperAddress = m_contractAddress; + compileAndRun(sourceCode, 20, "Main"); + BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } -BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) +BOOST_AUTO_TEST_CASE(return_string) { char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data1 = msg.data; return true; } - function reset() public returns (bool) { data1 = data2; return true; } - bytes data1; - bytes data2; + contract Main { + string public s; + function set(string calldata _s) external { + s = _s; + } + function get1() public returns (string memory r) { + return s; + } + function get2() public returns (string memory r) { + r = s; + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("reset()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Main"); + string s("Julia"); + bytes args = encodeArgs(u256(0x20), u256(s.length()), s); + BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs()); + ABI_CHECK(callContractFunction("get1()"), args); + ABI_CHECK(callContractFunction("get2()"), args); + ABI_CHECK(callContractFunction("s()"), args); } -BOOST_AUTO_TEST_CASE(bytes_inside_mappings) +BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes) { char const* sourceCode = R"( - contract c { - function set(uint key) public returns (bool) { data[key] = msg.data; return true; } - function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } - mapping(uint => bytes) data; + contract Main { + string public s1; + string public s2; + function set(string calldata _s1, uint x, string calldata _s2) external returns (uint) { + s1 = _s1; + s2 = _s2; + return x; + } + function get() public returns (string memory r1, string memory r2) { + r1 = s1; + r2 = s2; + } } )"; - compileAndRun(sourceCode); - // store a short byte array at 1 and a longer one at 2 - ABI_CHECK(callContractFunction("set(uint256)", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy shorter to longer - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy empty to both - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Main"); + string s1( + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + ); + string s2( + "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" + "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" + "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" + "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" + "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" + ); + vector lengths{0, 30, 32, 63, 64, 65, 210, 300}; + for (auto l1: lengths) + for (auto l2: lengths) + { + bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); + bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); + bytes args = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2; + BOOST_REQUIRE( + callContractFunction("set(string,uint256,string)", asString(args)) == + encodeArgs(u256(l1)) + ); + bytes result = encodeArgs(u256(0x40), u256(0x40 + dyn1.size())) + dyn1 + dyn2; + ABI_CHECK(callContractFunction("get()"), result); + ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn1); + ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn2); + } } -BOOST_AUTO_TEST_CASE(bytes_length_member) +BOOST_AUTO_TEST_CASE(accessor_involving_strings) { char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data = msg.data; return true; } - function getLength() public returns (uint) { return data.length; } - bytes data; + contract Main { + struct stringData { string a; uint b; string c; } + mapping(uint => stringData[]) public data; + function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) { + while (data[x].length < y + 1) + data[x].push(); + data[x][y].a = a; + data[x][y].b = b; + data[x][y].c = c; + return true; + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getLength()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("set()", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("getLength()"), encodeArgs(4+32+32)); + compileAndRun(sourceCode, 0, "Main"); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"); + bytes s1Data = encodeArgs(u256(s1.length()), s1); + bytes s2Data = encodeArgs(u256(s2.length()), s2); + u256 b = 765; + u256 x = 7; + u256 y = 123; + bytes args = encodeArgs(x, y, u256(0xa0), b, u256(0xa0 + s1Data.size()), s1Data, s2Data); + bytes result = encodeArgs(u256(0x60), b, u256(0x60 + s1Data.size()), s1Data, s2Data); + BOOST_REQUIRE(callContractFunction("set(uint256,uint256,string,uint256,string)", asString(args)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("data(uint256,uint256)", x, y) == result); } -BOOST_AUTO_TEST_CASE(struct_copy) +BOOST_AUTO_TEST_CASE(bytes_in_function_calls) { char const* sourceCode = R"( - contract c { - struct Nested { uint x; uint y; } - struct Struct { uint a; mapping(uint => Struct) b; Nested nested; uint c; } - mapping(uint => Struct) data; - function set(uint k) public returns (bool) { - data[k].a = 1; - data[k].nested.x = 3; - data[k].nested.y = 4; - data[k].c = 2; - return true; + contract Main { + string public s1; + string public s2; + function set(string memory _s1, uint x, string memory _s2) public returns (uint) { + s1 = _s1; + s2 = _s2; + return x; } - function copy(uint from, uint to) public returns (bool) { - data[to] = data[from]; - return true; + function setIndirectFromMemory(string memory _s1, uint x, string memory _s2) public returns (uint) { + return this.set(_s1, x, _s2); } - function retrieve(uint k) public returns (uint a, uint x, uint y, uint c) - { - a = data[k].a; - x = data[k].nested.x; - y = data[k].nested.y; - c = data[k].c; + function setIndirectFromCalldata(string calldata _s1, uint x, string calldata _s2) external returns (uint) { + return this.set(_s1, x, _s2); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set(uint256)", 7), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 0, 7), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(0, 0, 0, 0)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(0, 0, 0, 0)); + compileAndRun(sourceCode, 0, "Main"); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"); + vector lengths{0, 31, 64, 65}; + for (auto l1: lengths) + for (auto l2: lengths) + { + bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); + bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); + bytes args1 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2; + BOOST_REQUIRE( + callContractFunction("setIndirectFromMemory(string,uint256,string)", asString(args1)) == + encodeArgs(u256(l1)) + ); + ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn1); + ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn2); + // swapped + bytes args2 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn2.size())) + dyn2 + dyn1; + BOOST_REQUIRE( + callContractFunction("setIndirectFromCalldata(string,uint256,string)", asString(args2)) == + encodeArgs(u256(l1)) + ); + ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn2); + ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn1); + } } -BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) +BOOST_AUTO_TEST_CASE(return_bytes_internal) { char const* sourceCode = R"( - contract c { - struct Struct { uint a; bytes data; uint b; } - Struct data1; - Struct data2; - function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { - data1.a = _a; - data1.b = _b; - data1.data = _data; - return true; - } - function copy() public returns (bool) { - data1 = data2; - return true; + contract Main { + bytes s1; + function doSet(bytes memory _s1) public returns (bytes memory _r1) { + s1 = _s1; + _r1 = s1; } - function del() public returns (bool) { - delete data1; - return true; + function set(bytes calldata _s1) external returns (uint _r, bytes memory _r1) { + _r1 = doSet(_s1); + _r = _r1.length; } } )"; - compileAndRun(sourceCode); - string data = "123456789012345678901234567890123"; - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Main"); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + vector lengths{0, 31, 64, 65}; + for (auto l1: lengths) + { + bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); + bytes args1 = encodeArgs(u256(0x20)) + dyn1; + BOOST_REQUIRE( + callContractFunction("set(bytes)", asString(args1)) == + encodeArgs(u256(l1), u256(0x40)) + dyn1 + ); + } } -BOOST_AUTO_TEST_CASE(struct_copy_via_local) +BOOST_AUTO_TEST_CASE(bytes_index_access_memory) { char const* sourceCode = R"( - contract c { - struct Struct { uint a; uint b; } - Struct data1; - Struct data2; - function test() public returns (bool) { - data1.a = 1; - data1.b = 2; - Struct memory x = data1; - data2 = x; - return data2.a == data1.a && data2.b == data1.b; + contract Main { + function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { + c1 = _s1[i1]; + c2 = intern(_s1, i2); + c3 = internIndirect(_s1)[i3]; + } + function intern(bytes memory _s1, uint i) public returns (byte c) { + return _s1[i]; + } + function internIndirect(bytes memory _s1) public returns (bytes memory) { + return _s1; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); + compileAndRun(sourceCode, 0, "Main"); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + bytes dyn1 = encodeArgs(u256(s1.length()), s1); + bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1; + BOOST_REQUIRE( + callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) == + encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]}) + ); } -BOOST_AUTO_TEST_CASE(using_enums) +BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker) { char const* sourceCode = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - constructor() public - { - choices = ActionChoices.GoStraight; - } - function getChoice() public returns (uint d) - { - d = uint256(choices); - } - ActionChoices choices; + contract Test { + uint public m_x; + bytes public m_s; + constructor(uint x, bytes memory s) public { + m_x = x; + m_s = s; } + } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getChoice()"), encodeArgs(2)); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + bytes dyn1 = encodeArgs(u256(s1.length()), s1); + u256 x = 7; + bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; + compileAndRun(sourceCode, 0, "Test", args1); + BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x)); + BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1); } -BOOST_AUTO_TEST_CASE(enum_explicit_overflow) +BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) { char const* sourceCode = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight } - constructor() public - { - } - function getChoiceExp(uint x) public returns (uint d) - { - choice = ActionChoices(x); - d = uint256(choice); - } - function getChoiceFromSigned(int x) public returns (uint d) - { - choice = ActionChoices(x); - d = uint256(choice); - } - function getChoiceFromNegativeLiteral() public returns (uint d) - { - choice = ActionChoices(-1); - d = uint256(choice); - } - ActionChoices choice; + contract Base { + uint public m_x; + bytes m_s; + constructor(uint x, bytes memory s) public { + m_x = x; + m_s = s; + } + function part(uint i) public returns (byte) { + return m_s[i]; + } + } + contract Main is Base { + constructor(bytes memory s, uint x) Base(x, f(s)) public {} + function f(bytes memory s) public returns (bytes memory) { + return s; + } + } + contract Creator { + function f(uint x, bytes memory s) public returns (uint r, byte ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); } + } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - // These should throw - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 3), encodeArgs()); - ABI_CHECK(callContractFunction("getChoiceFromSigned(int256)", -1), encodeArgs()); - ABI_CHECK(callContractFunction("getChoiceFromNegativeLiteral()"), encodeArgs()); - // These should work - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 2), encodeArgs(2)); - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 0), encodeArgs(0)); - ) + compileAndRun(sourceCode, 0, "Creator"); + string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); + bytes dyn1 = encodeArgs(u256(s1.length()), s1); + u256 x = 7; + bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; + BOOST_REQUIRE( + callContractFunction("f(uint256,bytes)", asString(args1)) == + encodeArgs(x, string{s1[unsigned(x)]}) + ); } -BOOST_AUTO_TEST_CASE(storing_invalid_boolean) +BOOST_AUTO_TEST_CASE(arrays_in_constructors) { char const* sourceCode = R"( - contract C { - event Ev(bool); - bool public perm; - function set() public returns(uint) { - bool tmp; - assembly { - tmp := 5 - } - perm = tmp; - return 1; - } - function ret() public returns(bool) { - bool tmp; - assembly { - tmp := 5 - } - return tmp; + contract Base { + uint public m_x; + address[] m_s; + constructor(uint x, address[] memory s) public { + m_x = x; + m_s = s; } - function ev() public returns(uint) { - bool tmp; - assembly { - tmp := 5 - } - emit Ev(tmp); - return 1; + function part(uint i) public returns (address) { + return m_s[i]; + } + } + contract Main is Base { + constructor(address[] memory s, uint x) Base(x, f(s)) public {} + function f(address[] memory s) public returns (address[] memory) { + return s; + } + } + contract Creator { + function f(uint x, address[] memory s) public returns (uint r, address ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("perm()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("ret()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("ev()"), encodeArgs(1)); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs(1)); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Ev(bool)"))); + compileAndRun(sourceCode, 0, "Creator"); + vector s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + bytes dyn1 = encodeArgs(u256(s1.size()), s1); + u256 x = 7; + bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; + BOOST_REQUIRE( + callContractFunction("f(uint256,address[])", asString(args1)) == + encodeArgs(x, s1[unsigned(x)]) + ); } - -BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) +BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) { char const* sourceCode = R"( - contract test { - enum Choice { A, B, C } - function answer () public returns (test.Choice _ret) - { - _ret = test.Choice.B; - } + contract Test { + uint24[] public data; + function set(uint24[] memory _data) public returns (uint) { + data = _data; + return data.length; + } + function get() public returns (uint24[] memory) { + return data; } + } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); + compileAndRun(sourceCode, 0, "Test"); + + vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + BOOST_REQUIRE( + callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) == + encodeArgs(u256(data.size())) + ); + ABI_CHECK(callContractFunction("data(uint256)", u256(7)), encodeArgs(u256(8))); + ABI_CHECK(callContractFunction("data(uint256)", u256(15)), encodeArgs(u256(16))); + ABI_CHECK(callContractFunction("data(uint256)", u256(18)), encodeArgs()); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); } -BOOST_AUTO_TEST_CASE(using_inherited_enum) +BOOST_AUTO_TEST_CASE(memory_types_initialisation) { char const* sourceCode = R"( - contract base { - enum Choice { A, B, C } - } - - contract test is base { - function answer () public returns (Choice _ret) - { - _ret = Choice.B; - } + contract Test { + mapping(uint=>uint) data; + function stat() public returns (uint[5] memory) + { + data[2] = 3; // make sure to use some memory } + function dyn() public returns (uint[] memory) { stat(); } + function nested() public returns (uint[3][] memory) { stat(); } + function nestedStat() public returns (uint[3][7] memory) { stat(); } + } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); + compileAndRun(sourceCode, 0, "Test"); + + ABI_CHECK(callContractFunction("stat()"), encodeArgs(vector(5))); + ABI_CHECK(callContractFunction("dyn()"), encodeArgs(u256(0x20), u256(0))); + ABI_CHECK(callContractFunction("nested()"), encodeArgs(u256(0x20), u256(0))); + ABI_CHECK(callContractFunction("nestedStat()"), encodeArgs(vector(3 * 7))); } -BOOST_AUTO_TEST_CASE(using_inherited_enum_excplicitly) +BOOST_AUTO_TEST_CASE(memory_arrays_delete) { char const* sourceCode = R"( - contract base { - enum Choice { A, B, C } - } - - contract test is base { - function answer () public returns (base.Choice _ret) - { - _ret = base.Choice.B; - } + contract Test { + function del() public returns (uint24[3][4] memory) { + uint24[3][4] memory x; + for (uint24 i = 0; i < x.length; i ++) + for (uint24 j = 0; j < x[i].length; j ++) + x[i][j] = i * 0x10 + j; + delete x[1]; + delete x[3][2]; + return x; } + } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); + compileAndRun(sourceCode, 0, "Test"); + + vector data(3 * 4); + for (unsigned i = 0; i < 4; i++) + for (unsigned j = 0; j < 3; j++) + { + u256 v = 0; + if (!(i == 1 || (i == 3 && j == 2))) + v = i * 0x10 + j; + data[i * 3 + j] = v; + } + ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); } -BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) +BOOST_AUTO_TEST_CASE(calldata_struct_short) { char const* sourceCode = R"( - contract c { - enum Truth { False, True } - function test() public returns (uint) - { - return uint(Truth(uint8(0x701))); - } + pragma experimental ABIEncoderV2; + contract C { + struct S { uint256 a; uint256 b; } + function f(S calldata) external pure returns (uint256) { + return msg.data.length; } + } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); - ) + compileAndRun(sourceCode, 0, "C"); + + // double check that the valid case goes through + ABI_CHECK(callContractFunction("f((uint256,uint256))", u256(1), u256(2)), encodeArgs(0x44)); + + ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(63,0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(33,0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(32,0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(31,0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes()), encodeArgs()); } -BOOST_AUTO_TEST_CASE(struct_referencing) +BOOST_AUTO_TEST_CASE(calldata_struct_function_type) { - static char const* sourceCode = R"( + char const* sourceCode = R"( pragma experimental ABIEncoderV2; - interface I { - struct S { uint a; } - } - library L { - struct S { uint b; uint a; } - function f() public pure returns (S memory) { - S memory s; - s.a = 3; - return s; - } - function g() public pure returns (I.S memory) { - I.S memory s; - s.a = 4; - return s; - } - // argument-dependant lookup tests - function a(I.S memory) public pure returns (uint) { return 1; } - function a(S memory) public pure returns (uint) { return 2; } - } - contract C is I { - function f() public pure returns (S memory) { - S memory s; - s.a = 1; - return s; - } - function g() public pure returns (I.S memory) { - I.S memory s; - s.a = 2; - return s; - } - function h() public pure returns (L.S memory) { - L.S memory s; - s.a = 5; - return s; + contract C { + struct S { function (uint) external returns (uint) fn; } + function f(S calldata s) external returns (uint256) { + return s.fn(42); } - function x() public pure returns (L.S memory) { - return L.f(); + function g(uint256 a) external returns (uint256) { + return a * 3; } - function y() public pure returns (I.S memory) { - return L.g(); + function h(uint256 a) external returns (uint256) { + return 23; } - function a1() public pure returns (uint) { S memory s; return L.a(s); } - function a2() public pure returns (uint) { L.S memory s; return L.a(s); } } )"; - compileAndRun(sourceCode, 0, "L"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 3)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(4)); - compileAndRun(sourceCode, 0, "C", bytes(), map{ {"L", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 5)); - ABI_CHECK(callContractFunction("x()"), encodeArgs(0, 3)); - ABI_CHECK(callContractFunction("y()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("a1()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("a2()"), encodeArgs(2)); + compileAndRun(sourceCode, 0, "C"); + + bytes fn_C_g = m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + bytes(8,0); + bytes fn_C_h = m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("h(uint256)")).asBytes() + bytes(8,0); + ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_g), encodeArgs(42 * 3)); + ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(enum_referencing) +BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) { char const* sourceCode = R"( - interface I { - enum Direction { A, B, Left, Right } - } - library L { - enum Direction { Left, Right } - function f() public pure returns (Direction) { - return Direction.Right; - } - function g() public pure returns (I.Direction) { - return I.Direction.Right; - } - } - contract C is I { - function f() public pure returns (Direction) { - return Direction.Right; - } - function g() public pure returns (I.Direction) { - return I.Direction.Right; - } - function h() public pure returns (L.Direction) { - return L.Direction.Right; - } - function x() public pure returns (L.Direction) { - return L.f(); - } - function y() public pure returns (I.Direction) { - return L.g(); + pragma experimental ABIEncoderV2; + contract C { + function f(bytes[] calldata a, uint256 i) external returns (uint) { + return uint8(a[0][i]); } } )"; - compileAndRun(sourceCode, 0, "L"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("x()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "C"); + + ABI_CHECK( + callContractFunction("f(bytes[],uint256)", 0x40, 0, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), + encodeArgs('a') + ); + ABI_CHECK( + callContractFunction("f(bytes[],uint256)", 0x40, 1, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), + encodeArgs('b') + ); + ABI_CHECK( + callContractFunction("f(bytes[],uint256)", 0x40, 2, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), + encodeArgs() + ); } -BOOST_AUTO_TEST_CASE(inline_member_init) +BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) { - char const* sourceCode = R"( - contract test { - constructor() public { - m_b = 6; - m_c = 8; + vector> data { + { 0x0A01, 0x0A02, 0x0A03 }, + { 0x0B01, 0x0B02, 0x0B03, 0x0B04 } + }; + + for (bool outerDynamicallySized: { true, false }) + { + string arrayType = outerDynamicallySized ? "uint256[][]" : "uint256[][2]"; + string sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + function test()" + arrayType + R"( calldata a) external returns (uint256) { + return a.length; + } + function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j]; + } + function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } } - uint m_a = 5; - uint m_b; - uint m_c = 7; - function get() public returns (uint a, uint b, uint c){ - a = m_a; - b = m_b; - c = m_c; + )"; + compileAndRun(sourceCode, 0, "C"); + + bytes encoding = encodeArray( + outerDynamicallySized, + true, + data | boost::adaptors::transformed([&](vector const& _values) { + return encodeArray(true, false, _values); + }) + ); + + ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); + for (size_t i = 0; i < data.size(); i++) + { + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); + for (size_t j = 0; j < data[i].size(); j++) + { + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); + ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); } + // out of bounds access + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get()"), encodeArgs(5, 6, 8)); -} -BOOST_AUTO_TEST_CASE(inline_member_init_inheritence) -{ - char const* sourceCode = R"( - contract Base { - constructor() public {} - uint m_base = 5; - function getBMember() public returns (uint i) { return m_base; } - } - contract Derived is Base { - constructor() public {} - uint m_derived = 6; - function getDMember() public returns (uint i) { return m_derived; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); - ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); + // out of bounds access + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); + } } -BOOST_AUTO_TEST_CASE(inline_member_init_inheritence_without_constructor) +BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) { - char const* sourceCode = R"( - contract Base { - uint m_base = 5; - function getBMember() public returns (uint i) { return m_base; } - } - contract Derived is Base { - uint m_derived = 6; - function getDMember() public returns (uint i) { return m_derived; } + vector>> data { + { + { 0x010A01, 0x010A02, 0x010A03 }, + { 0x010B01, 0x010B02, 0x010B03 } + }, + { + { 0x020A01, 0x020A02, 0x020A03 }, + { 0x020B01, 0x020B02, 0x020B03 } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); - ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); -} + }; -BOOST_AUTO_TEST_CASE(external_function) -{ - char const* sourceCode = R"( - contract c { - function f(uint a) public returns (uint) { return a; } - function test(uint a, uint b) external returns (uint r_a, uint r_b) { - r_a = f(a + 7); - r_b = b; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test(uint256,uint256)", 2, 3), encodeArgs(2+7, 3)); - ) -} + for (bool outerDynamicallySized: { true, false }) + for (bool middleDynamicallySized: { true, false }) + for (bool innerDynamicallySized: { true, false }) + { + // only test dynamically encoded arrays + if (!outerDynamicallySized && !middleDynamicallySized && !innerDynamicallySized) + continue; -BOOST_AUTO_TEST_CASE(bytes_in_arguments) -{ - char const* sourceCode = R"( - contract c { - uint result; - function f(uint a, uint b) public { result += a + b; } - function g(uint a) public { result *= a; } - function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { - r_a = a; - address(this).call(data1); - address(this).call(data2); - r = result; - r_b = b; - l = data1.length; + string arrayType = "uint256"; + arrayType += innerDynamicallySized ? "[]" : "[3]"; + arrayType += middleDynamicallySized ? "[]" : "[2]"; + arrayType += outerDynamicallySized ? "[]" : "[2]"; + + string sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + function test()" + arrayType + R"( calldata a) external returns (uint256) { + return a.length; + } + function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j].length; + } + function test()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { + return a[i][j][k]; + } + function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { + return this.test(a, i, j, k); + } } - } - )"; - compileAndRun(sourceCode); + )"; + compileAndRun(sourceCode, 0, "C"); - string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); - string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); - bytes calldata = encodeArgs( - 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, - u256(innercalldata1.length()), innercalldata1, - u256(innercalldata2.length()), innercalldata2); - ABI_CHECK( - callContractFunction("test(uint256,bytes,bytes,uint256)", calldata), - encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length())) - ); -} + bytes encoding = encodeArray( + outerDynamicallySized, + middleDynamicallySized || innerDynamicallySized, + data | boost::adaptors::transformed([&](auto const& _middleData) { + return encodeArray( + middleDynamicallySized, + innerDynamicallySized, + _middleData | boost::adaptors::transformed([&](auto const& _values) { + return encodeArray(innerDynamicallySized, false, _values); + }) + ); + }) + ); -BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[2**10] data; - uint[2**10 + 3] ids; - function setIDStatic(uint id) public { ids[2] = id; } - function setID(uint index, uint id) public { ids[index] = id; } - function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } - function getID(uint index) public returns (uint) { return ids[index]; } - function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } + ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); + for (size_t i = 0; i < data.size(); i++) + { + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); + for (size_t j = 0; j < data[i].size(); j++) + { + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j].size())); + for (size_t k = 0; k < data[i][j].size(); k++) + { + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); + ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); + } + // out of bounds access + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs()); + } + // out of bounds access + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setIDStatic(uint256)", 11), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 2), encodeArgs(11)); - ABI_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 7), encodeArgs(8)); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9), bytes()); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11), bytes()); - ABI_CHECK(callContractFunction("getData(uint256)", 7), encodeArgs(8, 9)); - ABI_CHECK(callContractFunction("getData(uint256)", 8), encodeArgs(10, 11)); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(u256(1) << 10, (u256(1) << 10) + 3)); + // out of bounds access + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); + } } -BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) +BOOST_AUTO_TEST_CASE(literal_strings) { char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[] data; - uint[] ids; - function setIDStatic(uint id) public { ids[2] = id; } - function setID(uint index, uint id) public { ids[index] = id; } - function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } - function getID(uint index) public returns (uint) { return ids[index]; } - function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } - function setLengths(uint l1, uint l2) public { - while (data.length < l1) - data.push(); - while (ids.length < l2) - ids.push(); + contract Test { + string public long; + string public medium; + string public short; + string public empty; + function f() public returns (string memory) { + long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + short = "123"; + empty = ""; + return "Hello, World!"; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(0, 0)); - ABI_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49), bytes()); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(48, 49)); - ABI_CHECK(callContractFunction("setIDStatic(uint256)", 11), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 2), encodeArgs(11)); - ABI_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 7), encodeArgs(8)); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9), bytes()); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11), bytes()); - ABI_CHECK(callContractFunction("getData(uint256)", 7), encodeArgs(8, 9)); - ABI_CHECK(callContractFunction("getData(uint256)", 8), encodeArgs(10, 11)); + compileAndRun(sourceCode, 0, "Test"); + string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + string medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + string shortStr = "123"; + string hello = "Hello, World!"; + + ABI_CHECK(callContractFunction("f()"), encodeDyn(hello)); + ABI_CHECK(callContractFunction("long()"), encodeDyn(longStr)); + ABI_CHECK(callContractFunction("medium()"), encodeDyn(medium)); + ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); + ABI_CHECK(callContractFunction("empty()"), encodeDyn(string())); } -BOOST_AUTO_TEST_CASE(fixed_out_of_bounds_array_access) +BOOST_AUTO_TEST_CASE(initialise_string_constant) { char const* sourceCode = R"( - contract c { - uint[4] data; - function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } - function get(uint index) public returns (uint) { return data[index]; } - function length() public returns (uint) { return data.length; } + contract Test { + string public short = "abcdef"; + string public long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 5), bytes()); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 400, 5), bytes()); - ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4)); - ABI_CHECK(callContractFunction("get(uint256)", 4), bytes()); - ABI_CHECK(callContractFunction("get(uint256)", 400), bytes()); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ) + compileAndRun(sourceCode, 0, "Test"); + string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + string shortStr = "abcdef"; + + ABI_CHECK(callContractFunction("long()"), encodeDyn(longStr)); + ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); } -BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access) +BOOST_AUTO_TEST_CASE(string_as_mapping_key) { char const* sourceCode = R"( - contract c { - uint[] data; - function enlarge(uint amount) public returns (uint) { - while (data.length < amount) - data.push(); - return data.length; - } - function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } - function get(uint index) public returns (uint) { return data[index]; } - function length() public returns (uint) { return data.length; } + contract Test { + mapping(string => uint) data; + function set(string memory _s, uint _v) public { data[_s] = _v; } + function get(string memory _s) public returns (uint) { return data[_s]; } } )"; + + vector strings{ + "Hello, World!", + "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", + "", + "1" + }; + ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("length()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("get(uint256)", 3), bytes()); - ABI_CHECK(callContractFunction("enlarge(uint256)", 4), encodeArgs(4)); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true)); - ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4)); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes()); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ); + compileAndRun(sourceCode, 0, "Test"); + for (unsigned i = 0; i < strings.size(); i++) + ABI_CHECK(callContractFunction( + "set(string,uint256)", + u256(0x40), + u256(7 + i), + u256(strings[i].size()), + strings[i] + ), encodeArgs()); + for (unsigned i = 0; i < strings.size(); i++) + ABI_CHECK(callContractFunction( + "get(string)", + u256(0x20), + u256(strings[i].size()), + strings[i] + ), encodeArgs(u256(7 + i))); + ) } -BOOST_AUTO_TEST_CASE(fixed_array_cleanup) +BOOST_AUTO_TEST_CASE(string_as_public_mapping_key) { char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[20] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } + contract Test { + mapping(string => uint) public data; + function set(string memory _s, uint _v) public { data[_s] = _v; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); + compileAndRun(sourceCode, 0, "Test"); + vector strings{ + "Hello, World!", + "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", + "", + "1" + }; + for (unsigned i = 0; i < strings.size(); i++) + ABI_CHECK(callContractFunction( + "set(string,uint256)", + u256(0x40), + u256(7 + i), + u256(strings[i].size()), + strings[i] + ), encodeArgs()); + for (unsigned i = 0; i < strings.size(); i++) + ABI_CHECK(callContractFunction( + "data(string)", + u256(0x20), + u256(strings[i].size()), + strings[i] + ), encodeArgs(u256(7 + i))); } -BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) +BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key) { char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[3] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } + contract Test { + mapping(string => mapping(string => uint)) public data; + function set(string memory _s, string memory _s2, uint _v) public { + data[_s][_s2] = _v; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); + compileAndRun(sourceCode, 0, "Test"); + vector strings{ + "Hello, World!", + "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", + "", + "1", + "last one" + }; + for (unsigned i = 0; i + 1 < strings.size(); i++) + ABI_CHECK(callContractFunction( + "set(string,string,uint256)", + u256(0x60), + u256(roundTo32(0x80 + strings[i].size())), + u256(7 + i), + u256(strings[i].size()), + strings[i], + u256(strings[i+1].size()), + strings[i+1] + ), encodeArgs()); + for (unsigned i = 0; i + 1 < strings.size(); i++) + ABI_CHECK(callContractFunction( + "data(string,string)", + u256(0x40), + u256(roundTo32(0x60 + strings[i].size())), + u256(strings[i].size()), + strings[i], + u256(strings[i+1].size()), + strings[i+1] + ), encodeArgs(u256(7 + i))); } -BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) +BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) { char const* sourceCode = R"( - contract c { - uint[20] spacer; - uint[] dynamic; - function fill() public { - for (uint i = 0; i < 21; ++i) - dynamic.push(i + 1); - } - function halfClear() public { - while (dynamic.length > 5) - dynamic.pop(); + contract Test { + mapping(string => + mapping(int => + mapping(address => + mapping(bytes => int)))) public data; + + function set( + string memory _s1, + int _s2, + address _s3, + bytes memory _s4, + int _value + ) public + { + data[_s1][_s2][_s3][_s4] = _value; } - function fullClear() public { delete dynamic; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("halfClear()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fullClear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); + compileAndRun(sourceCode, 0, "Test"); + + struct Index + { + string s1; + int s2; + int s3; + string s4; + }; + + vector data{ + { "aabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcbc", 4, 23, "efg" }, + { "tiaron", 456, 63245, "908apzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapz" }, + { "", 2345, 12934, "665i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i5iart" }, + { "¡¿…", 9781, 8148, "" }, + { "ρν♀♀ω₂₃♀", 929608, 303030, "" } + }; + + for (size_t i = 0; i + 1 < data.size(); i++) + ABI_CHECK(callContractFunction( + "set(string,int256,address,bytes,int256)", + u256(0xA0), + u256(data[i].s2), + u256(data[i].s3), + u256(roundTo32(0xC0 + data[i].s1.size())), + u256(i - 3), + u256(data[i].s1.size()), + data[i].s1, + u256(data[i].s4.size()), + data[i].s4 + ), encodeArgs()); + for (size_t i = 0; i + 1 < data.size(); i++) + ABI_CHECK(callContractFunction( + "data(string,int256,address,bytes)", + u256(0x80), + u256(data[i].s2), + u256(data[i].s3), + u256(roundTo32(0xA0 + data[i].s1.size())), + u256(data[i].s1.size()), + data[i].s1, + u256(data[i].s4.size()), + data[i].s4 + ), encodeArgs(u256(i - 3))); } -BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) +BOOST_AUTO_TEST_CASE(constant_string_literal) { char const* sourceCode = R"( - contract c { - struct s { uint[][] d; } - s[] data; - function fill() public returns (uint) { - while (data.length < 3) - data.push(); - while (data[2].d.length < 4) - data[2].d.push(); - while (data[2].d[3].length < 5) - data[2].d[3].push(); - data[2].d[3][4] = 8; - return data[2].d[3][4]; + contract Test { + bytes32 constant public b = "abcdefghijklmnopq"; + string constant public x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; + + constructor() public { + string memory xx = x; + bytes32 bb = b; + } + function getB() public returns (bytes32) { return b; } + function getX() public returns (string memory) { return x; } + function getX2() public returns (string memory r) { r = x; } + function unused() public returns (uint) { + "unusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunused"; + return 2; } - function clear() public { delete data; } } )"; + compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), encodeArgs(8)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + string longStr = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; + string shortStr = "abcdefghijklmnopq"; + ABI_CHECK(callContractFunction("b()"), encodeArgs(shortStr)); + ABI_CHECK(callContractFunction("x()"), encodeDyn(longStr)); + ABI_CHECK(callContractFunction("getB()"), encodeArgs(shortStr)); + ABI_CHECK(callContractFunction("getX()"), encodeDyn(longStr)); + ABI_CHECK(callContractFunction("getX2()"), encodeDyn(longStr)); + ABI_CHECK(callContractFunction("unused()"), encodeArgs(2)); } -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) +BOOST_AUTO_TEST_CASE(library_call) { char const* sourceCode = R"( - contract c { - uint[] data1; - uint[] data2; - function setData1(uint length, uint index, uint value) public { - data1 = new uint[](length); - if (index < length) - data1[index] = value; - } - function copyStorageStorage() public { data2 = data1; } - function getData2(uint index) public returns (uint len, uint val) { - len = data2.length; if (index < len) val = data2[index]; + library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } + contract Test { + function f(uint x) public returns (uint) { + return Lib.m(x, 9); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 5), encodeArgs(10, 4)); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 0), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9)); } -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_static) +BOOST_AUTO_TEST_CASE(library_function_external) { char const* sourceCode = R"( - contract c { - uint[40] data1; - uint[20] data2; - function test() public returns (uint x, uint y){ - data1[30] = 4; - data1[2] = 7; - data1[3] = 9; - data2[3] = 8; - data1 = data2; - x = data1[3]; - y = data1[30]; // should be cleared + library Lib { function m(bytes calldata b) external pure returns (byte) { return b[2]; } } + contract Test { + function f(bytes memory b) public pure returns (byte) { + return Lib.m(b); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(8, 0)); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c")); } -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_dynamic) +BOOST_AUTO_TEST_CASE(library_stray_values) { char const* sourceCode = R"( - contract c { - uint[9] data1; - uint[] data2; - function test() public returns (uint x, uint y){ - data1[8] = 4; - data2 = data1; - x = data2.length; - y = data2[8]; + library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } + contract Test { + function f(uint x) public returns (uint) { + Lib; + Lib.m; + return x + 9; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(9, 4)); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(42))); } -BOOST_AUTO_TEST_CASE(array_copy_different_packing) +BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( - contract c { - bytes8[] data1; // 4 per slot - bytes10[] data2; // 3 per slot - function test() public returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) { - data1 = new bytes8[](9); - for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(uint64(i)); - data2 = data1; - a = data2[1]; - b = data2[2]; - c = data2[3]; - d = data2[4]; - e = data2[5]; + library Lib { + function find(uint16[] storage _haystack, uint16 _needle) public view returns (uint) + { + for (uint i = 0; i < _haystack.length; ++i) + if (_haystack[i] == _needle) + return i; + return uint(-1); } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000001")), - asString(fromHex("0000000000000002")), - asString(fromHex("0000000000000003")), - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000005")) - )); -} - -BOOST_AUTO_TEST_CASE(array_copy_target_simple) -{ - char const* sourceCode = R"( - contract c { - bytes8[9] data1; // 4 per slot - bytes17[10] data2; // 1 per slot, no offset counter - function test() public returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) { - for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(uint64(i)); - data2[8] = data2[9] = bytes8(uint64(2)); - data2 = data1; - a = data2[1]; - b = data2[2]; - c = data2[3]; - d = data2[4]; - e = data2[9]; + contract Test { + mapping(string => uint16[]) data; + function f() public returns (uint a, uint b) + { + while (data["abc"].length < 20) + data["abc"].push(); + data["abc"][4] = 9; + data["abc"][17] = 3; + a = Lib.find(data["abc"], 9); + b = Lib.find(data["abc"], 3); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000001")), - asString(fromHex("0000000000000002")), - asString(fromHex("0000000000000003")), - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000000")) - )); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4), u256(17))); } -BOOST_AUTO_TEST_CASE(array_copy_target_leftover) +BOOST_AUTO_TEST_CASE(mapping_arguments_in_library) { - // test that leftover elements in the last slot of target are correctly cleared during assignment char const* sourceCode = R"( - contract c { - byte[10] data1; - bytes2[32] data2; - function test() public returns (uint check, uint res1, uint res2) { - uint i; - for (i = 0; i < data2.length; ++i) - data2[i] = 0xffff; - check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); - for (i = 0; i < data1.length; ++i) - data1[i] = byte(uint8(1 + i)); - data2 = data1; - for (i = 0; i < 16; ++i) - res1 |= uint(uint16(data2[i])) * 0x10000**i; - for (i = 0; i < 16; ++i) - res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) internal + { + m[key] = value; + } + function get(mapping(uint => uint) storage m, uint key) internal view returns (uint) + { + return m[key]; + } + } + contract Test { + mapping(uint => uint) m; + function set(uint256 key, uint256 value) public returns (uint) + { + uint oldValue = Lib.get(m, key); + Lib.set(m, key, value); + return oldValue; + } + function get(uint256 key) public view returns (uint) { + return Lib.get(m, key); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256("0xffffffff"), - asString(fromHex("0000000000000000""000000000a000900""0800070006000500""0400030002000100")), - asString(fromHex("0000000000000000""0000000000000000""0000000000000000""0000000000000000")) - )); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(42)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(84)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(7)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(21)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(42)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(14)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(14))); } -BOOST_AUTO_TEST_CASE(array_copy_target_leftover2) +BOOST_AUTO_TEST_CASE(mapping_returns_in_library) { - // since the copy always copies whole slots, we have to make sure that the source size maxes - // out a whole slot and at the same time there are still elements left in the target at that point char const* sourceCode = R"( - contract c { - bytes8[4] data1; // fits into one slot - bytes10[6] data2; // 4 elements need two slots - function test() public returns (bytes10 r1, bytes10 r2, bytes10 r3) { - data1[0] = bytes8(uint64(1)); - data1[1] = bytes8(uint64(2)); - data1[2] = bytes8(uint64(3)); - data1[3] = bytes8(uint64(4)); - for (uint i = 0; i < data2.length; ++i) - data2[i] = bytes10(uint80(0xffff00 | (1 + i))); - data2 = data1; - r1 = data2[3]; - r2 = data2[4]; - r3 = data2[5]; + library Lib { + function choose_mapping(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) internal pure returns(mapping(uint=>uint) storage) + { + return c ? a : b; + } + } + contract Test { + mapping(uint => uint) a; + mapping(uint => uint) b; + function set(bool choice, uint256 key, uint256 value) public returns (uint) + { + mapping(uint => uint) storage m = Lib.choose_mapping(a, b, choice); + uint oldValue = m[key]; + m[key] = value; + return oldValue; + } + function get(bool choice, uint256 key) public view returns (uint) { + return Lib.choose_mapping(a, b, choice)[key]; + } + function get_a(uint256 key) public view returns (uint) { + return a[key]; + } + function get_b(uint256 key) public view returns (uint) { + return b[key]; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000000")), - asString(fromHex("0000000000000000")) - )); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(42)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(84)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(7)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(10)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(11)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(12)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(21)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(42)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(14)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(30)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(31)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(32)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(14))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(14))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(30))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(31))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(32))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(30))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(31))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(32))); } -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) +BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named) { char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[] data1; - Data[] data2; - function test() public returns (uint x, uint y) { - while (data1.length < 9) - data1.push(); - data1[8].x = 4; - data1[8].y = 5; - data2 = data1; - x = data2[8].x; - y = data2[8].y; - while (data1.length > 0) - data1.pop(); - data2 = data1; + library Lib { + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b) internal returns(mapping(uint=>uint) storage r) + { + r = a; + r[1] = 42; + r = b; + r[1] = 21; + } + } + contract Test { + mapping(uint => uint) a; + mapping(uint => uint) b; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.f(a, b)[2] = 84; + return (a[0], a[1], a[2], b[0], b[1], b[2]); + } + function g() public returns (uint, uint, uint, uint, uint, uint) + { + mapping(uint => uint) storage m = Lib.f(a, b); + m[2] = 17; + return (a[0], a[1], a[2], b[0], b[1], b[2]); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(4, 5)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(84))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17))); } -BOOST_AUTO_TEST_CASE(array_copy_storage_abi) +BOOST_AUTO_TEST_CASE(using_library_mappings_public) { - // NOTE: This does not really test copying from storage to ABI directly, - // because it will always copy to memory first. char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract c { - uint8[] x; - uint16[] y; - uint24[] z; - uint24[][] w; - function test1() public returns (uint8[] memory) { - for (uint i = 0; i < 101; ++i) - x.push(uint8(i)); - return x; - } - function test2() public returns (uint16[] memory) { - for (uint i = 0; i < 101; ++i) - y.push(uint16(i)); - return y; - } - function test3() public returns (uint24[] memory) { - for (uint i = 0; i < 101; ++i) - z.push(uint24(i)); - return z; - } - function test4() public returns (uint24[][] memory) { - w = new uint24[][](5); - for (uint i = 0; i < 5; ++i) - for (uint j = 0; j < 101; ++j) - w[i].push(uint24(j)); - return w; + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) public + { + m[key] = value; + } } - } - )"; - compileAndRun(sourceCode); - bytes valueSequence; - for (size_t i = 0; i < 101; ++i) - valueSequence += toBigEndian(u256(i)); - ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test4()"), - encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence - ); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed) -{ - // NOTE: This does not really test copying from storage to ABI directly, - // because it will always copy to memory first. - char const* sourceCode = R"( - contract c { - int16[] x; - function test() public returns (int16[] memory) { - x.push(int16(-1)); - x.push(int16(-1)); - x.push(int16(8)); - x.push(int16(-16)); - x.push(int16(-2)); - x.push(int16(6)); - x.push(int16(8)); - x.push(int16(-1)); - return x; + contract Test { + mapping(uint => uint) m1; + mapping(uint => uint) m2; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.set(m1, 0, 1); + Lib.set(m1, 2, 42); + Lib.set(m2, 0, 23); + Lib.set(m2, 2, 99); + return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); + } } - } - )"; - compileAndRun(sourceCode); - bytes valueSequence; - ABI_CHECK(callContractFunction("test()"), encodeArgs(0x20, 8, - u256(-1), - u256(-1), - u256(8), - u256(-16), - u256(-2), - u256(6), - u256(8), - u256(-1) - )); + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); } -BOOST_AUTO_TEST_CASE(array_push) +BOOST_AUTO_TEST_CASE(using_library_mappings_external) { - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (uint x, uint y, uint z, uint l) { - data.push(5); - x = data[0]; - data.push(4); - y = data[1]; - data.push(3); - l = data.length; - z = data[2]; + char const* libSourceCode = R"( + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) external + { + m[key] = value * 2; + } } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3)); -} - -BOOST_AUTO_TEST_CASE(array_push_struct) -{ + )"; char const* sourceCode = R"( - contract c { - struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; } - S[] data; - function test() public returns (uint16, uint16, uint16, uint16) { - S memory s; - s.a = 2; - s.b = 3; - s.c[2] = 4; - s.d = new uint16[](4); - s.d[2] = 5; - data.push(s); - return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) external {} } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5)); -} - -BOOST_AUTO_TEST_CASE(array_push_packed_array) -{ - char const* sourceCode = R"( - contract c { - uint80[] x; - function test() public returns (uint80, uint80, uint80, uint80) { - x.push(1); - x.push(2); - x.push(3); - x.push(4); - x.push(5); - x.pop(); - return (x[0], x[1], x[2], x[3]); + contract Test { + mapping(uint => uint) m1; + mapping(uint => uint) m2; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.set(m1, 0, 1); + Lib.set(m1, 2, 42); + Lib.set(m2, 0, 23); + Lib.set(m2, 2, 99); + return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); + } } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4)); + )"; + for (auto v2: {false, true}) + { + string prefix = v2 ? "pragma experimental ABIEncoderV2;\n" : ""; + compileAndRun(prefix + libSourceCode, 0, "Lib"); + compileAndRun(prefix + sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2), u256(0), u256(84), u256(46), u256(0), u256(198))); + } } -BOOST_AUTO_TEST_CASE(byte_array_push) +BOOST_AUTO_TEST_CASE(using_library_mappings_return) { char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bool x) { - data.push(0x05); - if (data.length != 1) return true; - if (data[0] != 0x05) return true; - data.push(0x04); - if (data[1] != 0x04) return true; - data.push(0x03); - uint l = data.length; - if (data[2] != 0x03) return true; - if (l != 0x03) return true; + library Lib { + function choose(mapping(uint => mapping(uint => uint)) storage m, uint key) external returns (mapping(uint => uint) storage) { + return m[key]; + } } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); -} - -BOOST_AUTO_TEST_CASE(byte_array_push_transition) -{ - // Tests transition between short and long encoding - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (uint) { - for (uint8 i = 1; i < 40; i++) + contract Test { + mapping(uint => mapping(uint => uint)) m; + function f() public returns (uint, uint, uint, uint, uint, uint) { - data.push(byte(i)); - if (data.length != i) return 0x1000 + i; - if (data[data.length - 1] != byte(i)) return i; + Lib.choose(m, 0)[0] = 1; + Lib.choose(m, 0)[2] = 42; + Lib.choose(m, 1)[0] = 23; + Lib.choose(m, 1)[2] = 99; + return (m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]); } - for (uint8 i = 1; i < 40; i++) - if (data[i - 1] != byte(i)) return 0x1000000 + i; - return 0; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(array_pop) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (uint x, uint l) { - data.push(7); - data.push(3); - x = data.length; - data.pop(); - x = data.length; - data.pop(); - l = data.length; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 0)); + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); } -BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) +BOOST_AUTO_TEST_CASE(using_library_structs) { char const* sourceCode = R"( - contract c { - uint16[] data; - function test() public returns (uint16 x, uint16 y, uint16 z) { - for (uint i = 1; i <= 48; i++) - data.push(uint16(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1]; - for (uint m = 1; m <= 18; m++) - data.pop(); + library Lib { + struct Data { uint a; uint[] b; } + function set(Data storage _s) public + { + _s.a = 7; + while (_s.b.length < 20) + _s.b.push(); + _s.b[19] = 8; } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_pop_uint24_transition) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint24[] data; - function test() public returns (uint24 x, uint24 y) { - for (uint i = 1; i <= 30; i++) - data.push(uint24(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); + contract Test { + mapping(string => Lib.Data) data; + function f() public returns (uint a, uint b) + { + Lib.set(data["abc"]); + a = data["abc"].a; + b = data["abc"].b[19]; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), u256(8))); } -BOOST_AUTO_TEST_CASE(array_pop_array_transition) +BOOST_AUTO_TEST_CASE(short_strings) { + // This test verifies that the byte array encoding that combines length and data works + // correctly. char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - uint16[][] data; - function test() public returns (uint x, uint y, uint z) { - for (uint i = 1; i <= 48; i++) - data.push(inner); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1][0]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1][1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1][2]; - for (uint m = 1; m <= 18; m++) - data.pop(); - delete inner; + contract A { + bytes public data1 = "123"; + bytes data2; + function lengthChange() public returns (uint) + { + // store constant in short and long string + data1 = "123"; + if (!equal(data1, "123")) return 1; + data2 = "12345678901234567890123456789012345678901234567890a"; + if (data2[17] != "8") return 3; + if (data2.length != 51) return 4; + if (data2[data2.length - 1] != "a") return 5; + // change length: short -> short + while (data1.length < 5) + data1.push(); + if (data1.length != 5) return 6; + data1[4] = "4"; + if (data1[0] != "1") return 7; + if (data1[4] != "4") return 8; + // change length: short -> long + while (data1.length < 80) + data1.push(); + if (data1.length != 80) return 9; + while (data1.length > 70) + data1.pop(); + if (data1.length != 70) return 9; + if (data1[0] != "1") return 10; + if (data1[4] != "4") return 11; + for (uint i = 0; i < data1.length; i ++) + data1[i] = byte(uint8(i * 3)); + if (uint8(data1[4]) != 4 * 3) return 12; + if (uint8(data1[67]) != 67 * 3) return 13; + // change length: long -> short + while (data1.length > 22) + data1.pop(); + if (data1.length != 22) return 14; + if (uint8(data1[21]) != 21 * 3) return 15; + if (uint8(data1[2]) != 2 * 3) return 16; + // change length: short -> shorter + while (data1.length > 19) + data1.pop(); + if (data1.length != 19) return 17; + if (uint8(data1[7]) != 7 * 3) return 18; + // and now again to original size + while (data1.length < 22) + data1.push(); + if (data1.length != 22) return 19; + if (data1[21] != 0) return 20; + while (data1.length > 0) + data1.pop(); + while (data2.length > 0) + data2.pop(); + } + function copy() public returns (uint) { + bytes memory x = "123"; + bytes memory y = "012345678901234567890123456789012345678901234567890123456789"; + bytes memory z = "1234567"; + data1 = x; + data2 = y; + if (!equal(data1, x)) return 1; + if (!equal(data2, y)) return 2; + // lengthen + data1 = y; + if (!equal(data1, y)) return 3; + // shorten + data1 = x; + if (!equal(data1, x)) return 4; + // change while keeping short + data1 = z; + if (!equal(data1, z)) return 5; + // copy storage -> storage + data1 = x; + data2 = y; + // lengthen + data1 = data2; + if (!equal(data1, y)) return 6; + // shorten + data1 = x; + data2 = data1; + if (!equal(data2, x)) return 7; + bytes memory c = data2; + data1 = c; + if (!equal(data1, x)) return 8; + data1 = ""; + data2 = ""; + } + function deleteElements() public returns (uint) { + data1 = "01234"; + delete data1[2]; + if (data1[2] != 0) return 1; + if (data1[0] != "0") return 2; + if (data1[3] != "3") return 3; + delete data1; + if (data1.length != 0) return 4; + } + + function equal(bytes storage a, bytes memory b) internal returns (bool) { + if (a.length != b.length) return false; + for (uint i = 0; i < a.length; ++i) if (a[i] != b[i]) return false; + return true; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3)); + compileAndRun(sourceCode, 0, "A"); + ABI_CHECK(callContractFunction("data1()"), encodeDyn(string("123"))); + ABI_CHECK(callContractFunction("lengthChange()"), encodeArgs(u256(0))); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("deleteElements()"), encodeArgs(u256(0))); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ABI_CHECK(callContractFunction("copy()"), encodeArgs(u256(0))); BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(array_pop_empty_exception) +BOOST_AUTO_TEST_CASE(calldata_offset) { + // This tests a specific bug that was caused by not using the correct memory offset in the + // calldata unpacker. char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (bool) { - data.pop(); - return true; + contract CB + { + address[] _arr; + string public last = "nd"; + constructor(address[] memory guardians) public + { + _arr = guardians; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); + compileAndRun(sourceCode, 0, "CB", encodeArgs(u256(0x20), u256(0x00))); + ABI_CHECK(callContractFunction("last()", encodeArgs()), encodeDyn(string("nd"))); } -BOOST_AUTO_TEST_CASE(array_pop_storage_empty) +BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) { char const* sourceCode = R"( + library lib {} contract c { - uint[] data; - function test() public { - data.push(7); - data.pop(); + constructor() public payable {} + function f(address payable x) public returns (bool) { + return x.send(1); } + receive () external payable {} } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "lib"); + Address libraryAddress = m_contractAddress; + compileAndRun(sourceCode, 10, "c"); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); + BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); + ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(libraryAddress))), encodeArgs(false)); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); + BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); + ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(m_contractAddress))), encodeArgs(true)); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); + BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); } -BOOST_AUTO_TEST_CASE(byte_array_pop) +BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) { + // Check allocation size of byte array. Should be 32 plus length rounded up to next + // multiple of 32 char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (uint x, uint y, uint l) { - data.push(0x07); - data.push(0x03); - x = data.length; - data.pop(); - data.pop(); - data.push(0x02); - y = data.length; - l = data.length; + contract C { + function f() public pure returns (uint d1, uint d2, uint d3, uint memsize) { + bytes memory b1 = new bytes(31); + bytes memory b2 = new bytes(32); + bytes memory b3 = new bytes(256); + bytes memory b4 = new bytes(31); + assembly { + d1 := sub(b2, b1) + d2 := sub(b3, b2) + d3 := sub(b4, b3) + memsize := msize() + } } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 1, 1)); + if (!m_optimiserSettings.runYulOptimiser) + { + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256, 0x260)); + } } -BOOST_AUTO_TEST_CASE(byte_array_pop_empty_exception) +BOOST_AUTO_TEST_CASE(using_for_function_on_int) { char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - bytes data; - function test() public returns (bool) { - data.pop(); - return true; + library D { function double(uint self) public returns (uint) { return 2*self; } } + contract C { + using D for uint; + function f(uint a) public returns (uint) { + return a.double(); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(9)), encodeArgs(u256(2 * 9))); } -BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) +BOOST_AUTO_TEST_CASE(using_for_function_on_struct) { char const* sourceCode = R"( - contract c { - bytes data; - function test() public { - data.push(0x07); - data.push(0x05); - data.push(0x03); - data.pop(); - data.pop(); - data.pop(); + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s public x; + function f(uint a) public returns (uint) { + x.a = 3; + return x.mul(a); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(3 * 7))); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(3 * 7))); } -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty) +BOOST_AUTO_TEST_CASE(using_for_overload) { char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - bytes data; - function test() public returns (bool) { - for (uint8 i = 0; i <= 40; i++) - data.push(byte(i+1)); - for (int8 j = 40; j >= 0; j--) { - require(data[uint8(j)] == byte(j+1)); - require(data.length == uint8(j+1)); - data.pop(); - } - return true; - } + library D { + struct s { uint a; } + function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } + function mul(s storage self, bytes32 x) public returns (bytes32) { } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - bytes data; - function test() public { - for (uint8 i = 0; i <= 40; i++) - data.push(0x03); - for (uint8 j = 0; j <= 40; j++) { - assembly { - mstore(0, "garbage") - } - data.pop(); - } + contract C { + using D for D.s; + D.s public x; + function f(uint a) public returns (uint) { + x.a = 6; + return x.mul(a); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } -BOOST_AUTO_TEST_CASE(byte_array_pop_masking_long) +BOOST_AUTO_TEST_CASE(using_for_by_name) { char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bytes memory) { - for (uint i = 0; i < 34; i++) - data.push(0x03); - data.pop(); - return data; + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s public x; + function f(uint a) public returns (uint) { + x.a = 6; + return x.mul({x: a}); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256(0x20), - u256(33), - asString(fromHex("0303030303030303030303030303030303030303030303030303030303030303")), - asString(fromHex("03")) - )); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } -BOOST_AUTO_TEST_CASE(byte_array_pop_copy_long) +BOOST_AUTO_TEST_CASE(bound_function_in_function) { char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bytes memory) { - for (uint i = 0; i < 33; i++) - data.push(0x03); - for (uint j = 0; j < 4; j++) - data.pop(); - return data; - } + library L { + function g(function() internal returns (uint) _t) internal returns (uint) { return _t(); } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256(0x20), - u256(29), - asString(fromHex("0303030303030303030303030303030303030303030303030303030303")) - )); -} - -BOOST_AUTO_TEST_CASE(array_pop_isolated) -{ - char const* sourceCode = R"( - // This tests that the compiler knows the correct size of the function on the stack. - contract c { - uint[] data; - function test() public returns (uint x) { - x = 2; - data.pop; - x = 3; + contract C { + using L for *; + function f() public returns (uint) { + return t.g(); } + function t() public pure returns (uint) { return 7; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "L"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); } -BOOST_AUTO_TEST_CASE(byte_array_pop_isolated) +BOOST_AUTO_TEST_CASE(bound_function_in_var) { char const* sourceCode = R"( - // This tests that the compiler knows the correct size of the function on the stack. - contract c { - bytes data; - function test() public returns (uint x) { - x = 2; - data.pop; - x = 3; + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s public x; + function f(uint a) public returns (uint) { + x.a = 6; + return (x.mul)({x: a}); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } -BOOST_AUTO_TEST_CASE(external_array_args) +BOOST_AUTO_TEST_CASE(bound_function_to_string) { char const* sourceCode = R"( - contract c { - function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) - external returns (uint av, uint bv, uint cv) { - av = a[a_index]; - bv = b[b_index]; - cv = c[c_index]; + library D { function length(string memory self) public returns (uint) { return bytes(self).length; } } + contract C { + using D for string; + string x; + function f() public returns (uint) { + x = "abc"; + return x.length(); + } + function g() public returns (uint) { + string memory s = "abc"; + return s.length(); } } )"; - compileAndRun(sourceCode); - bytes params = encodeArgs( - 1, 2, 3, 4, 5, 6, 7, 8, // a - 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b - 21, 22, 23, 24, 25, // c - 0, 1, 2, // (a,b,c)_index - 3, // b.length - 11, 12, 13 // b - ); - ABI_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params), encodeArgs(1, 12, 23)); + compileAndRun(sourceCode, 0, "D"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(3))); } -BOOST_AUTO_TEST_CASE(bytes_index_access) +BOOST_AUTO_TEST_CASE(inline_long_string_return) { - char const* sourceCode = R"( - contract c { - bytes data; - function direct(bytes calldata arg, uint index) external returns (uint) { - return uint(uint8(arg[index])); - } - function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { - data = arg; - return uint(uint8(data[index])); - } - function storageWrite() external returns (uint) { - data = new bytes(35); - data[31] = 0x77; - data[32] = 0x14; - - data[31] = 0x01; - data[31] |= 0x08; - data[30] = 0x01; - data[32] = 0x03; - return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); + char const* sourceCode = R"( + contract C { + function f() public returns (string memory) { + return (["somethingShort", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"][1]); } } )"; - compileAndRun(sourceCode); - string array{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33}; - ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193)); + + string strLong = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeDyn(strLong)); } -BOOST_AUTO_TEST_CASE(bytes_delete_element) +BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) { + // Test for a bug where higher order bits cleanup was not done for array index access. char const* sourceCode = R"( - contract c { - bytes data; - function test1() external returns (bool) { - data = new bytes(100); - for (uint i = 0; i < data.length; i++) - data[i] = byte(uint8(i)); - delete data[94]; - delete data[96]; - delete data[98]; - return data[94] == 0 && uint8(data[95]) == 95 && data[96] == 0 && uint8(data[97]) == 97; + contract C { + function f(uint x) public returns (uint[256] memory r){ + r[uint8(x)] = 2; + } } - } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test1()"), encodeArgs(true)); + compileAndRun(sourceCode, 0, "C"); + // neither of the two should throw due to out-of-bounds access + BOOST_CHECK(callContractFunction("f(uint256)", u256(0x01)).size() == 256 * 32); + BOOST_CHECK(callContractFunction("f(uint256)", u256(0x101)).size() == 256 * 32); } -BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) +BOOST_AUTO_TEST_CASE(failed_create) { char const* sourceCode = R"( - contract c { - uint[9] m_data; - uint[] m_data_dyn; - uint8[][] m_byte_data; - function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { - m_data = a; - m_data_dyn = a; - m_byte_data = b; - return b[3][1]; // note that access and declaration are reversed to each other + contract D { constructor() public payable {} } + contract C { + uint public x; + constructor() public payable {} + function f(uint amount) public returns (D) { + x++; + return (new D).value(amount)(); } - function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { - a = m_data.length; - b = m_data[7]; - c = m_data_dyn.length; - d = m_data_dyn[7]; - e = m_byte_data.length; - f = m_byte_data[3].length; - g = m_byte_data[3][1]; + function stack(uint depth) public returns (address) { + if (depth < 1024) + return this.stack(depth - 1); + else + return address(f(0)); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs( - 21, 22, 23, 24, 25, 26, 27, 28, 29, // a - u256(32 * (9 + 1)), - 4, // size of b - 1, 2, 3, // b[0] - 11, 12, 13, // b[1] - 21, 22, 23, // b[2] - 31, 32, 33 // b[3] - )), encodeArgs(32)); - ABI_CHECK(callContractFunction("retrieve()"), encodeArgs( - 9, 28, 9, 28, - 4, 3, 32)); + compileAndRun(sourceCode, 20, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", 20) != encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("f(uint256)", 20), encodeArgs()); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("stack(uint256)", 1023), encodeArgs()); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); } -BOOST_AUTO_TEST_CASE(array_copy_nested_array) +BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) { + // Memory arrays are initialized using calldatacopy past the size of the calldata. + // This test checks that it also works in the constructor context. char const* sourceCode = R"( - contract c { - uint[4][] a; - uint[10][] b; - uint[][] c; - function test(uint[2][] calldata d) external returns (uint) { - a = d; - b = a; - c = b; - return c[1][1] | c[1][2] | c[1][3] | c[1][4]; + contract C { + bool public success; + constructor() public { + // Make memory dirty. + assembly { + for { let i := 0 } lt(i, 64) { i := add(i, 1) } { + mstore(msize(), not(0)) + } + } + uint16[3] memory c; + require(c[0] == 0 && c[1] == 0 && c[2] == 0); + uint16[] memory x = new uint16[](3); + require(x[0] == 0 && x[1] == 0 && x[2] == 0); + success = true; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test(uint256[2][])", encodeArgs( - 32, 3, - 7, 8, - 9, 10, - 11, 12 - )), encodeArgs(10)); + // Cannot run against yul optimizer because of msize + if (!m_optimiserSettings.runYulOptimiser) + { + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1))); + } } -BOOST_AUTO_TEST_CASE(array_copy_including_mapping) +BOOST_AUTO_TEST_CASE(mutex) { char const* sourceCode = R"( - contract c { - mapping(uint=>uint)[90][] large; - mapping(uint=>uint)[3][] small; - function test() public returns (uint r) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - large[3][2][0] = 2; - large[1] = large[3]; - small[3][2][0] = 2; - small[1] = small[2]; - r = (( - small[3][2][0] * 0x100 | - small[1][2][0]) * 0x100 | - large[3][2][0]) * 0x100 | - large[1][2][0]; - delete small; - delete large; - + contract mutexed { + bool locked; + modifier protected { + if (locked) revert(); + locked = true; + _; + locked = false; } - function clear() public returns (uint, uint) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - small[3][2][0] = 0; - large[3][2][0] = 0; - while (small.length > 0) - small.pop(); - while (large.length > 0) - large.pop(); - return (small.length, large.length); + } + contract Fund is mutexed { + uint shares; + constructor() public payable { shares = msg.value; } + function withdraw(uint amount) public protected returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) revert(); + (bool success,) = msg.sender.call.value(amount)(""); + require(success); + shares -= amount; + return shares; + } + function withdrawUnprotected(uint amount) public returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) revert(); + (bool success,) = msg.sender.call.value(amount)(""); + require(success); + shares -= amount; + return shares; } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200)); - // storage is not empty because we cannot delete the mappings - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(swap_in_storage_overwrite) -{ - // This tests a swap in storage which does not work as one - // might expect because we do not have temporary storage. - // (x, y) = (y, x) is the same as - // y = x; - // x = y; - char const* sourceCode = R"( - contract c { - struct S { uint a; uint b; } - S public x; - S public y; - function set() public { - x.a = 1; x.b = 2; - y.a = 3; y.b = 4; + contract Attacker { + Fund public fund; + uint callDepth; + bool protected; + function setProtected(bool _protected) public { protected = _protected; } + constructor(Fund _fund) public { fund = _fund; } + function attack() public returns (uint) { + callDepth = 0; + return attackInternal(); } - function swap() public { - (x, y) = (y, x); + function attackInternal() internal returns (uint) { + if (protected) + return fund.withdraw(10); + else + return fund.withdrawUnprotected(10); + } + fallback() external payable { + callDepth++; + if (callDepth < 4) + attackInternal(); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0), u256(0))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(0), u256(0))); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1), u256(2))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(3), u256(4))); - ABI_CHECK(callContractFunction("swap()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1), u256(2))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(1), u256(2))); + compileAndRun(sourceCode, 500, "Fund"); + auto fund = m_contractAddress; + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + compileAndRun(sourceCode, 0, "Attacker", encodeArgs(u160(fund))); + ABI_CHECK(callContractFunction("setProtected(bool)", true), encodeArgs()); + ABI_CHECK(callContractFunction("attack()"), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + ABI_CHECK(callContractFunction("setProtected(bool)", false), encodeArgs()); + ABI_CHECK(callContractFunction("attack()"), encodeArgs(u256(460))); + BOOST_CHECK_EQUAL(balanceAt(fund), 460); } -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) +BOOST_AUTO_TEST_CASE(payable_function) { char const* sourceCode = R"( - contract Base { - constructor(uint i) public - { - m_i = i; + contract C { + uint public a; + function f() payable public returns (uint) { + return msg.value; + } + fallback() external payable { + a = msg.value + 1; } - uint public m_i; - } - contract Derived is Base { - constructor(uint i) Base(i) public - {} - } - contract Final is Derived(4) { } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs(u256(27))); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27); + ABI_CHECK(callContractFunctionWithValue("", 27), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); + ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(28))); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); } -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base) +BOOST_AUTO_TEST_CASE(payable_function_calls_library) { char const* sourceCode = R"( - contract Base { - constructor(uint j) public - { - m_i = j; - } - uint public m_i; - } - contract Base1 is Base { - constructor(uint k) Base(k) public {} - } - contract Derived is Base, Base1 { - constructor(uint i) Base1(i) public - {} + library L { + function f() public returns (uint) { return 7; } } - contract Final is Derived(4) { + contract C { + function f() public payable returns (uint) { + return L.f(); + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); + compileAndRun(sourceCode, 0, "L"); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs(u256(7))); } -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) +BOOST_AUTO_TEST_CASE(non_payable_throw) { char const* sourceCode = R"( - contract Base { - constructor(uint i) public - { - m_i = i; + contract C { + uint public a; + function f() public returns (uint) { + return msgvalue(); + } + function msgvalue() internal returns (uint) { + return msg.value; + } + fallback() external { + update(); + } + function update() internal { + a = msg.value + 1; } - uint public m_i; - } - abstract contract Base1 is Base { - constructor(uint k) public {} - } - contract Derived is Base, Base1 { - constructor(uint i) Base(i) Base1(7) public {} - } - contract Final is Derived(4) { - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); -} -BOOST_AUTO_TEST_CASE(simple_constant_variables_test) -{ - char const* sourceCode = R"( - contract Foo { - function getX() public returns (uint r) { return x; } - uint constant x = 56; } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getX()"), encodeArgs(56)); + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); + ABI_CHECK(callContractFunction(""), encodeArgs()); + ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunctionWithValue("", 27), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); + ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunctionWithValue("a()", 27), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); } -BOOST_AUTO_TEST_CASE(constant_variables) +BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) { char const* sourceCode = R"( - contract Foo { - uint constant x = 56; - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - ActionChoices constant choices = ActionChoices.GoLeft; - bytes32 constant st = "abc\x00\xff__"; + contract C { + modifier tryCircumvent { + if (false) _; // avoid the function, we should still not accept ether + } + function f() tryCircumvent public returns (uint) { + return msgvalue(); + } + function msgvalue() internal returns (uint) { + return msg.value; + } } )"; ALSO_VIA_YUL( compileAndRun(sourceCode); + ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); ) } -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression) +BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) { + // This tests that memory resize for return values is not paid during the call, which would + // make the gas calculation overly complex. We access the end of the output area before + // the call is made. + // Tests that this also survives the optimizer. char const* sourceCode = R"( contract C { - uint constant x = 0x123 + 0x456; - function f() public returns (uint) { return x + 1; } + function f() public returns (uint[200] memory) {} + } + contract D { + function f(C c) public returns (uint) { c.f(); return 7; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x123 + 0x456 + 1)); + + compileAndRun(sourceCode, 0, "C"); + u160 cAddr = m_contractAddress; + compileAndRun(sourceCode, 0, "D"); + ABI_CHECK(callContractFunction("f(address)", cAddr), encodeArgs(u256(7))); } -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) +BOOST_AUTO_TEST_CASE(receive_external_function_type) { char const* sourceCode = R"( contract C { - bytes32 constant x = keccak256("abc"); - function f() public returns (bytes32) { return x; } + function g() public returns (uint) { return 7; } + function f(function() external returns (uint) g) public returns (uint) { + return g(); + } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(util::keccak256("abc"))); -} - -// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented -//BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) -//{ -// char const* sourceCode = R"( -// contract C { -// uint[3] constant x = [uint(1), 2, 3]; -// uint constant y = x[0] + x[1] + x[2]; -// function f() public returns (uint) { return y; } -// } -// )"; -// compileAndRun(sourceCode); -// ABI_CHECK(callContractFunction("f()"), encodeArgs(1 + 2 + 3)); -//} -// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented -//BOOST_AUTO_TEST_CASE(constant_struct) -//{ -// char const* sourceCode = R"( -// contract C { -// struct S { uint x; uint[] y; } -// S constant x = S(5, new uint[](4)); -// function f() public returns (uint) { return x.x; } -// } -// )"; -// compileAndRun(sourceCode); -// ABI_CHECK(callContractFunction("f()"), encodeArgs(5)); -//} + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction( + "f(function)", + m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0) + ), encodeArgs(u256(7))); +} -BOOST_AUTO_TEST_CASE(packed_storage_structs_uint) +BOOST_AUTO_TEST_CASE(return_external_function_type) { char const* sourceCode = R"( contract C { - struct str { uint8 a; uint16 b; uint248 c; } - str data; - function test() public returns (uint) { - data.a = 2; - if (data.a != 2) return 2; - data.b = 0xabcd; - if (data.b != 0xabcd) return 3; - data.c = 0x1234567890; - if (data.c != 0x1234567890) return 4; - if (data.a != 2) return 5; - data.a = 8; - if (data.a != 8) return 6; - if (data.b != 0xabcd) return 7; - data.b = 0xdcab; - if (data.b != 0xdcab) return 8; - if (data.c != 0x1234567890) return 9; - data.c = 0x9876543210; - if (data.c != 0x9876543210) return 10; - return 1; + function g() public {} + function f() public returns (function() external) { + return this.g; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); + + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK( + callContractFunction("f()"), + m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0) + ); } -BOOST_AUTO_TEST_CASE(packed_storage_structs_enum) +// TODO: store bound internal library functions + +BOOST_AUTO_TEST_CASE(shift_bytes) { char const* sourceCode = R"( contract C { - enum small { A, B, C, D } - enum larger { A, B, C, D, E} - struct str { small a; small b; larger c; larger d; } - str data; - function test() public returns (uint) { - data.a = small.B; - if (data.a != small.B) return 2; - data.b = small.C; - if (data.b != small.C) return 3; - data.c = larger.D; - if (data.c != larger.D) return 4; - if (data.a != small.B) return 5; - data.a = small.C; - if (data.a != small.C) return 6; - if (data.b != small.C) return 7; - data.b = small.D; - if (data.b != small.D) return 8; - if (data.c != larger.D) return 9; - data.c = larger.B; - if (data.c != larger.B) return 10; - return 1; + function left(bytes20 x, uint8 y) public returns (bytes20) { + return x << y; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(packed_storage_structs_bytes) -{ - char const* sourceCode = R"( - contract C { - struct s1 { byte a; byte b; bytes10 c; bytes9 d; bytes10 e; } - struct s2 { byte a; s1 inner; byte b; byte c; } - byte x; - s2 data; - byte y; - function test() public returns (bool) { - x = 0x01; - data.a = 0x02; - data.inner.a = 0x03; - data.inner.b = 0x04; - data.inner.c = "1234567890"; - data.inner.d = "123456789"; - data.inner.e = "abcdefghij"; - data.b = 0x05; - data.c = byte(0x06); - y = 0x07; - return x == 0x01 && data.a == 0x02 && data.inner.a == 0x03 && data.inner.b == 0x04 && - data.inner.c == "1234567890" && data.inner.d == "123456789" && - data.inner.e == "abcdefghij" && data.b == 0x05 && data.c == byte(0x06) && y == 0x07; + function right(bytes20 x, uint8 y) public returns (bytes20) { + return x >> y; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("left(bytes20,uint8)", "12345678901234567890", 8 * 8), encodeArgs("901234567890" + string(8, 0))); + ABI_CHECK(callContractFunction("right(bytes20,uint8)", "12345678901234567890", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); } -BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) +BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) { char const* sourceCode = R"( contract C { - struct str { uint8 a; uint16 b; uint8 c; } - uint8 x; - uint16 y; - str data; - function test() public returns (uint) { - x = 1; - y = 2; - data.a = 2; - data.b = 0xabcd; - data.c = 0xfa; - if (x != 1 || y != 2 || data.a != 2 || data.b != 0xabcd || data.c != 0xfa) - return 2; - delete y; - delete data.b; - if (x != 1 || y != 0 || data.a != 2 || data.b != 0 || data.c != 0xfa) - return 3; - delete x; - delete data; - return 1; + function left(uint8 y) public returns (bytes20) { + bytes20 x; + assembly { x := "12345678901234567890abcde" } + return x << y; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) -{ - char const* sourceCode = R"( - contract test { - function f(uint k) public returns(uint d) { return k; } - function f(uint a, uint b) public returns(uint d) { return a + b; } - function g() public returns(uint d) { return f(3); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ) -} - -BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second) -{ - char const* sourceCode = R"( - contract test { - function f(uint a, uint b) public returns(uint d) { return a + b; } - function f(uint k) public returns(uint d) { return k; } - function g() public returns(uint d) { return f(3, 7); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ) -} - -BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) -{ - char const* sourceCode = R"( - contract test { - function f(uint a, uint b) public returns(uint d) { return a + b; } - function f(uint k) public returns(uint d) { return k; } - function g(bool flag) public returns(uint d) { - if (flag) - return f(3); - else - return f(3, 7); + function right(uint8 y) public returns (bytes20) { + bytes20 x; + assembly { x := "12345678901234567890abcde" } + return x >> y; } } )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(3)); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs(10)); - ) -} - -BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) -{ - char const* sourceCode = R"( - contract B { function f() public returns(uint) { return 10; } } - contract C is B { - function f(uint i) public returns(uint) { return 2 * i; } - function g() public returns(uint) { return f(1); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) -{ - char const* sourceCode = R"( - contract A { function f(uint a) public returns(uint) { return 2 * a; } } - contract B { function f() public returns(uint) { return 10; } } - contract C is A, B { - function g() public returns(uint) { return f(); } - function h() public returns(uint) { return f(1); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(super_overload) -{ - char const* sourceCode = R"( - contract A { function f(uint a) public returns(uint) { return 2 * a; } } - contract B { function f(bool b) public returns(uint) { return 10; } } - contract C is A, B { - function g() public returns(uint) { return super.f(true); } - function h() public returns(uint) { return super.f(1); } - } - )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("left(uint8)", 8 * 8), encodeArgs("901234567890" + string(8, 0))); + ABI_CHECK(callContractFunction("right(uint8)", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); } -BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) +BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) { char const* sourceCode = R"( - contract C { - function gasleft() public returns(uint256) { return 0; } - function f() public returns(uint256) { return gasleft(); } - } + contract C1 {} + /** + **/ + contract C2 {} )"; ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + compileAndRun(sourceCode, 0, "C1"); + compileAndRun(sourceCode, 0, "C2"); ) } -BOOST_AUTO_TEST_CASE(bool_conversion) +BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) { char const* sourceCode = R"( - contract C { - function f(bool _b) public returns(uint) { - if (_b) - return 1; - else - return 0; + contract D { + bytes a = hex"1237651237125387136581271652831736512837126583171583712358126123765123712538713658127165283173651283712658317158371235812612376512371253871365812716528317365128371265831715837123581261237651237125387136581271652831736512837126583171583712358126"; + bytes b = hex"1237651237125327136581271252831736512837126583171383712358126123765125712538713658127165253173651283712658357158371235812612376512371a5387136581271652a317365128371265a317158371235812612a765123712538a13658127165a83173651283712a58317158371235a126"; + constructor(uint) public {} + } + contract Double { + function f() public { + new D(2); } - function g(bool _in) public returns (bool _out) { - _out = _in; + function g() public { + new D(3); } } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("f(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("g(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(packed_storage_signed) -{ - char const* sourceCode = R"( - contract C { - int8 a; - uint8 b; - int8 c; - uint8 d; - function test() public returns (uint x1, uint x2, uint x3, uint x4) { - a = -2; - b = -uint8(a) * 2; - c = a * int8(120) * int8(121); - x1 = uint(a); - x2 = b; - x3 = uint(c); - x4 = d; + contract Single { + function f() public { + new D(2); } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); + BOOST_CHECK_LE( + double(m_compiler.object("Double").bytecode.size()), + 1.2 * double(m_compiler.object("Single").bytecode.size()) + ); } -BOOST_AUTO_TEST_CASE(external_types_in_calls) +BOOST_AUTO_TEST_CASE(revert_with_cause) { char const* sourceCode = R"( - contract C1 { C1 public bla; constructor(C1 x) public { bla = x; } } - contract C { - function test() public returns (C1 x, C1 y) { - C1 c = new C1(C1(9)); - x = c.bla(); - y = this.t1(C1(7)); + contract D { + string constant msg1 = "test1234567890123456789012345678901234567890"; + string msg2 = "test1234567890123456789012345678901234567890"; + function f() public { + revert("test123"); + } + function g() public { + revert("test1234567890123456789012345678901234567890"); + } + function h() public { + revert(msg1); + } + function i() public { + revert(msg2); + } + function j() public { + string memory msg3 = "test1234567890123456789012345678901234567890"; + revert(msg3); } - function t1(C1 a) public returns (C1) { return a; } - function t2() public returns (C1) { return C1(9); } } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(9), u256(7))); - ABI_CHECK(callContractFunction("t2()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(invalid_enum_compared) -{ - char const* sourceCode = R"( contract C { - enum X { A, B } - - function test_eq() public returns (bool) { - X garbled; + D d = new D(); + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { + uint retsize; assembly { - garbled := 5 + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() } - return garbled == garbled; - } - function test_eq_ok() public returns (bool) { - X garbled = X.A; - return garbled == garbled; - } - function test_neq() public returns (bool) { - X garbled; + retval = new bytes(retsize); assembly { - garbled := 5 + returndatacopy(add(retval, 0x20), 0, returndatasize()) } - return garbled != garbled; + } + function f() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function h() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function i() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function j() public returns (bool, bytes memory) { + return forward(address(d), msg.data); } } )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test_eq_ok()"), encodeArgs(u256(1))); - // both should throw - ABI_CHECK(callContractFunction("test_eq()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_neq()"), encodeArgs()); + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { + compileAndRun(sourceCode, 0, "C"); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "test123") + bytes(28, 0)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); + ABI_CHECK(callContractFunction("j()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); + } } -BOOST_AUTO_TEST_CASE(invalid_enum_logged) +BOOST_AUTO_TEST_CASE(require_with_message) { char const* sourceCode = R"( - contract C { - enum X { A, B } - event Log(X); - - function test_log() public returns (uint) { - X garbled = X.A; - assembly { - garbled := 5 - } - emit Log(garbled); - return 1; + contract D { + bool flag = false; + string storageError = "abc"; + string constant constantError = "abc"; + function f(uint x) public { + require(x > 7, "failed"); } - function test_log_ok() public returns (uint) { - X x = X.A; - emit Log(x); - return 1; + function g() public { + // As a side-effect of internalFun, the flag will be set to true + // (even if the condition is true), + // but it will only throw in the next evaluation. + bool flagCopy = flag; + require(flagCopy == false, internalFun()); + } + function internalFun() public returns (string memory) { + flag = true; + return "only on second run"; + } + function h() public { + require(false, storageError); + } + function i() public { + require(false, constantError); + } + function j() public { + string memory errMsg = "msg"; + require(false, errMsg); } } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test_log_ok()"), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_REQUIRE_EQUAL(logTopic(0, 0), util::keccak256(string("Log(uint8)"))); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(0))); - - // should throw - ABI_CHECK(callContractFunction("test_log()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(invalid_enum_stored) -{ - char const* sourceCode = R"( contract C { - enum X { A, B } - X public x; - - function test_store() public returns (uint) { - X garbled = X.A; + D d = new D(); + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { + uint retsize; assembly { - garbled := 5 + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) } - x = garbled; - return 1; } - function test_store_ok() public returns (uint) { - x = X.A; - return 1; + function f(uint x) public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function h() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function i() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function j() public returns (bool, bytes memory) { + return forward(address(d), msg.data); } } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test_store_ok()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - - // should throw - ABI_CHECK(callContractFunction("test_store()"), encodeArgs()); + )"; + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { + compileAndRun(sourceCode, 0, "C"); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f(uint256)", 8), encodeArgs(1, 0x40, 0)); + ABI_CHECK(callContractFunction("f(uint256)", 5), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 6, "failed") + bytes(28, 0)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(1, 0x40, 0)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 18, "only on second run") + bytes(28, 0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0)); + ABI_CHECK(callContractFunction("j()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "msg") + bytes(28, 0)); + } } -BOOST_AUTO_TEST_CASE(invalid_enum_as_external_ret) +BOOST_AUTO_TEST_CASE(bubble_up_error_messages) { char const* sourceCode = R"( + contract D { + function f() public { + revert("message"); + } + function g() public { + this.f(); + } + } contract C { - enum X { A, B } - - function test_return() public returns (X) { - X garbled; + D d = new D(); + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { + uint retsize; assembly { - garbled := 5 + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() } - return garbled; - } - function test_inline_assignment() public returns (X _ret) { + retval = new bytes(retsize); assembly { - _ret := 5 + returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function test_assignment() public returns (X _ret) { - X tmp; - assembly { - tmp := 5 - } - _ret = tmp; + function f() public returns (bool, bytes memory) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes memory) { + return forward(address(d), msg.data); } } )"; - ALSO_VIA_YUL( + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { compileAndRun(sourceCode, 0, "C"); - // both should throw - ABI_CHECK(callContractFunction("test_return()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_inline_assignment()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_assignment()"), encodeArgs()); - ) + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); + } } -BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg) +BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) { char const* sourceCode = R"( - contract C { - enum X { A, B } - - function tested (X x) public returns (uint) { - return 1; + contract D { + receive() external payable { + revert("message"); } - - function test() public returns (uint) { - X garbled; - + function f() public { + address(this).transfer(0); + } + } + contract C { + D d = new D(); + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { + uint retsize; assembly { - garbled := 5 + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() } - - return this.tested(garbled); + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f() public returns (bool, bytes memory) { + return forward(address(d), msg.data); } } )"; - compileAndRun(sourceCode, 0, "C"); - // should throw - ABI_CHECK(callContractFunction("test()"), encodeArgs()); + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { + compileAndRun(sourceCode, 0, "C"); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); + } } -BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct) +BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) { char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; - } - testStruct data1; - testStruct data2; - testStruct data3; - constructor() public - { - data1.m_value = 2; - } - function assign() public returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1) - { - testStruct storage x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 - data2 = data1; // should copy data. data2.m_value == 2 - - ret_local = x.m_value; // = 2 - ret_global = data2.m_value; // = 2 - - x.m_value = 3; - data3 = x; //should copy the data. data3.m_value == 3 - ret_global3 = data3.m_value; // = 3 - ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value + contract E { + constructor() public { + revert("message"); } } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("assign()"), encodeArgs(2, 2, 3, 3)); -} - -BOOST_AUTO_TEST_CASE(struct_delete_member) -{ - char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; + contract D { + function f() public { + E x = new E(); } - testStruct data1; - constructor() public - { - data1.m_value = 2; + } + contract C { + D d = new D(); + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } } - function deleteMember() public returns (uint ret_value) - { - testStruct storage x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 - x.m_value = 4; - delete x.m_value; - ret_value = data1.m_value; + function f() public returns (bool, bytes memory) { + return forward(address(d), msg.data); } } )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("deleteMember()"), encodeArgs(0)); + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { + compileAndRun(sourceCode, 0, "C"); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); + } } -BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping) +BOOST_AUTO_TEST_CASE(interface_contract) { char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; + interface I { + event A(); + function f() external returns (bool); + fallback() external payable; + } + + contract A is I { + function f() public override returns (bool) { + return g(); } - mapping (uint => testStruct) campaigns; - constructor() public - { - campaigns[0].m_value = 2; + function g() public returns (bool) { + return true; } - function deleteIt() public returns (uint) - { - delete campaigns[0]; - return campaigns[0].m_value; + + fallback() override external payable { } } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("deleteIt()"), encodeArgs(0)); -} -BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access) -{ - char const* sourceCode = R"( - contract A { - uint[3] arr; - bool public test = false; - function getElement(uint i) public returns (uint) - { - return arr[i]; - } - function testIt() public returns (bool) - { - uint i = this.getElement(5); - test = true; - return true; + contract C { + function f(address payable _interfaceAddress) public returns (bool) { + I i = I(_interfaceAddress); + return i.f(); } } )"; compileAndRun(sourceCode, 0, "A"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("testIt()"), encodeArgs()); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); + u160 const recipient = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(address)", recipient), encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail) +BOOST_AUTO_TEST_CASE(bare_call_invalid_address) { - char const* sourceCode = R"( - contract A { - constructor() public - { - address(this).call("123"); + char const* sourceCode = R"YY( + contract C { + /// Calling into non-existent account is successful (creates the account) + function f() external returns (bool) { + (bool success,) = address(0x4242).call(""); + return success; } - } - contract B { - uint public test = 1; - function testIt() public - { - A a = new A(); - ++test; + function h() external returns (bool) { + (bool success,) = address(0x4242).delegatecall(""); + return success; } } - )"; - compileAndRun(sourceCode, 0, "B"); + )YY"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("testIt()"), encodeArgs()); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2)); + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract C { + function f() external returns (bool, bytes memory) { + return address(0x4242).staticcall(""); + } + } + )YY"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), 0x40, 0x00)); + } } -BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) -{ - char const* sourceCode = R"( - contract A { - uint public test = 1; - uint[3] arr; - constructor() public - { - uint index = 5; - test = arr[index]; - ++test; - } - } - )"; - ABI_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A"), encodeArgs()); - BOOST_CHECK(!m_transactionSuccessful); -} - -BOOST_AUTO_TEST_CASE(positive_integers_to_signed) -{ - char const* sourceCode = R"( - contract test { - int8 public x = 2; - int8 public y = 127; - int16 public q = 250; - } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("y()"), encodeArgs(127)); - ABI_CHECK(callContractFunction("q()"), encodeArgs(250)); -} - -BOOST_AUTO_TEST_CASE(failing_send) -{ - char const* sourceCode = R"( - contract Helper { - uint[] data; - fallback () external { - data[9]; // trigger exception - } - } - contract Main { - constructor() public payable {} - function callHelper(address payable _a) public returns (bool r, uint bal) { - r = !_a.send(5); - bal = address(this).balance; - } - } - )"; - compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; - compileAndRun(sourceCode, 20, "Main"); - BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); -} - -BOOST_AUTO_TEST_CASE(send_zero_ether) -{ - // Sending zero ether to a contract should still invoke the receive ether function - // (it previously did not because the gas stipend was not provided by the EVM) - char const* sourceCode = R"( - contract Receiver { - receive () external payable { - } - } - contract Main { - constructor() public payable {} - function s() public returns (bool) { - Receiver r = new Receiver(); - return address(r).send(0); - } - } - )"; - compileAndRun(sourceCode, 20, "Main"); - BOOST_REQUIRE(callContractFunction("s()") == encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(reusing_memory) -{ - // Invoke some features that use memory and test that they do not interfere with each other. - char const* sourceCode = R"( - contract Helper { - uint public flag; - constructor(uint x) public { - flag = x; - } - } - contract Main { - mapping(uint => uint) map; - function f(uint x) public returns (uint) { - map[x] = x; - return (new Helper(uint(keccak256(abi.encodePacked(this.g(map[x])))))).flag(); - } - function g(uint a) public returns (uint) - { - return map[a]; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(util::keccak256(util::toBigEndian(u256(0x34))))); -} - -BOOST_AUTO_TEST_CASE(return_string) -{ - char const* sourceCode = R"( - contract Main { - string public s; - function set(string calldata _s) external { - s = _s; - } - function get1() public returns (string memory r) { - return s; - } - function get2() public returns (string memory r) { - r = s; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s("Julia"); - bytes args = encodeArgs(u256(0x20), u256(s.length()), s); - BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs()); - ABI_CHECK(callContractFunction("get1()"), args); - ABI_CHECK(callContractFunction("get2()"), args); - ABI_CHECK(callContractFunction("s()"), args); -} - -BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes) -{ - char const* sourceCode = R"( - contract Main { - string public s1; - string public s2; - function set(string calldata _s1, uint x, string calldata _s2) external returns (uint) { - s1 = _s1; - s2 = _s2; - return x; - } - function get() public returns (string memory r1, string memory r2) { - r1 = s1; - r2 = s2; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1( - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - ); - string s2( - "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" - "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" - "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" - "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" - "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" - ); - vector lengths{0, 30, 32, 63, 64, 65, 210, 300}; - for (auto l1: lengths) - for (auto l2: lengths) - { - bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); - bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); - bytes args = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2; - BOOST_REQUIRE( - callContractFunction("set(string,uint256,string)", asString(args)) == - encodeArgs(u256(l1)) - ); - bytes result = encodeArgs(u256(0x40), u256(0x40 + dyn1.size())) + dyn1 + dyn2; - ABI_CHECK(callContractFunction("get()"), result); - ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn1); - ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn2); - } -} - -BOOST_AUTO_TEST_CASE(accessor_involving_strings) -{ - char const* sourceCode = R"( - contract Main { - struct stringData { string a; uint b; string c; } - mapping(uint => stringData[]) public data; - function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) { - while (data[x].length < y + 1) - data[x].push(); - data[x][y].a = a; - data[x][y].b = b; - data[x][y].c = c; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"); - bytes s1Data = encodeArgs(u256(s1.length()), s1); - bytes s2Data = encodeArgs(u256(s2.length()), s2); - u256 b = 765; - u256 x = 7; - u256 y = 123; - bytes args = encodeArgs(x, y, u256(0xa0), b, u256(0xa0 + s1Data.size()), s1Data, s2Data); - bytes result = encodeArgs(u256(0x60), b, u256(0x60 + s1Data.size()), s1Data, s2Data); - BOOST_REQUIRE(callContractFunction("set(uint256,uint256,string,uint256,string)", asString(args)) == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("data(uint256,uint256)", x, y) == result); -} - -BOOST_AUTO_TEST_CASE(bytes_in_function_calls) -{ - char const* sourceCode = R"( - contract Main { - string public s1; - string public s2; - function set(string memory _s1, uint x, string memory _s2) public returns (uint) { - s1 = _s1; - s2 = _s2; - return x; - } - function setIndirectFromMemory(string memory _s1, uint x, string memory _s2) public returns (uint) { - return this.set(_s1, x, _s2); - } - function setIndirectFromCalldata(string calldata _s1, uint x, string calldata _s2) external returns (uint) { - return this.set(_s1, x, _s2); - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"); - vector lengths{0, 31, 64, 65}; - for (auto l1: lengths) - for (auto l2: lengths) - { - bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); - bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); - bytes args1 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2; - BOOST_REQUIRE( - callContractFunction("setIndirectFromMemory(string,uint256,string)", asString(args1)) == - encodeArgs(u256(l1)) - ); - ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn1); - ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn2); - // swapped - bytes args2 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn2.size())) + dyn2 + dyn1; - BOOST_REQUIRE( - callContractFunction("setIndirectFromCalldata(string,uint256,string)", asString(args2)) == - encodeArgs(u256(l1)) - ); - ABI_CHECK(callContractFunction("s1()"), encodeArgs(0x20) + dyn2); - ABI_CHECK(callContractFunction("s2()"), encodeArgs(0x20) + dyn1); - } -} - -BOOST_AUTO_TEST_CASE(return_bytes_internal) +BOOST_AUTO_TEST_CASE(bare_call_return_data) { - char const* sourceCode = R"( - contract Main { - bytes s1; - function doSet(bytes memory _s1) public returns (bytes memory _r1) { - s1 = _s1; - _r1 = s1; - } - function set(bytes calldata _s1) external returns (uint _r, bytes memory _r1) { - _r1 = doSet(_s1); - _r = _r1.length; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - vector lengths{0, 31, 64, 65}; - for (auto l1: lengths) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { - bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); - bytes args1 = encodeArgs(u256(0x20)) + dyn1; - BOOST_REQUIRE( - callContractFunction("set(bytes)", asString(args1)) == - encodeArgs(u256(l1), u256(0x40)) + dyn1 - ); - } -} - -BOOST_AUTO_TEST_CASE(bytes_index_access_memory) -{ - char const* sourceCode = R"( - contract Main { - function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { - c1 = _s1[i1]; - c2 = intern(_s1, i2); - c3 = internIndirect(_s1)[i3]; - } - function intern(bytes memory _s1, uint i) public returns (byte c) { - return _s1[i]; - } - function internIndirect(bytes memory _s1) public returns (bytes memory) { - return _s1; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) == - encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]}) - ); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker) -{ - char const* sourceCode = R"( - contract Test { - uint public m_x; - bytes public m_s; - constructor(uint x, bytes memory s) public { - m_x = x; - m_s = s; - } - } - )"; - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - compileAndRun(sourceCode, 0, "Test", args1); - BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x)); - BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - bytes m_s; - constructor(uint x, bytes memory s) public { - m_x = x; - m_s = s; - } - function part(uint i) public returns (byte) { - return m_s[i]; - } - } - contract Main is Base { - constructor(bytes memory s, uint x) Base(x, f(s)) public {} - function f(bytes memory s) public returns (bytes memory) { - return s; - } - } - contract Creator { - function f(uint x, bytes memory s) public returns (uint r, byte ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,bytes)", asString(args1)) == - encodeArgs(x, string{s1[unsigned(x)]}) - ); -} - -BOOST_AUTO_TEST_CASE(arrays_in_constructors) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - address[] m_s; - constructor(uint x, address[] memory s) public { - m_x = x; - m_s = s; - } - function part(uint i) public returns (address) { - return m_s[i]; - } - } - contract Main is Base { - constructor(address[] memory s, uint x) Base(x, f(s)) public {} - function f(address[] memory s) public returns (address[] memory) { - return s; - } - } - contract Creator { - function f(uint x, address[] memory s) public returns (uint r, address ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - vector s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - bytes dyn1 = encodeArgs(u256(s1.size()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,address[])", asString(args1)) == - encodeArgs(x, s1[unsigned(x)]) - ); -} - -BOOST_AUTO_TEST_CASE(fixed_arrays_in_constructors) -{ - char const* sourceCode = R"( - contract Creator { - uint public r; - address public ch; - constructor(address[3] memory s, uint x) public { - r = x; - ch = s[2]; - } - } - )"; - compileAndRun(sourceCode, 0, "Creator", encodeArgs(u256(1), u256(2), u256(3), u256(4))); - BOOST_REQUIRE(callContractFunction("r()") == encodeArgs(u256(4))); - BOOST_REQUIRE(callContractFunction("ch()") == encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[] public data; - function set(uint24[] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) == - encodeArgs(u256(data.size())) - ); - ABI_CHECK(callContractFunction("data(uint256)", u256(7)), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("data(uint256)", u256(15)), encodeArgs(u256(16))); - ABI_CHECK(callContractFunction("data(uint256)", u256(18)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); -} - -BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][] public data; - function set(uint24[3][] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[3][] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[3][])", u256(0x20), u256(data.size() / 3), data) == - encodeArgs(u256(data.size() / 3)) - ); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(2), u256(2)), encodeArgs(u256(9))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(5), u256(1)), encodeArgs(u256(17))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(6), u256(0)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size() / 3), data)); -} - -BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][] memory _data, uint a, uint b) public returns (uint l, uint e) { - l = _data.length; - e = _data[a][b]; - } - } - )"; - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(uint24[3][],uint256,uint256)", - u256(0x60), - u256(3), - u256(2), - u256(data.size() / 3), - data - ) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2]))); - ); -} - -BOOST_AUTO_TEST_CASE(bytes_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(bytes memory _data, uint i) public returns (uint l, byte c) { - l = _data.length; - c = _data[i]; - } - } - )"; - string data("abcdefgh"); - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(bytes,uint256)", - u256(0x40), - u256(3), - u256(data.size()), - data - ) == encodeArgs(u256(data.size()), string("d"))); - ); -} - -BOOST_AUTO_TEST_CASE(storage_array_ref) -{ - char const* sourceCode = R"( - contract BinarySearch { - /// Finds the position of _value in the sorted list _data. - /// Note that "internal" is important here, because storage references only work for internal or private functions - function find(uint[] storage _data, uint _value) internal returns (uint o_position) { - return find(_data, 0, _data.length, _value); - } - function find(uint[] storage _data, uint _begin, uint _len, uint _value) private returns (uint o_position) { - if (_len == 0 || (_len == 1 && _data[_begin] != _value)) - return uint(-1); // failure - uint halfLen = _len / 2; - uint v = _data[_begin + halfLen]; - if (_value < v) - return find(_data, _begin, halfLen, _value); - else if (_value > v) - return find(_data, _begin + halfLen + 1, halfLen - 1, _value); - else - return _begin + halfLen; - } - } - - contract Store is BinarySearch { - uint[] data; - function add(uint v) public { - data.push(0); - data[data.length - 1] = v; - } - function find(uint v) public returns (uint) { - return find(data, v); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Store"); - BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1))); - BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs()); - ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5))); - ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("find(uint256)", u256(400)), encodeArgs(u256(-1))); - ); -} - -BOOST_AUTO_TEST_CASE(memory_types_initialisation) -{ - char const* sourceCode = R"( - contract Test { - mapping(uint=>uint) data; - function stat() public returns (uint[5] memory) - { - data[2] = 3; // make sure to use some memory - } - function dyn() public returns (uint[] memory) { stat(); } - function nested() public returns (uint[3][] memory) { stat(); } - function nestedStat() public returns (uint[3][7] memory) { stat(); } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("stat()"), encodeArgs(vector(5))); - ABI_CHECK(callContractFunction("dyn()"), encodeArgs(u256(0x20), u256(0))); - ABI_CHECK(callContractFunction("nested()"), encodeArgs(u256(0x20), u256(0))); - ABI_CHECK(callContractFunction("nestedStat()"), encodeArgs(vector(3 * 7))); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_delete) -{ - char const* sourceCode = R"( - contract Test { - function del() public returns (uint24[3][4] memory) { - uint24[3][4] memory x; - for (uint24 i = 0; i < x.length; i ++) - for (uint24 j = 0; j < x[i].length; j ++) - x[i][j] = i * 0x10 + j; - delete x[1]; - delete x[3][2]; - return x; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - for (unsigned i = 0; i < 4; i++) - for (unsigned j = 0; j < 3; j++) - { - u256 v = 0; - if (!(i == 1 || (i == 3 && j == 2))) - v = i * 0x10 + j; - data[i * 3 + j] = v; - } - ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][4] memory x) public { - x[2][2] = 1; - x[3][2] = 7; - } - function f() public returns (uint24[3][4] memory){ - uint24[3][4] memory data; - set(data); - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(data)); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][][4] data; - function set(uint24[3][][4] memory x) internal returns (uint24[3][][4] memory) { - x[1][2][2] = 1; - x[1][3][2] = 7; - return x; - } - function f() public returns (uint24[3][] memory) { - while (data[1].length < 4) - data[1].push(); - return set(data)[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(4), data)); -} - -BOOST_AUTO_TEST_CASE(memory_structs_read_write) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; uint8[2] a; } - S[5] data; - function testInit() public returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) { - S[2] memory d; - x = d[0].x; - y = d[0].y; - z = d[0].z; - a = d[0].a[1]; - flag = true; - } - function testCopyRead() public returns (uint8 x, uint16 y, uint z, uint8 a) { - data[2].x = 1; - data[2].y = 2; - data[2].z = 3; - data[2].a[1] = 4; - S memory s = data[2]; - x = s.x; - y = s.y; - z = s.z; - a = s.a[1]; - } - function testAssign() public returns (uint8 x, uint16 y, uint z, uint8 a) { - S memory s; - s.x = 1; - s.y = 2; - s.z = 3; - s.a[1] = 4; - x = s.x; - y = s.y; - z = s.z; - a = s.a[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("testInit()"), encodeArgs(u256(0), u256(0), u256(0), u256(0), true)); - ABI_CHECK(callContractFunction("testCopyRead()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); - ABI_CHECK(callContractFunction("testAssign()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_as_function_args) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - function test() public returns (uint x, uint y, uint z) { - S memory data = combine(1, 2, 3); - x = extract(data, 0); - y = extract(data, 1); - z = extract(data, 2); - } - function extract(S memory s, uint which) internal returns (uint x) { - if (which == 0) return s.x; - else if (which == 1) return s.y; - else return s.z; - } - function combine(uint8 x, uint16 y, uint z) internal returns (S memory s) { - s.x = x; - s.y = y; - s.z = z; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(1), u256(2), u256(3))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_nested) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - struct X { uint8 x; S s; } - function test() public returns (uint a, uint x, uint y, uint z) { - X memory d = combine(1, 2, 3, 4); - a = extract(d, 0); - x = extract(d, 1); - y = extract(d, 2); - z = extract(d, 3); - } - function extract(X memory s, uint which) internal returns (uint x) { - if (which == 0) return s.x; - else if (which == 1) return s.s.x; - else if (which == 2) return s.s.y; - else return s.s.z; - } - function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X memory s) { - s.x = a; - s.s.x = x; - s.s.y = y; - s.s.z = z; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_nested_load) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - struct X { uint8 x; S s; uint8[2] a; } - X m_x; - function load() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - m_x.x = 1; - m_x.s.x = 2; - m_x.s.y = 3; - m_x.s.z = 4; - m_x.a[0] = 5; - m_x.a[1] = 6; - X memory d = m_x; - a = d.x; - x = d.s.x; - y = d.s.y; - z = d.s.z; - a1 = d.a[0]; - a2 = d.a[1]; - } - function store() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - X memory d; - d.x = 1; - d.s.x = 2; - d.s.y = 3; - d.s.z = 4; - d.a[0] = 5; - d.a[1] = 6; - m_x = d; - a = m_x.x; - x = m_x.s.x; - y = m_x.s.y; - z = m_x.s.z; - a1 = m_x.a[0]; - a2 = m_x.a[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6)); - ABI_CHECK(callContractFunction("load()"), out); - ABI_CHECK(callContractFunction("store()"), out); -} - -BOOST_AUTO_TEST_CASE(struct_constructor_nested) -{ - char const* sourceCode = R"( - contract C { - struct X { uint x1; uint x2; } - struct S { uint s1; uint[3] s2; X s3; } - S s; - constructor() public { - uint[3] memory s2; - s2[1] = 9; - s = S(1, s2, X(4, 5)); - } - function get() public returns (uint s1, uint[3] memory s2, uint x1, uint x2) - { - s1 = s.s1; - s2 = s.s2; - x1 = s.s3.x1; - x2 = s.s3.x2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5)); - ABI_CHECK(callContractFunction("get()"), out); -} - -BOOST_AUTO_TEST_CASE(struct_named_constructor) -{ - char const* sourceCode = R"( - contract C { - struct S { uint a; bool x; } - S public s; - constructor() public { - s = S({a: 1, x: true}); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("s()"), encodeArgs(u256(1), true)); -} - -BOOST_AUTO_TEST_CASE(calldata_array) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint[2] calldata s) external pure returns (uint256 a, uint256 b) { - a = s[0]; - b = s[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f(uint256[2])", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S calldata s) external pure returns (uint256 a, uint256 b) { - a = s.a; - b = s.b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_and_ints) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(uint256 a, S calldata s, uint256 b) external pure returns (uint256, uint256, uint256, uint256) { - return (a, s.a, s.b, b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f(uint256,(uint256,uint256),uint256)", encodeArgs(u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - - -BOOST_AUTO_TEST_CASE(calldata_structs) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; } - function f(S1 calldata s1, S2 calldata s2, S1 calldata s3) - external pure returns (uint256 a, uint256 b, uint256 c, uint256 d, uint256 e) { - a = s1.a; - b = s1.b; - c = s2.a; - d = s3.a; - e = s3.b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256),(uint256),(uint256,uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_array_member) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256[2] b; uint256 c; } - function f(S calldata s) external pure returns (uint256 a, uint256 b0, uint256 b1, uint256 c) { - a = s.a; - b0 = s.b[0]; - b1 = s.b[1]; - c = s.c; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256[2],uint256))", encodeArgs(u256(42), u256(1), u256(2), u256(23))), encodeArgs(u256(42), u256(1), u256(2), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_array_of_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) { - l = s.length; - a = s[0].a; - b = s[0].b; - c = s[1].a; - d = s[1].b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(calldata_array_of_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) { - S[] memory m = s; - l = m.length; - a = m[0].a; - b = m[0].b; - c = m[1].a; - d = m[1].b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4))); -} - - -BOOST_AUTO_TEST_CASE(calldata_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S calldata s) external pure returns (uint256, uint256) { - S memory m = s; - return (m.a, m.b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(nested_calldata_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; uint256 b; S1 s; uint256 c; } - function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) { - return (s.a, s.b, s.s.a, s.s.b, s.c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - -BOOST_AUTO_TEST_CASE(nested_calldata_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; uint256 b; S1 s; uint256 c; } - function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) { - S2 memory m = s; - return (m.a, m.b, m.s.a, m.s.b, m.c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_short) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S calldata) external pure returns (uint256) { - return msg.data.length; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // double check that the valid case goes through - ABI_CHECK(callContractFunction("f((uint256,uint256))", u256(1), u256(2)), encodeArgs(0x44)); - - ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(63,0)), encodeArgs()); - ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(33,0)), encodeArgs()); - ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(32,0)), encodeArgs()); - ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(31,0)), encodeArgs()); - ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes()), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_cleaning) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint8 a; bytes1 b; } - function f(S calldata s) external pure returns (uint256 a, bytes32 b) { - uint8 tmp1 = s.a; - bytes1 tmp2 = s.b; - assembly { - a := tmp1 - b := tmp2 - } - - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // double check that the valid case goes through - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x12), bytes{0x34} + bytes(31,0)), encodeArgs(0x12, bytes{0x34} + bytes(31,0))); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x1234), bytes{0x56, 0x78} + bytes(30,0)), encodeArgs()); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(-1), u256(-1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_function_type) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { function (uint) external returns (uint) fn; } - function f(S calldata s) external returns (uint256) { - return s.fn(42); - } - function g(uint256 a) external returns (uint256) { - return a * 3; - } - function h(uint256 a) external returns (uint256) { - return 23; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes fn_C_g = m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + bytes(8,0); - bytes fn_C_h = m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("h(uint256)")).asBytes() + bytes(8,0); - ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_g), encodeArgs(42 * 3)); - ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); -} - -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_bytes) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f1(bytes[1] calldata a) external returns (uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); - } - function f2(bytes[1] calldata a, bytes[1] calldata b) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), b[0].length, uint8(b[0][0]), uint8(b[0][1])); - } - function g1(bytes[2] calldata a) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])); - } - function g2(bytes[] calldata a) external returns (uint256[8] memory) { - return [a.length, a[0].length, uint8(a[0][0]), uint8(a[0][1]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes bytes010203 = bytes{1,2,3}+bytes(29,0); - bytes bytes040506 = bytes{4,5,6}+bytes(29,0); - bytes bytes0102 = bytes{1,2}+bytes(30,0); - ABI_CHECK( - callContractFunction("f1(bytes[1])", 0x20, 0x20, 3, bytes010203), - encodeArgs(3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("f2(bytes[1],bytes[1])", 0x40, 0xA0, 0x20, 3, bytes010203, 0x20, 2, bytes0102), - encodeArgs(3, 1, 2, 3, 2, 1, 2) - ); - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x80, 3, bytes010203, 3, bytes040506), - encodeArgs(3, 1, 2, 3, 3, 4, 5, 6) - ); - // same offset for both arrays - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x40, 3, bytes010203), - encodeArgs(3, 1, 2, 3, 3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("g2(bytes[])", 0x20, 2, 0x40, 0x80, 2, bytes0102, 3, bytes040506), - encodeArgs(2, 2, 1, 2, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_dynamic_array_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][] calldata a) external returns (uint, uint256[] memory) { - uint256[] memory m = a[0]; - return (a.length, m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(uint256[][])", 0x20, 1, 0x20, 2, 23, 42), - encodeArgs(1, 0x40, 2, 23, 42) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_bytes_array_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a) external returns (uint, uint, bytes memory) { - bytes memory m = a[0]; - return (a.length, m.length, m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 0x60, 2, bytes{'a','b'} + bytes(30, 0)) - ); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, bytes(32, 'x')), - encodeArgs(1, 32, 0x60, 32, bytes(32, 'x')) - ); - bytes x_zero_a = bytes{'x'} + bytes(30, 0) + bytes{'a'}; - bytes a_zero_x = bytes{'a'} + bytes(30, 0) + bytes{'x'}; - bytes a_m_x = bytes{'a'} + bytes(30, 'm') + bytes{'x'}; - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, x_zero_a), - encodeArgs(1, 32, 0x60, 32, x_zero_a) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_zero_x), - encodeArgs(1, 32, 0x60, 32, a_zero_x) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_m_x), - encodeArgs(1, 32, 0x60, 32, a_m_x) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a, uint256 i) external returns (uint) { - return uint8(a[0][i]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 0, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('a') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 1, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('b') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 2, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs() - ); -} - -BOOST_AUTO_TEST_CASE(calldata_string_array) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(string[] calldata a) external returns (uint, uint, uint, string memory) { - string memory s1 = a[0]; - bytes memory m1 = bytes(s1); - return (a.length, m1.length, uint8(m1[0]), s1); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(string[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 'a', 0x80, 2, bytes{'a', 'b'} + bytes(30, 0)) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) -{ - vector> data { - { 0x0A01, 0x0A02, 0x0A03 }, - { 0x0B01, 0x0B02, 0x0B03, 0x0B04 } - }; - - for (bool outerDynamicallySized: { true, false }) - { - string arrayType = outerDynamicallySized ? "uint256[][]" : "uint256[][2]"; - string sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function test()" + arrayType + R"( calldata a) external returns (uint256) { - return a.length; - } - function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { - return a[i].length; - } - function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return a[i][j]; - } - function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return this.test(a, i, j); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes encoding = encodeArray( - outerDynamicallySized, - true, - data | boost::adaptors::transformed([&](vector const& _values) { - return encodeArray(true, false, _values); - }) - ); - - ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); - for (size_t i = 0; i < data.size(); i++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); - for (size_t j = 0; j < data[i].size(); j++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); - } -} - -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) -{ - vector>> data { - { - { 0x010A01, 0x010A02, 0x010A03 }, - { 0x010B01, 0x010B02, 0x010B03 } - }, - { - { 0x020A01, 0x020A02, 0x020A03 }, - { 0x020B01, 0x020B02, 0x020B03 } - } - }; - - for (bool outerDynamicallySized: { true, false }) - for (bool middleDynamicallySized: { true, false }) - for (bool innerDynamicallySized: { true, false }) - { - // only test dynamically encoded arrays - if (!outerDynamicallySized && !middleDynamicallySized && !innerDynamicallySized) - continue; - - string arrayType = "uint256"; - arrayType += innerDynamicallySized ? "[]" : "[3]"; - arrayType += middleDynamicallySized ? "[]" : "[2]"; - arrayType += outerDynamicallySized ? "[]" : "[2]"; - - string sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function test()" + arrayType + R"( calldata a) external returns (uint256) { - return a.length; - } - function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { - return a[i].length; - } - function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return a[i][j].length; - } - function test()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { - return a[i][j][k]; - } - function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { - return this.test(a, i, j, k); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes encoding = encodeArray( - outerDynamicallySized, - middleDynamicallySized || innerDynamicallySized, - data | boost::adaptors::transformed([&](auto const& _middleData) { - return encodeArray( - middleDynamicallySized, - innerDynamicallySized, - _middleData | boost::adaptors::transformed([&](auto const& _values) { - return encodeArray(innerDynamicallySized, false, _values); - }) - ); - }) - ); - - ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); - for (size_t i = 0; i < data.size(); i++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); - for (size_t j = 0; j < data[i].size(); j++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j].size())); - for (size_t k = 0; k < data[i][j].size(); k++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); - ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs()); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); - } -} - -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_invalid) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][] calldata a) external returns (uint) { - return 42; - } - function g(uint256[][] calldata a) external returns (uint) { - a[0]; - return 42; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // valid access stub - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 0), encodeArgs(42)); - // invalid on argument decoding - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1), encodeArgs()); - // invalid on outer access - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][])", 0x20, 1, 0x20), encodeArgs()); - // invalid on inner access - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1, 0x20, 2, 0x42), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][])", 0x20, 1, 0x20, 2, 0x42), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_invalid_static_middle) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][1][] calldata a) external returns (uint) { - return 42; - } - function g(uint256[][1][] calldata a) external returns (uint) { - a[0]; - return 42; - } - function h(uint256[][1][] calldata a) external returns (uint) { - a[0][0]; - return 42; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // valid access stub - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 0), encodeArgs(42)); - // invalid on argument decoding - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1), encodeArgs()); - // invalid on outer access - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20), encodeArgs()); - // invalid on inner access - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("h(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs()); - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs(42)); - ABI_CHECK(callContractFunction("h(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(literal_strings) -{ - char const* sourceCode = R"( - contract Test { - string public long; - string public medium; - string public short; - string public empty; - function f() public returns (string memory) { - long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; - short = "123"; - empty = ""; - return "Hello, World!"; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - string medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; - string shortStr = "123"; - string hello = "Hello, World!"; - - ABI_CHECK(callContractFunction("f()"), encodeDyn(hello)); - ABI_CHECK(callContractFunction("long()"), encodeDyn(longStr)); - ABI_CHECK(callContractFunction("medium()"), encodeDyn(medium)); - ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); - ABI_CHECK(callContractFunction("empty()"), encodeDyn(string())); -} - -BOOST_AUTO_TEST_CASE(initialise_string_constant) -{ - char const* sourceCode = R"( - contract Test { - string public short = "abcdef"; - string public long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - } - )"; - compileAndRun(sourceCode, 0, "Test"); - string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - string shortStr = "abcdef"; - - ABI_CHECK(callContractFunction("long()"), encodeDyn(longStr)); - ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); -} - -BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 a; mapping(uint => uint) b; uint8 c; } - S s; - function f() public returns (uint) { - S memory x; - if (x.a != 0 || x.c != 0) return 1; - x.a = 4; x.c = 5; - s = x; - if (s.a != 4 || s.c != 5) return 2; - x = S(2, 3); - if (x.a != 2 || x.c != 3) return 3; - x = s; - if (s.a != 4 || s.c != 5) return 4; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(string_bytes_conversion) -{ - char const* sourceCode = R"( - contract Test { - string s; - bytes b; - function f(string memory _s, uint n) public returns (byte) { - b = bytes(_s); - s = string(b); - return bytes(s)[n]; - } - function l() public returns (uint) { return bytes(s).length; } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction( - "f(string,uint256)", - u256(0x40), - u256(2), - u256(6), - string("abcdef") - ), encodeArgs("c")); - ABI_CHECK(callContractFunction("l()"), encodeArgs(u256(6))); -} - -BOOST_AUTO_TEST_CASE(string_as_mapping_key) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => uint) data; - function set(string memory _s, uint _v) public { data[_s] = _v; } - function get(string memory _s) public returns (uint) { return data[_s]; } - } - )"; - - vector strings{ - "Hello, World!", - "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", - "", - "1" - }; - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - for (unsigned i = 0; i < strings.size(); i++) - ABI_CHECK(callContractFunction( - "set(string,uint256)", - u256(0x40), - u256(7 + i), - u256(strings[i].size()), - strings[i] - ), encodeArgs()); - for (unsigned i = 0; i < strings.size(); i++) - ABI_CHECK(callContractFunction( - "get(string)", - u256(0x20), - u256(strings[i].size()), - strings[i] - ), encodeArgs(u256(7 + i))); - ) -} - -BOOST_AUTO_TEST_CASE(string_as_public_mapping_key) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => uint) public data; - function set(string memory _s, uint _v) public { data[_s] = _v; } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - vector strings{ - "Hello, World!", - "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", - "", - "1" - }; - for (unsigned i = 0; i < strings.size(); i++) - ABI_CHECK(callContractFunction( - "set(string,uint256)", - u256(0x40), - u256(7 + i), - u256(strings[i].size()), - strings[i] - ), encodeArgs()); - for (unsigned i = 0; i < strings.size(); i++) - ABI_CHECK(callContractFunction( - "data(string)", - u256(0x20), - u256(strings[i].size()), - strings[i] - ), encodeArgs(u256(7 + i))); -} - -BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => mapping(string => uint)) public data; - function set(string memory _s, string memory _s2, uint _v) public { - data[_s][_s2] = _v; } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - vector strings{ - "Hello, World!", - "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", - "", - "1", - "last one" - }; - for (unsigned i = 0; i + 1 < strings.size(); i++) - ABI_CHECK(callContractFunction( - "set(string,string,uint256)", - u256(0x60), - u256(roundTo32(0x80 + strings[i].size())), - u256(7 + i), - u256(strings[i].size()), - strings[i], - u256(strings[i+1].size()), - strings[i+1] - ), encodeArgs()); - for (unsigned i = 0; i + 1 < strings.size(); i++) - ABI_CHECK(callContractFunction( - "data(string,string)", - u256(0x40), - u256(roundTo32(0x60 + strings[i].size())), - u256(strings[i].size()), - strings[i], - u256(strings[i+1].size()), - strings[i+1] - ), encodeArgs(u256(7 + i))); -} - -BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => - mapping(int => - mapping(address => - mapping(bytes => int)))) public data; - - function set( - string memory _s1, - int _s2, - address _s3, - bytes memory _s4, - int _value - ) public - { - data[_s1][_s2][_s3][_s4] = _value; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - struct Index - { - string s1; - int s2; - int s3; - string s4; - }; - - vector data{ - { "aabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcbc", 4, 23, "efg" }, - { "tiaron", 456, 63245, "908apzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapz" }, - { "", 2345, 12934, "665i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i5iart" }, - { "¡¿…", 9781, 8148, "" }, - { "ρν♀♀ω₂₃♀", 929608, 303030, "" } - }; - - for (size_t i = 0; i + 1 < data.size(); i++) - ABI_CHECK(callContractFunction( - "set(string,int256,address,bytes,int256)", - u256(0xA0), - u256(data[i].s2), - u256(data[i].s3), - u256(roundTo32(0xC0 + data[i].s1.size())), - u256(i - 3), - u256(data[i].s1.size()), - data[i].s1, - u256(data[i].s4.size()), - data[i].s4 - ), encodeArgs()); - for (size_t i = 0; i + 1 < data.size(); i++) - ABI_CHECK(callContractFunction( - "data(string,int256,address,bytes)", - u256(0x80), - u256(data[i].s2), - u256(data[i].s3), - u256(roundTo32(0xA0 + data[i].s1.size())), - u256(data[i].s1.size()), - data[i].s1, - u256(data[i].s4.size()), - data[i].s4 - ), encodeArgs(u256(i - 3))); -} - -BOOST_AUTO_TEST_CASE(accessor_for_state_variable) -{ - char const* sourceCode = R"( - contract Lotto { - uint public ticketPrice = 500; - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(500))); - ); -} - -BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable) -{ - char const* sourceCode = R"( - contract Lotto{ - uint constant public ticketPrice = 555; - } - )"; - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(555))); -} - -BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) -{ - char const* text = R"( - contract Scope { - uint stateVar = 42; - - function getStateVar() public view returns (uint stateVar) { - stateVar = Scope.stateVar; - } - } - )"; - compileAndRun(text); - ABI_CHECK(callContractFunction("getStateVar()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(state_variable_local_variable_mixture) -{ - char const* sourceCode = R"( - contract A { - uint x = 1; - uint y = 2; - function a() public returns (uint x) { - x = A.y; - } - } - )"; - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(inherited_function) { - char const* sourceCode = R"( - contract A { function f() virtual internal returns (uint) { return 1; } } - contract B is A { - function f() internal override returns (uint) { return 2; } - function g() public returns (uint) { - return A.f(); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory) { - char const* sourceCode = R"( - contract A { function f(uint[] calldata a) virtual external returns (uint) { return a[0]; } } - contract B is A { - function f(uint[] memory a) public override returns (uint) { return a[1]; } - function g() public returns (uint) { - uint[] memory m = new uint[](2); - m[0] = 42; - m[1] = 23; - return A(this).f(m); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(23))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory_interface) { - char const* sourceCode = R"( - interface I { function f(uint[] calldata a) external returns (uint); } - contract A is I { function f(uint[] memory a) public override returns (uint) { return 42; } } - contract B { - function f(uint[] memory a) public returns (uint) { return a[1]; } - function g() public returns (uint) { - I i = I(new A()); - return i.f(new uint[](2)); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_calldata_interface) { - char const* sourceCode = R"( - interface I { function f(uint[] calldata a) external returns (uint); } - contract A is I { function f(uint[] calldata a) external override returns (uint) { return 42; } } - contract B { - function f(uint[] memory a) public returns (uint) { return a[1]; } - function g() public returns (uint) { - I i = I(new A()); - return i.f(new uint[](2)); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) { - char const* sourceCode = R"( - library A { function f() internal returns (uint) { return 1; } } - contract B { - function f() internal returns (uint) { return 2; } - function g() public returns (uint) { - return A.f(); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(inherited_constant_state_var) -{ - char const* sourceCode = R"( - contract A { - uint constant x = 7; - } - contract B is A { - function f() public returns (uint) { - return A.x; - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(constant_string_literal) -{ - char const* sourceCode = R"( - contract Test { - bytes32 constant public b = "abcdefghijklmnopq"; - string constant public x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; - - constructor() public { - string memory xx = x; - bytes32 bb = b; - } - function getB() public returns (bytes32) { return b; } - function getX() public returns (string memory) { return x; } - function getX2() public returns (string memory r) { r = x; } - function unused() public returns (uint) { - "unusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunused"; - return 2; - } - } - )"; - - compileAndRun(sourceCode); - string longStr = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; - string shortStr = "abcdefghijklmnopq"; - ABI_CHECK(callContractFunction("b()"), encodeArgs(shortStr)); - ABI_CHECK(callContractFunction("x()"), encodeDyn(longStr)); - ABI_CHECK(callContractFunction("getB()"), encodeArgs(shortStr)); - ABI_CHECK(callContractFunction("getX()"), encodeDyn(longStr)); - ABI_CHECK(callContractFunction("getX2()"), encodeDyn(longStr)); - ABI_CHECK(callContractFunction("unused()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => uint) data; - function f() public returns (uint) { - data["abc"] = 2; - return data["abc"]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(library_call) -{ - char const* sourceCode = R"( - library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } - contract Test { - function f(uint x) public returns (uint) { - return Lib.m(x, 9); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9)); -} - -BOOST_AUTO_TEST_CASE(library_function_external) -{ - char const* sourceCode = R"( - library Lib { function m(bytes calldata b) external pure returns (byte) { return b[2]; } } - contract Test { - function f(bytes memory b) public pure returns (byte) { - return Lib.m(b); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c")); -} - -BOOST_AUTO_TEST_CASE(library_stray_values) -{ - char const* sourceCode = R"( - library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } - contract Test { - function f(uint x) public returns (uint) { - Lib; - Lib.m; - return x + 9; - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(cross_contract_types) -{ - char const* sourceCode = R"( - contract Lib { struct S {uint a; uint b; } } - contract Test { - function f() public returns (uint r) { - Lib.S memory x = Lib.S({a: 2, b: 3}); - r = x.b; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(simple_throw) -{ - char const* sourceCode = R"( - contract Test { - function f(uint x) public returns (uint) { - if (x > 10) - return x + 10; - else - revert(); - return 2; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(11)), encodeArgs(u256(21))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(strings_in_struct) -{ - char const* sourceCode = R"( - contract buggystruct { - Buggy public bug; - - struct Buggy { - uint first; - uint second; - uint third; - string last; - } - - constructor() public { - bug = Buggy(10, 20, 30, "asdfghjkl"); - } - function getFirst() public returns (uint) - { - return bug.first; - } - function getSecond() public returns (uint) - { - return bug.second; - } - function getThird() public returns (uint) - { - return bug.third; - } - function getLast() public returns (string memory) - { - return bug.last; - } - } - )"; - compileAndRun(sourceCode); - string s = "asdfghjkl"; - ABI_CHECK(callContractFunction("getFirst()"), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("getSecond()"), encodeArgs(u256(20))); - ABI_CHECK(callContractFunction("getThird()"), encodeArgs(u256(30))); - ABI_CHECK(callContractFunction("getLast()"), encodeDyn(s)); -} - -BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) -{ - char const* sourceCode = R"( - contract A { - function f(uint16 input) public pure returns (uint16[5] memory arr) - { - arr[0] = input; - arr[1] = ++input; - arr[2] = ++input; - arr[3] = ++input; - arr[4] = ++input; - } - } - contract B { - function f() public returns (uint16[5] memory res, uint16[5] memory res2) - { - A a = new A(); - res = a.f(2); - res2 = a.f(1000); - } - } - )"; - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - u256(2), u256(3), u256(4), u256(5), u256(6), // first return argument - u256(1000), u256(1001), u256(1002), u256(1003), u256(1004)) // second return argument - ); -} - -BOOST_AUTO_TEST_CASE(internal_types_in_library) -{ - char const* sourceCode = R"( - library Lib { - function find(uint16[] storage _haystack, uint16 _needle) public view returns (uint) - { - for (uint i = 0; i < _haystack.length; ++i) - if (_haystack[i] == _needle) - return i; - return uint(-1); - } - } - contract Test { - mapping(string => uint16[]) data; - function f() public returns (uint a, uint b) - { - while (data["abc"].length < 20) - data["abc"].push(); - data["abc"][4] = 9; - data["abc"][17] = 3; - a = Lib.find(data["abc"], 9); - b = Lib.find(data["abc"], 3); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4), u256(17))); -} - -BOOST_AUTO_TEST_CASE(mapping_arguments_in_library) -{ - char const* sourceCode = R"( - library Lib { - function set(mapping(uint => uint) storage m, uint key, uint value) internal - { - m[key] = value; - } - function get(mapping(uint => uint) storage m, uint key) internal view returns (uint) - { - return m[key]; - } - } - contract Test { - mapping(uint => uint) m; - function set(uint256 key, uint256 value) public returns (uint) - { - uint oldValue = Lib.get(m, key); - Lib.set(m, key, value); - return oldValue; - } - function get(uint256 key) public view returns (uint) { - return Lib.get(m, key); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(42)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(84)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(7)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(84))); - ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(21)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(42)), encodeArgs(u256(84))); - ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(14)), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(21))); - ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(14))); -} - -BOOST_AUTO_TEST_CASE(mapping_returns_in_library) -{ - char const* sourceCode = R"( - library Lib { - function choose_mapping(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) internal pure returns(mapping(uint=>uint) storage) - { - return c ? a : b; - } - } - contract Test { - mapping(uint => uint) a; - mapping(uint => uint) b; - function set(bool choice, uint256 key, uint256 value) public returns (uint) - { - mapping(uint => uint) storage m = Lib.choose_mapping(a, b, choice); - uint oldValue = m[key]; - m[key] = value; - return oldValue; - } - function get(bool choice, uint256 key) public view returns (uint) { - return Lib.choose_mapping(a, b, choice)[key]; - } - function get_a(uint256 key) public view returns (uint) { - return a[key]; - } - function get_b(uint256 key) public view returns (uint) { - return b[key]; - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(42)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(84)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(7)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(10)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(11)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(12)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(84))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(84))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(11))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(12))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(11))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(12))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(21)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(42)), encodeArgs(u256(84))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(14)), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(30)), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(31)), encodeArgs(u256(11))); - ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(32)), encodeArgs(u256(12))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(21))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(14))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(21))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(14))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(30))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(31))); - ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(32))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(30))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(31))); - ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(32))); -} - -BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named) -{ - char const* sourceCode = R"( - library Lib { - function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b) internal returns(mapping(uint=>uint) storage r) - { - r = a; - r[1] = 42; - r = b; - r[1] = 21; - } - } - contract Test { - mapping(uint => uint) a; - mapping(uint => uint) b; - function f() public returns (uint, uint, uint, uint, uint, uint) - { - Lib.f(a, b)[2] = 84; - return (a[0], a[1], a[2], b[0], b[1], b[2]); - } - function g() public returns (uint, uint, uint, uint, uint, uint) - { - mapping(uint => uint) storage m = Lib.f(a, b); - m[2] = 17; - return (a[0], a[1], a[2], b[0], b[1], b[2]); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(84))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17))); -} - -BOOST_AUTO_TEST_CASE(using_library_mappings_public) -{ - char const* sourceCode = R"( - library Lib { - function set(mapping(uint => uint) storage m, uint key, uint value) public - { - m[key] = value; - } - } - contract Test { - mapping(uint => uint) m1; - mapping(uint => uint) m2; - function f() public returns (uint, uint, uint, uint, uint, uint) - { - Lib.set(m1, 0, 1); - Lib.set(m1, 2, 42); - Lib.set(m2, 0, 23); - Lib.set(m2, 2, 99); - return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); -} - -BOOST_AUTO_TEST_CASE(using_library_mappings_external) -{ - char const* libSourceCode = R"( - library Lib { - function set(mapping(uint => uint) storage m, uint key, uint value) external - { - m[key] = value * 2; - } - } - )"; - char const* sourceCode = R"( - library Lib { - function set(mapping(uint => uint) storage m, uint key, uint value) external {} - } - contract Test { - mapping(uint => uint) m1; - mapping(uint => uint) m2; - function f() public returns (uint, uint, uint, uint, uint, uint) - { - Lib.set(m1, 0, 1); - Lib.set(m1, 2, 42); - Lib.set(m2, 0, 23); - Lib.set(m2, 2, 99); - return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); - } - } - )"; - for (auto v2: {false, true}) - { - string prefix = v2 ? "pragma experimental ABIEncoderV2;\n" : ""; - compileAndRun(prefix + libSourceCode, 0, "Lib"); - compileAndRun(prefix + sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2), u256(0), u256(84), u256(46), u256(0), u256(198))); - } -} - -BOOST_AUTO_TEST_CASE(using_library_mappings_return) -{ - char const* sourceCode = R"( - library Lib { - function choose(mapping(uint => mapping(uint => uint)) storage m, uint key) external returns (mapping(uint => uint) storage) { - return m[key]; - } - } - contract Test { - mapping(uint => mapping(uint => uint)) m; - function f() public returns (uint, uint, uint, uint, uint, uint) - { - Lib.choose(m, 0)[0] = 1; - Lib.choose(m, 0)[2] = 42; - Lib.choose(m, 1)[0] = 23; - Lib.choose(m, 1)[2] = 99; - return (m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]); - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); -} - -BOOST_AUTO_TEST_CASE(using_library_structs) -{ - char const* sourceCode = R"( - library Lib { - struct Data { uint a; uint[] b; } - function set(Data storage _s) public - { - _s.a = 7; - while (_s.b.length < 20) - _s.b.push(); - _s.b[19] = 8; - } - } - contract Test { - mapping(string => Lib.Data) data; - function f() public returns (uint a, uint b) - { - Lib.set(data["abc"]); - a = data["abc"].a; - b = data["abc"].b[19]; - } - } - )"; - compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), u256(8))); -} - -BOOST_AUTO_TEST_CASE(library_struct_as_an_expression) -{ - char const* sourceCode = R"( - library Arst { - struct Foo { - int Things; - int Stuff; - } - } - - contract Tsra { - function f() public returns(uint) { - Arst.Foo; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "Tsra"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(library_enum_as_an_expression) -{ - char const* sourceCode = R"( - library Arst { - enum Foo { - Things, - Stuff - } - } - - contract Tsra { - function f() public returns(uint) { - Arst.Foo; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "Tsra"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(short_strings) -{ - // This test verifies that the byte array encoding that combines length and data works - // correctly. - char const* sourceCode = R"( - contract A { - bytes public data1 = "123"; - bytes data2; - function lengthChange() public returns (uint) - { - // store constant in short and long string - data1 = "123"; - if (!equal(data1, "123")) return 1; - data2 = "12345678901234567890123456789012345678901234567890a"; - if (data2[17] != "8") return 3; - if (data2.length != 51) return 4; - if (data2[data2.length - 1] != "a") return 5; - // change length: short -> short - while (data1.length < 5) - data1.push(); - if (data1.length != 5) return 6; - data1[4] = "4"; - if (data1[0] != "1") return 7; - if (data1[4] != "4") return 8; - // change length: short -> long - while (data1.length < 80) - data1.push(); - if (data1.length != 80) return 9; - while (data1.length > 70) - data1.pop(); - if (data1.length != 70) return 9; - if (data1[0] != "1") return 10; - if (data1[4] != "4") return 11; - for (uint i = 0; i < data1.length; i ++) - data1[i] = byte(uint8(i * 3)); - if (uint8(data1[4]) != 4 * 3) return 12; - if (uint8(data1[67]) != 67 * 3) return 13; - // change length: long -> short - while (data1.length > 22) - data1.pop(); - if (data1.length != 22) return 14; - if (uint8(data1[21]) != 21 * 3) return 15; - if (uint8(data1[2]) != 2 * 3) return 16; - // change length: short -> shorter - while (data1.length > 19) - data1.pop(); - if (data1.length != 19) return 17; - if (uint8(data1[7]) != 7 * 3) return 18; - // and now again to original size - while (data1.length < 22) - data1.push(); - if (data1.length != 22) return 19; - if (data1[21] != 0) return 20; - while (data1.length > 0) - data1.pop(); - while (data2.length > 0) - data2.pop(); - } - function copy() public returns (uint) { - bytes memory x = "123"; - bytes memory y = "012345678901234567890123456789012345678901234567890123456789"; - bytes memory z = "1234567"; - data1 = x; - data2 = y; - if (!equal(data1, x)) return 1; - if (!equal(data2, y)) return 2; - // lengthen - data1 = y; - if (!equal(data1, y)) return 3; - // shorten - data1 = x; - if (!equal(data1, x)) return 4; - // change while keeping short - data1 = z; - if (!equal(data1, z)) return 5; - // copy storage -> storage - data1 = x; - data2 = y; - // lengthen - data1 = data2; - if (!equal(data1, y)) return 6; - // shorten - data1 = x; - data2 = data1; - if (!equal(data2, x)) return 7; - bytes memory c = data2; - data1 = c; - if (!equal(data1, x)) return 8; - data1 = ""; - data2 = ""; - } - function deleteElements() public returns (uint) { - data1 = "01234"; - delete data1[2]; - if (data1[2] != 0) return 1; - if (data1[0] != "0") return 2; - if (data1[3] != "3") return 3; - delete data1; - if (data1.length != 0) return 4; - } - - function equal(bytes storage a, bytes memory b) internal returns (bool) { - if (a.length != b.length) return false; - for (uint i = 0; i < a.length; ++i) if (a[i] != b[i]) return false; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "A"); - ABI_CHECK(callContractFunction("data1()"), encodeDyn(string("123"))); - ABI_CHECK(callContractFunction("lengthChange()"), encodeArgs(u256(0))); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("deleteElements()"), encodeArgs(u256(0))); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy()"), encodeArgs(u256(0))); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(calldata_offset) -{ - // This tests a specific bug that was caused by not using the correct memory offset in the - // calldata unpacker. - char const* sourceCode = R"( - contract CB - { - address[] _arr; - string public last = "nd"; - constructor(address[] memory guardians) public - { - _arr = guardians; - } - } - )"; - compileAndRun(sourceCode, 0, "CB", encodeArgs(u256(0x20), u256(0x00))); - ABI_CHECK(callContractFunction("last()", encodeArgs()), encodeDyn(string("nd"))); -} - -BOOST_AUTO_TEST_CASE(contract_binary_dependencies) -{ - char const* sourceCode = R"( - contract A { function f() public { new B(); } } - contract B { function f() public { } } - contract C { function f() public { new B(); } } - )"; - compileAndRun(sourceCode); -} - -BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) -{ - char const* sourceCode = R"( - library lib {} - contract c { - constructor() public payable {} - function f(address payable x) public returns (bool) { - return x.send(1); - } - receive () external payable {} - } - )"; - compileAndRun(sourceCode, 0, "lib"); - Address libraryAddress = m_contractAddress; - compileAndRun(sourceCode, 10, "c"); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); - BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); - ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(libraryAddress))), encodeArgs(false)); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); - BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); - ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(m_contractAddress))), encodeArgs(true)); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); - BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration) -{ - char const* sourceCode = R"( - contract C { - function g() public returns (uint a, uint b, uint c) { - a = 1; b = 2; c = 3; - } - function h() public returns (uint a, uint b, uint c, uint d) { - a = 1; b = 2; c = 3; d = 4; - } - function f1() public returns (bool) { - (uint x, uint y, uint z) = g(); - if (x != 1 || y != 2 || z != 3) return false; - (, uint a,) = g(); - if (a != 2) return false; - (uint b, , ) = g(); - if (b != 1) return false; - (, , uint c) = g(); - if (c != 3) return false; - return true; - } - function f2() public returns (bool) { - (uint a1, , uint a3, ) = h(); - if (a1 != 1 || a3 != 3) return false; - (uint b1, uint b2, , ) = h(); - if (b1 != 1 || b2 != 2) return false; - (, uint c2, uint c3, ) = h(); - if (c2 != 2 || c3 != 3) return false; - (, , uint d3, uint d4) = h(); - if (d3 != 3 || d4 != 4) return false; - (uint e1, , uint e3, uint e4) = h(); - if (e1 != 1 || e3 != 3 || e4 != 4) return false; - return true; - } - function f() public returns (bool) { - return f1() && f2(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(typed_multi_variable_declaration) -{ - char const* sourceCode = R"( - contract C { - struct S { uint x; } - S s; - function g() internal returns (uint, S storage, uint) { - s.x = 7; - return (1, s, 2); - } - function f() public returns (bool) { - (uint x1, S storage y1, uint z1) = g(); - if (x1 != 1 || y1.x != 7 || z1 != 2) return false; - (, S storage y2,) = g(); - if (y2.x != 7) return false; - (uint x2,,) = g(); - if (x2 != 1) return false; - (,,uint z2) = g(); - if (z2 != 2) return false; - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(tuples) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - uint[] m_c; - function g() internal returns (uint a, uint b, uint[] storage c) { - return (1, 2, data); - } - function h() external returns (uint a, uint b) { - return (5, 6); - } - function f() public returns (uint) { - data.push(3); - uint a; uint b; - (a, b) = this.h(); - if (a != 5 || b != 6) return 1; - uint[] storage c = m_c; - (a, b, c) = g(); - if (a != 1 || b != 2 || c[0] != 3) return 2; - (a, b) = (b, a); - if (a != 2 || b != 1) return 3; - (a, , b, , ) = (8, 9, 10, 11, 12); - if (a != 8 || b != 10) return 4; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(string_tuples) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (string memory, uint) { - return ("abc", 8); - } - function g() public returns (string memory, string memory) { - return (h(), "def"); - } - function h() public returns (string memory) { - return ("abc"); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x40), u256(8), u256(3), string("abc"))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x40), u256(0x80), u256(3), string("abc"), u256(3), string("def"))); -} - -BOOST_AUTO_TEST_CASE(decayed_tuple) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint x = 1; - (x) = 2; - return x; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(inline_tuple_with_rational_numbers) -{ - char const* sourceCode = R"( - contract c { - function f() public returns (int8) { - int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; - return foo3[0]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(destructuring_assignment) -{ - char const* sourceCode = R"( - contract C { - uint x = 7; - bytes data; - uint[] y; - uint[] arrayData; - function returnsArray() public returns (uint[] memory) { - arrayData = new uint[](9); - arrayData[2] = 5; - arrayData[7] = 4; - return arrayData; - } - function f(bytes memory s) public returns (uint) { - uint loc; - uint[] memory memArray; - (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2); - if (loc != 8) return 1; - if (x != 4) return 2; - if (y.length != 9) return 3; - if (y[2] != 5) return 4; - if (y[7] != 4) return 5; - if (data.length != s.length) return 6; - if (data[3] != s[3]) return 7; - if (arrayData[3] != 2) return 8; - (memArray, loc) = (arrayData, 3); - if (loc != 3) return 9; - if (memArray.length != arrayData.length) return 10; - bytes memory memBytes; - (x, memBytes, y[2], , ) = (456, s, 789, 101112, 131415); - if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), string("abcde")), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(lone_struct_array_type) -{ - char const* sourceCode = R"( - contract C { - struct s { uint a; uint b;} - function f() public returns (uint) { - s[7][]; // This is only the type, should not have any effect - return 3; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); - ) -} - -BOOST_AUTO_TEST_CASE(create_memory_array) -{ - char const* sourceCode = R"( - contract C { - struct S { uint[2] a; bytes b; } - function f() public returns (byte, uint, uint, byte) { - bytes memory x = new bytes(200); - x[199] = 'A'; - uint[2][] memory y = new uint[2][](300); - y[203][1] = 8; - S[] memory z = new S[](180); - z[170].a[1] = 4; - z[170].b = new bytes(102); - z[170].b[99] = 'B'; - return (x[199], y[203][1], z[170].a[1], z[170].b[99]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(string("A"), u256(8), u256(4), string("B"))); -} - -BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) -{ - // Check allocation size of byte array. Should be 32 plus length rounded up to next - // multiple of 32 - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint d1, uint d2, uint d3, uint memsize) { - bytes memory b1 = new bytes(31); - bytes memory b2 = new bytes(32); - bytes memory b3 = new bytes(256); - bytes memory b4 = new bytes(31); - assembly { - d1 := sub(b2, b1) - d2 := sub(b3, b2) - d3 := sub(b4, b3) - memsize := msize() - } - } - } - )"; - if (!m_optimiserSettings.runYulOptimiser) - { - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256, 0x260)); - } -} - -BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) -{ - // Computes binomial coefficients the chinese way - char const* sourceCode = R"( - contract C { - function f(uint n, uint k) public returns (uint) { - uint[][] memory rows = new uint[][](n + 1); - for (uint i = 1; i <= n; i++) { - rows[i] = new uint[](i); - rows[i][0] = rows[i][rows[i].length - 1] = 1; - for (uint j = 1; j < i - 1; j++) - rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; - } - return rows[n][k - 1]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(3), u256(1))), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70))); -} - -BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint[][] memory x = new uint[][](42); - assert(x[0].length == 0); - x[0] = new uint[](1); - x[0][0] = 1; - assert(x[4].length == 0); - x[4] = new uint[](1); - x[4][0] = 2; - assert(x[10].length == 0); - x[10] = new uint[](1); - x[10][0] = 44; - uint[][] memory y = new uint[][](24); - assert(y[0].length == 0); - y[0] = new uint[](1); - y[0][0] = 1; - assert(y[4].length == 0); - y[4] = new uint[](1); - y[4][0] = 2; - assert(y[10].length == 0); - y[10] = new uint[](1); - y[10][0] = 88; - if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88)) - return 7; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(memory_overwrite) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (bytes memory x) { - x = "12345"; - x[3] = 0x61; - x[0] = 0x62; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeDyn(string("b23a5"))); -} - -BOOST_AUTO_TEST_CASE(addmod_mulmod) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint) { - // Note that this only works because computation on literals is done using - // unbounded integers. - if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) - return 1; - if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) - return 2; - return 0; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) -{ - char const* sourceCode = R"( - contract C { - function f(uint d) public pure returns (uint) { - addmod(1, 2, d); - return 2; - } - function g(uint d) public pure returns (uint) { - mulmod(1, 2, d); - return 2; - } - function h() public pure returns (uint) { - mulmod(0, 1, 2); - mulmod(1, 0, 2); - addmod(0, 1, 2); - addmod(1, 0, 2); - return 2; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("g(uint)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(divisiod_by_zero) -{ - char const* sourceCode = R"( - contract C { - function div(uint a, uint b) public returns (uint) { - return a / b; - } - function mod(uint a, uint b) public returns (uint) { - return a % b; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("div(uint256,uint256)", 7, 2), encodeArgs(u256(3))); - // throws - ABI_CHECK(callContractFunction("div(uint256,uint256)", 7, 0), encodeArgs()); - ABI_CHECK(callContractFunction("mod(uint256,uint256)", 7, 2), encodeArgs(u256(1))); - // throws - ABI_CHECK(callContractFunction("mod(uint256,uint256)", 7, 0), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(string_allocation_bug) -{ - char const* sourceCode = R"( - contract Sample - { - struct s { uint16 x; uint16 y; string a; string b;} - s[2] public p; - constructor() public { - s memory m; - m.x = 0xbbbb; - m.y = 0xcccc; - m.a = "hello"; - m.b = "world"; - p[0] = m; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("p(uint256)", 0), encodeArgs( - u256(0xbbbb), - u256(0xcccc), - u256(0x80), - u256(0xc0), - u256(5), - string("hello"), - u256(5), - string("world") - )); -} - -BOOST_AUTO_TEST_CASE(using_for_function_on_int) -{ - char const* sourceCode = R"( - library D { function double(uint self) public returns (uint) { return 2*self; } } - contract C { - using D for uint; - function f(uint a) public returns (uint) { - return a.double(); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(9)), encodeArgs(u256(2 * 9))); -} - -BOOST_AUTO_TEST_CASE(using_for_function_on_struct) -{ - char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s public x; - function f(uint a) public returns (uint) { - x.a = 3; - return x.mul(a); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(3 * 7))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(3 * 7))); -} - -BOOST_AUTO_TEST_CASE(using_for_overload) -{ - char const* sourceCode = R"( - library D { - struct s { uint a; } - function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } - function mul(s storage self, bytes32 x) public returns (bytes32) { } - } - contract C { - using D for D.s; - D.s public x; - function f(uint a) public returns (uint) { - x.a = 6; - return x.mul(a); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); -} - -BOOST_AUTO_TEST_CASE(using_for_by_name) -{ - char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s public x; - function f(uint a) public returns (uint) { - x.a = 6; - return x.mul({x: a}); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); -} - -BOOST_AUTO_TEST_CASE(bound_function_in_function) -{ - char const* sourceCode = R"( - library L { - function g(function() internal returns (uint) _t) internal returns (uint) { return _t(); } - } - contract C { - using L for *; - function f() public returns (uint) { - return t.g(); - } - function t() public pure returns (uint) { return 7; } - } - )"; - compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(bound_function_in_var) -{ - char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s public x; - function f(uint a) public returns (uint) { - x.a = 6; - return (x.mul)({x: a}); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); -} - -BOOST_AUTO_TEST_CASE(bound_function_to_string) -{ - char const* sourceCode = R"( - library D { function length(string memory self) public returns (uint) { return bytes(self).length; } } - contract C { - using D for string; - string x; - function f() public returns (uint) { - x = "abc"; - return x.length(); - } - function g() public returns (uint) { - string memory s = "abc"; - return s.length(); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_strings) -{ - char const* sourceCode = R"( - contract C { - string s = "doh"; - function f() public returns (string memory, string memory) { - string memory t = "ray"; - string[3] memory x = [s, t, "mi"]; - return (x[1], x[2]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x40), u256(0x80), u256(3), string("ray"), u256(2), string("mi"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_strings_from_document) -{ - char const* sourceCode = R"( - contract C { - function f(uint i) public returns (string memory) { - string[4] memory x = ["This", "is", "an", "array"]; - return (x[i]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0x20), u256(4), string("This"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(0x20), u256(2), string("is"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0x20), u256(2), string("an"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(0x20), u256(5), string("array"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_ints) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint x, uint y) { - x = 3; - y = 6; - uint[2] memory z = [x, y]; - return (z[0], z[1]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3, 6)); -} - -BOOST_AUTO_TEST_CASE(inline_array_index_access_ints) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return ([1, 2, 3, 4][2]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); -} - -BOOST_AUTO_TEST_CASE(inline_array_index_access_strings) -{ - char const* sourceCode = R"( - contract C { - string public tester; - function f() public returns (string memory) { - return (["abc", "def", "g"][0]); - } - function test() public { - tester = f(); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - ABI_CHECK(callContractFunction("tester()"), encodeArgs(u256(0x20), u256(3), string("abc"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_return) -{ - char const* sourceCode = R"( - contract C { - uint8[] tester; - function f() public returns (uint8[5] memory) { - return ([1,2,3,4,5]); - } - function test() public returns (uint8, uint8, uint8, uint8, uint8) { - tester = f(); - return (tester[0], tester[1], tester[2], tester[3], tester[4]); - } - - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1, 2, 3, 4, 5)); -} - -BOOST_AUTO_TEST_CASE(inline_array_singleton) -{ - // This caused a failure since the type was not converted to its mobile type. - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return [4][0]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(inline_long_string_return) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (string memory) { - return (["somethingShort", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"][1]); - } - } - )"; - - string strLong = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeDyn(strLong)); -} - -BOOST_AUTO_TEST_CASE(fixed_bytes_index_access) -{ - char const* sourceCode = R"( - contract C { - bytes16[] public data; - function f(bytes32 x) public returns (byte) { - return x[2]; - } - function g(bytes32 x) public returns (uint) { - data = [x[0], x[1], x[2]]; - data[0] = "12345"; - return uint(uint8(data[0][4])); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(bytes32)", "789"), encodeArgs("9")); - ABI_CHECK(callContractFunction("g(bytes32)", "789"), encodeArgs(u256(int('5')))); - ABI_CHECK(callContractFunction("data(uint256)", u256(1)), encodeArgs("8")); -} - -BOOST_AUTO_TEST_CASE(fixed_bytes_length_access) -{ - char const* sourceCode = R"( - contract C { - byte a; - function f(bytes32 x) public returns (uint, uint, uint) { - return (x.length, bytes16(uint128(2)).length, a.length + 7); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(bytes32)", "789"), encodeArgs(u256(32), u256(16), u256(8))); -} - -BOOST_AUTO_TEST_CASE(byte_optimization_bug) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint a) { - assembly { - a := byte(x, 31) - } - } - function g(uint x) public returns (uint a) { - assembly { - a := byte(31, x) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("g(uint256)", u256(2)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_write_to_stack) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint r, bytes32 r2) { - assembly { r := 7 r2 := "abcdef" } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), string("abcdef"))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_read_and_write_stack) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint r) { - for (uint x = 0; x < 10; ++x) - assembly { r := add(r, x) } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(45))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_memory_access) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (bytes memory) { - bytes memory x = new bytes(5); - for (uint i = 0; i < x.length; ++i) - x[i] = byte(uint8(i + 1)); - assembly { mstore(add(x, 32), "12345678901234567890123456789012") } - return x; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0x20), u256(5), string("12345"))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access) -{ - char const* sourceCode = R"( - contract C { - uint16 x; - uint16 public y; - uint public z; - function f() public returns (bool) { - uint off1; - uint off2; - assembly { - sstore(z_slot, 7) - off1 := z_offset - off2 := y_offset - } - assert(off1 == 0); - assert(off2 == 2); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("z()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_inside_function) -{ - char const* sourceCode = R"( - contract C { - uint16 x; - uint16 public y; - uint public z; - function f() public returns (bool) { - uint off1; - uint off2; - assembly { - function f() -> o1 { - sstore(z_slot, 7) - o1 := y_offset - } - off2 := f() - } - assert(off2 == 2); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("z()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer) -{ - char const* sourceCode = R"( - contract C { - struct Data { uint contents; } - uint public separator; - Data public a; - uint public separator2; - function f() public returns (bool) { - Data storage x = a; - uint off; - assembly { - sstore(x_slot, 7) - off := x_offset - } - assert(off == 0); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("separator()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("separator2()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - return(0, 0x60) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let a1, b1, c1 - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - return(0, 0x60) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let d := 0x10 - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - mstore(0x60, d) - return(0, 0x80) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7), u256(0x10))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_embedded_function_call) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let d := 0x10 - function asmfun(a, b, c) -> x, y, z { - x := g(a) - function g(r) -> s { s := mul(r, r) } - y := g(b) - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - mstore(0x60, d) - return(0, 0x80) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(4), u256(7), u256(0x10))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_if) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - if gt(a, 1) { b := 2 } - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_switch) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - switch a - case 1 { b := 8 } - case 2 { b := 9 } - default { b := 2 } - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(9))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_recursion) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - function fac(n) -> nf { - switch n - case 0 { nf := 1 } - case 1 { nf := 1 } - default { nf := mul(n, fac(sub(n, 1))) } - } - b := fac(a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("f(uint256)", u256(4)), encodeArgs(u256(24))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_for) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - function fac(n) -> nf { - nf := 1 - for { let i := n } gt(i, 0) { i := sub(i, 1) } { - nf := mul(nf, i) - } - } - b := fac(a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("f(uint256)", u256(4)), encodeArgs(u256(24))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_for2) -{ - char const* sourceCode = R"( - contract C { - uint st; - function f(uint a) public returns (uint b, uint c, uint d) { - st = 0; - assembly { - function sideeffect(r) -> x { sstore(0, add(sload(0), r)) x := 1} - for { let i := a } eq(i, sideeffect(2)) { d := add(d, 3) } { - b := i - i := 0 - } - } - c = st; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0), u256(2), u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1), u256(4), u256(3))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0), u256(2), u256(0))); - ) -} - -BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) -{ - // Test for a bug where higher order bits cleanup was not done for array index access. - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint[256] memory r){ - r[uint8(x)] = 2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // neither of the two should throw due to out-of-bounds access - BOOST_CHECK(callContractFunction("f(uint256)", u256(0x01)).size() == 256 * 32); - BOOST_CHECK(callContractFunction("f(uint256)", u256(0x101)).size() == 256 * 32); -} - -BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) -{ - // Test for a bug where we did not increment the counter properly while deleting a dynamic array. - char const* sourceCode = R"( - contract C { - struct S { uint x; uint[] y; } - S[] data; - function f() public returns (bool) { - S storage s1 = data.push(); - s1.x = 2**200; - S storage s2 = data.push(); - s2.x = 2**200; - delete data; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // This code interprets x as an array length and thus will go out of gas. - // neither of the two should throw due to out-of-bounds access - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - -} - -BOOST_AUTO_TEST_CASE(internal_library_function) -{ - // tests that internal library functions can be called from outside - // and retain the same memory context (i.e. are pulled into the caller's code) - char const* sourceCode = R"( - library L { - function f(uint[] memory _data) internal { - _data[3] = 2; - } - } - contract C { - function f() public returns (uint) { - uint[] memory x = new uint[](7); - x[3] = 8; - L.f(x); - return x[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_calling_private) -{ - // tests that internal library functions that are called from outside and that - // themselves call private functions are still able to (i.e. the private function - // also has to be pulled into the caller's code) - char const* sourceCode = R"( - library L { - function g(uint[] memory _data) private { - _data[3] = 2; - } - function f(uint[] memory _data) internal { - g(_data); - } - } - contract C { - function f() public returns (uint) { - uint[] memory x = new uint[](7); - x[3] = 8; - L.f(x); - return x[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_bound) -{ - char const* sourceCode = R"( - library L { - struct S { uint[] data; } - function f(S memory _s) internal { - _s.data[3] = 2; - } - } - contract C { - using L for L.S; - function f() public returns (uint) { - L.S memory x; - x.data = new uint[](7); - x.data[3] = 8; - x.f(); - return x.data[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_return_var_size) -{ - char const* sourceCode = R"( - library L { - struct S { uint[] data; } - function f(S memory _s) internal returns (uint[] memory) { - _s.data[3] = 2; - return _s.data; - } - } - contract C { - using L for L.S; - function f() public returns (uint) { - L.S memory x; - x.data = new uint[](7); - x.data[3] = 8; - return x.f()[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(iszero_bnot_correct) -{ - // A long time ago, some opcodes were renamed, which involved the opcodes - // "iszero" and "not". - char const* sourceCode = R"( - contract C { - function f() public returns (bool) { - bytes32 x = bytes32(uint256(1)); - assembly { x := not(x) } - if (x != ~bytes32(uint256(1))) return false; - assembly { x := iszero(x) } - if (x != bytes32(0)) return false; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(cleanup_bytes_types) -{ - // Checks that bytesXX types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(bytes2 a, uint16 x) public returns (uint) { - if (a != "ab") return 1; - if (x != 0x0102) return 2; - if (bytes3(uint24(x)) != 0x000102) return 3; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // We input longer data on purpose. - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), v2 ? encodeArgs() : encodeArgs(0)); -} -BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (bytes32 r) { - bytes4 x = 0xffffffff; - bytes2 y = bytes2(x); - assembly { r := y } - // At this point, r and y both store four bytes, but - // y is properly cleaned before the equality check - require(y == bytes2(0xffff)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs("\xff\xff\xff\xff")); -} -BOOST_AUTO_TEST_CASE(cleanup_address_types) -{ - // Checks that address types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(address a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - function g(address payable a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - // We input longer data on purpose. - ABI_CHECK(callContractFunction("f(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); - ABI_CHECK(callContractFunction("g(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(cleanup_address_types_shortening) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (address r) { - bytes21 x = 0x1122334455667788990011223344556677889900ff; - bytes20 y; - assembly { y := x } - address z = address(y); - assembly { r := z } - require(z == 0x1122334455667788990011223344556677889900); - } - function g() public pure returns (address payable r) { - bytes21 x = 0x1122334455667788990011223344556677889900ff; - bytes20 y; - assembly { y := x } - address payable z = address(y); - assembly { r := z } - require(z == 0x1122334455667788990011223344556677889900); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); - ) -} - -BOOST_AUTO_TEST_CASE(skip_dynamic_types) -{ - // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. - char const* sourceCode = R"( - contract C { - function f() public returns (uint, uint[] memory, uint) { - return (7, new uint[](2), 8); - } - function g() public returns (uint, uint) { - // Previous implementation "moved" b to the second place and did not skip. - (uint a,, uint b) = this.f(); - return (a, b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(7), u256(8))); -} - -BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) -{ - // For accessors, the dynamic types are already removed in the external signature itself. - char const* sourceCode = R"( - contract C { - struct S { - uint x; - string a; // this is present in the accessor - uint[] b; // this is not present - uint y; - } - S public s; - function g() public returns (uint, uint) { - s.x = 2; - s.a = "abc"; - s.b = [7, 8, 9]; - s.y = 6; - (uint x,, uint y) = this.s(); - return (x, y); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(2), u256(6))); -} - -BOOST_AUTO_TEST_CASE(failed_create) -{ - char const* sourceCode = R"( - contract D { constructor() public payable {} } - contract C { - uint public x; - constructor() public payable {} - function f(uint amount) public returns (D) { - x++; - return (new D).value(amount)(); - } - function stack(uint depth) public returns (address) { - if (depth < 1024) - return this.stack(depth - 1); - else - return address(f(0)); - } - } - )"; - compileAndRun(sourceCode, 20, "C"); - BOOST_CHECK(callContractFunction("f(uint256)", 20) != encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", 20), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("stack(uint256)", 1023), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint[][] memory a = new uint[][](0); - return 7; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) -{ - // Memory arrays are initialized using calldatacopy past the size of the calldata. - // This test checks that it also works in the constructor context. - char const* sourceCode = R"( - contract C { - bool public success; - constructor() public { - // Make memory dirty. - assembly { - for { let i := 0 } lt(i, 64) { i := add(i, 1) } { - mstore(msize(), not(0)) - } - } - uint16[3] memory c; - require(c[0] == 0 && c[1] == 0 && c[2] == 0); - uint16[] memory x = new uint16[](3); - require(x[0] == 0 && x[1] == 0 && x[2] == 0); - success = true; - } - } - )"; - // Cannot run against yul optimizer because of msize - if (!m_optimiserSettings.runYulOptimiser) - { - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1))); - } -} - -BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier setsx { - _; - x = 9; - } - function f() setsx public returns (uint) { - return 2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(break_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - _; - break; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(continue_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - if (i % 2 == 1) continue; - _; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(5))); -} - -BOOST_AUTO_TEST_CASE(return_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 1; i < 10; i++) { - if (i == 5) return; - _; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - _; - break; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(mutex) -{ - char const* sourceCode = R"( - contract mutexed { - bool locked; - modifier protected { - if (locked) revert(); - locked = true; - _; - locked = false; - } - } - contract Fund is mutexed { - uint shares; - constructor() public payable { shares = msg.value; } - function withdraw(uint amount) public protected returns (uint) { - // NOTE: It is very bad practice to write this function this way. - // Please refer to the documentation of how to do this properly. - if (amount > shares) revert(); - (bool success,) = msg.sender.call.value(amount)(""); - require(success); - shares -= amount; - return shares; - } - function withdrawUnprotected(uint amount) public returns (uint) { - // NOTE: It is very bad practice to write this function this way. - // Please refer to the documentation of how to do this properly. - if (amount > shares) revert(); - (bool success,) = msg.sender.call.value(amount)(""); - require(success); - shares -= amount; - return shares; - } - } - contract Attacker { - Fund public fund; - uint callDepth; - bool protected; - function setProtected(bool _protected) public { protected = _protected; } - constructor(Fund _fund) public { fund = _fund; } - function attack() public returns (uint) { - callDepth = 0; - return attackInternal(); - } - function attackInternal() internal returns (uint) { - if (protected) - return fund.withdraw(10); - else - return fund.withdrawUnprotected(10); - } - fallback() external payable { - callDepth++; - if (callDepth < 4) - attackInternal(); - } - } - )"; - compileAndRun(sourceCode, 500, "Fund"); - auto fund = m_contractAddress; - BOOST_CHECK_EQUAL(balanceAt(fund), 500); - compileAndRun(sourceCode, 0, "Attacker", encodeArgs(u160(fund))); - ABI_CHECK(callContractFunction("setProtected(bool)", true), encodeArgs()); - ABI_CHECK(callContractFunction("attack()"), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(fund), 500); - ABI_CHECK(callContractFunction("setProtected(bool)", false), encodeArgs()); - ABI_CHECK(callContractFunction("attack()"), encodeArgs(u256(460))); - BOOST_CHECK_EQUAL(balanceAt(fund), 460); -} - -BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws) -{ - char const* sourceCode = R"YY( - abstract contract D { function g() public virtual; } - contract C { - D d = D(0x1212); - function f() public returns (uint) { - d.g(); - return 7; - } - function g() public returns (uint) { - d.g.gas(200)(); - return 7; - } - function h() public returns (uint) { - address(d).call(""); // this does not throw (low-level) - return 7; - } - } - )YY"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(payable_constructor) -{ - char const* sourceCode = R"( - contract C { - constructor() public payable { } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 27, "C"); - ) -} - -BOOST_AUTO_TEST_CASE(payable_function) -{ - char const* sourceCode = R"( - contract C { - uint public a; - function f() payable public returns (uint) { - return msg.value; - } - fallback() external payable { - a = msg.value + 1; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs(u256(27))); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27); - ABI_CHECK(callContractFunctionWithValue("", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(28))); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); -} - -BOOST_AUTO_TEST_CASE(payable_function_calls_library) -{ - char const* sourceCode = R"( - library L { - function f() public returns (uint) { return 7; } - } - contract C { - function f() public payable returns (uint) { - return L.f(); - } - } - )"; - compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(non_payable_throw) -{ - char const* sourceCode = R"( - contract C { - uint public a; - function f() public returns (uint) { - return msgvalue(); - } - function msgvalue() internal returns (uint) { - return msg.value; - } - fallback() external { - update(); - } - function update() internal { - a = msg.value + 1; - } - - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); - ABI_CHECK(callContractFunction(""), encodeArgs()); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunctionWithValue("", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunctionWithValue("a()", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); -} - -BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) -{ - char const* sourceCode = R"( - contract C { - modifier tryCircumvent { - if (false) _; // avoid the function, we should still not accept ether - } - function f() tryCircumvent public returns (uint) { - return msgvalue(); - } - function msgvalue() internal returns (uint) { - return msg.value; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); - ) -} - -BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) -{ - // This tests that memory resize for return values is not paid during the call, which would - // make the gas calculation overly complex. We access the end of the output area before - // the call is made. - // Tests that this also survives the optimizer. - char const* sourceCode = R"( - contract C { - function f() public returns (uint[200] memory) {} - } - contract D { - function f(C c) public returns (uint) { c.f(); return 7; } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - u160 cAddr = m_contractAddress; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f(address)", cAddr), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(calling_uninitialized_function) -{ - char const* sourceCode = R"( - contract C { - function intern() public returns (uint) { - function (uint) internal returns (uint) x; - x(2); - return 7; - } - function extern() public returns (uint) { - function (uint) external returns (uint) x; - x(2); - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - // This should throw exceptions - ABI_CHECK(callContractFunction("intern()"), encodeArgs()); - ABI_CHECK(callContractFunction("extern()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint) x; - int mutex; - function t() public returns (uint) { - if (mutex > 0) - { assembly { mstore(0, 7) return(0, 0x20) } } - mutex = 1; - // Avoid re-executing this function if we jump somewhere. - x(); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array) -{ - char const* sourceCode = R"( - contract C { - int mutex; - function t() public returns (uint) { - if (mutex > 0) - { assembly { mstore(0, 7) return(0, 0x20) } } - mutex = 1; - // Avoid re-executing this function if we jump somewhere. - function() internal returns (uint)[200] memory x; - x[0](); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(pass_function_types_internally) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint) { - return eval(g, x); - } - function eval(function(uint) internal returns (uint) x, uint a) internal returns (uint) { - return x(a); - } - function g(uint x) public returns (uint) { return x + 1; } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", 7), encodeArgs(u256(8))); -} - -BOOST_AUTO_TEST_CASE(pass_function_types_externally) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint) { - return this.eval(this.g, x); - } - function f2(uint x) public returns (uint) { - return eval(this.g, x); - } - function eval(function(uint) external returns (uint) x, uint a) public returns (uint) { - return x(a); - } - function g(uint x) public returns (uint) { return x + 1; } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", 7), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f2(uint256)", 7), encodeArgs(u256(8))); -} - -BOOST_AUTO_TEST_CASE(receive_external_function_type) -{ - char const* sourceCode = R"( - contract C { - function g() public returns (uint) { return 7; } - function f(function() external returns (uint) g) public returns (uint) { - return g(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction( - "f(function)", - m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0) - ), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(return_external_function_type) -{ - char const* sourceCode = R"( - contract C { - function g() public {} - function f() public returns (function() external) { - return this.g; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK( - callContractFunction("f()"), - m_contractAddress.asBytes() + FixedHash<4>(util::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0) - ); -} - -BOOST_AUTO_TEST_CASE(store_function) -{ - char const* sourceCode = R"( - contract Other { - function addTwo(uint x) public returns (uint) { return x + 2; } - } - contract C { - function (function (uint) external returns (uint)) internal returns (uint) ev; - function (uint) external returns (uint) x; - function store(function(uint) external returns (uint) y) public { - x = y; - } - function eval(function(uint) external returns (uint) y) public returns (uint) { - return y(7); - } - function t() public returns (uint) { - ev = eval; - this.store((new Other()).addTwo); - return ev(x); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(store_function_in_constructor) -{ - char const* sourceCode = R"( - contract C { - uint public result_in_constructor; - function (uint) internal returns (uint) x; - constructor() public { - x = double; - result_in_constructor = use(2); - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - function use(uint _arg) public returns (uint) { - return x(_arg); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("use(uint256)", encodeArgs(u256(3))), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("result_in_constructor()"), encodeArgs(u256(4))); -} - -// TODO: store bound internal library functions - -BOOST_AUTO_TEST_CASE(store_internal_unused_function_in_constructor) -{ - char const* sourceCode = R"( - contract C { - function () internal returns (uint) x; - constructor() public { - x = unused; - } - function unused() internal returns (uint) { - return 7; - } - function t() public returns (uint) { - return x(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(store_internal_unused_library_function_in_constructor) -{ - char const* sourceCode = R"( - library L { function x() internal returns (uint) { return 7; } } - contract C { - function () internal returns (uint) x; - constructor() public { - x = L.x; - } - function t() public returns (uint) { - return x(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime) -{ - char const* sourceCode = R"( - contract C { - uint public initial; - constructor() public { - initial = double(2); - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - function runtime(uint _arg) public returns (uint) { - return double(_arg); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("runtime(uint256)", encodeArgs(u256(3))), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("initial()"), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime_equality_check) -{ - char const* sourceCode = R"( - contract C { - function (uint) internal returns (uint) x; - constructor() public { - x = double; - } - function test() public returns (bool) { - return x == double; - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(function_type_library_internal) -{ - char const* sourceCode = R"( - library Utils { - function reduce(uint[] memory array, function(uint, uint) internal returns (uint) f, uint init) internal returns (uint) { - for (uint i = 0; i < array.length; i++) { - init = f(array[i], init); - } - return init; - } - function sum(uint a, uint b) internal returns (uint) { - return a + b; - } - } - contract C { - function f(uint[] memory x) public returns (uint) { - return Utils.reduce(x, Utils.sum, 0); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256[])", 0x20, 3, u256(1), u256(7), u256(3)), encodeArgs(u256(11))); -} - - -BOOST_AUTO_TEST_CASE(call_function_returning_function) -{ - char const* sourceCode = R"( - contract test { - function f0() public returns (uint) { - return 2; - } - function f1() internal returns (function() internal returns (uint)) { - return f0; - } - function f2() internal returns (function() internal returns (function () internal returns (uint))) { - return f1; - } - function f3() internal returns (function() internal returns (function () internal returns (function () internal returns (uint)))) - { - return f2; - } - function f() public returns (uint) { - function() internal returns(function() internal returns(function() internal returns(function() internal returns(uint)))) x; - x = f3; - return x()()()(); - } - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(mapping_of_functions) -{ - char const* sourceCode = R"( - contract Flow { - bool public success; - - mapping (address => function () internal) stages; - - function stage0() internal { - stages[msg.sender] = stage1; - } - - function stage1() internal { - stages[msg.sender] = stage2; - } - - function stage2() internal { - success = true; - } - - constructor() public { - stages[msg.sender] = stage0; - } - - function f() public returns (uint) { - stages[msg.sender](); - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "Flow"); - ABI_CHECK(callContractFunction("success()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("success()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("success()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(packed_functions) -{ - char const* sourceCode = R"( - contract C { - // these should take the same slot - function() internal returns (uint) a; - function() external returns (uint) b; - function() external returns (uint) c; - function() internal returns (uint) d; - uint8 public x; - - function set() public { - x = 2; - d = g; - c = this.h; - b = this.h; - a = g; - } - function t1() public returns (uint) { - return a(); - } - function t2() public returns (uint) { - return b(); - } - function t3() public returns (uint) { - return a(); - } - function t4() public returns (uint) { - return b(); - } - function g() public returns (uint) { - return 7; - } - function h() public returns (uint) { - return 8; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("t1()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("t2()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("t3()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("t4()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(function_memory_array) -{ - char const* sourceCode = R"( - contract C { - function a(uint x) public returns (uint) { return x + 1; } - function b(uint x) public returns (uint) { return x + 2; } - function c(uint x) public returns (uint) { return x + 3; } - function d(uint x) public returns (uint) { return x + 5; } - function e(uint x) public returns (uint) { return x + 8; } - function test(uint x, uint i) public returns (uint) { - function(uint) internal returns (uint)[] memory arr = - new function(uint) internal returns (uint)[](10); - arr[0] = a; - arr[1] = b; - arr[2] = c; - arr[3] = d; - arr[4] = e; - return arr[i](x); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(0)), encodeArgs(u256(11))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(1)), encodeArgs(u256(12))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(2)), encodeArgs(u256(13))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(3)), encodeArgs(u256(15))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(4)), encodeArgs(u256(18))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(5)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(function_delete_storage) -{ - char const* sourceCode = R"( - contract C { - function a() public returns (uint) { return 7; } - function() internal returns (uint) y; - function set() public returns (uint) { - y = a; - return y(); - } - function d() public returns (uint) { - delete y; - return 1; - } - function ca() public returns (uint) { - return y(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("set()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("ca()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("d()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("ca()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(function_delete_stack) -{ - char const* sourceCode = R"( - contract C { - function a() public returns (uint) { return 7; } - function test() public returns (uint) { - function () returns (uint) y = a; - delete y; - y(); - } - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(copy_function_storage_array) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint)[] x; - function() internal returns (uint)[] y; - function test() public returns (uint) { - x = new function() internal returns (uint)[](10); - x[9] = a; - y = x; - return y[9](); - } - function a() public returns (uint) { - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(function_array_cross_calls) -{ - char const* sourceCode = R"( - contract D { - function f(function() external returns (function() external returns (uint))[] memory x) - public returns (function() external returns (uint)[3] memory r) - { - r[0] = x[0](); - r[1] = x[1](); - r[2] = x[2](); - } - } - contract C { - function test() public returns (uint, uint, uint) { - function() external returns (function() external returns (uint))[] memory x = - new function() external returns (function() external returns (uint))[](10); - for (uint i = 0; i < x.length; i ++) - x[i] = this.h; - x[0] = this.htwo; - function() external returns (uint)[3] memory y = (new D()).f(x); - return (y[0](), y[1](), y[2]()); - } - function e() public returns (uint) { return 5; } - function f() public returns (uint) { return 6; } - function g() public returns (uint) { return 7; } - uint counter; - function h() public returns (function() external returns (uint)) { - return counter++ == 0 ? this.f : this.g; - } - function htwo() public returns (function() external returns (uint)) { - return this.e; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(5), u256(6), u256(7))); -} - -BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint)[20] x; - int mutex; - function one() public returns (uint) { - function() internal returns (uint)[20] memory xmem; - x = xmem; - return 3; - } - function two() public returns (uint) { - if (mutex > 0) - return 7; - mutex = 1; - // If this test fails, it might re-execute this function. - x[0](); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("one()"), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("two()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left) -{ - char const* sourceCode = R"( - contract C { - uint public a = 0x42 << 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_constant_left) -{ - char const* sourceCode = R"( - contract C { - int public a = -0x42 << 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(-0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right) -{ - char const* sourceCode = R"( - contract C { - uint public a = 0x4200 >> 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_constant_right) -{ - char const* sourceCode = R"( - contract C { - int public a = -0x4200 >> 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(-0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_left) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_uint32) -{ - char const* sourceCode = R"( - contract C { - function f(uint32 a, uint32 b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(32)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_uint8) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)), encodeArgs(u256(0x66))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_larger_type) -{ - // This basically tests proper cleanup and conversion. It should not convert x to int8. - char const* sourceCode = R"( - contract C { - function f() public returns (int8) { - uint8 x = 254; - int8 y = 1; - return y << x; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - a <<= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_assignment_different_type) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint8 b) public returns (uint) { - a <<= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); -} - -BOOST_AUTO_TEST_CASE(shift_right) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(1)<<255, u256(5)), encodeArgs(u256(1)<<250)); -} - -BOOST_AUTO_TEST_CASE(shift_right_garbled) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - assembly { - a := 0xffffffff - } - // Higher bits should be cleared before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(0xf))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(0xf))); -} - -BOOST_AUTO_TEST_CASE(shift_right_garbled_signed) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xfffffff0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - function g(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xf0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_uint32) -{ - char const* sourceCode = R"( - contract C { - function f(uint32 a, uint32 b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_uint8) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)), encodeArgs(u256(0x66))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_assignment_signed) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_literal) -{ - char const* sourceCode = R"( - contract C { - function f1() public pure returns (bool) { - return (-4266 >> 0) == -4266; - } - function f2() public pure returns (bool) { - return (-4266 >> 1) == -2133; - } - function f3() public pure returns (bool) { - return (-4266 >> 4) == -267; - } - function f4() public pure returns (bool) { - return (-4266 >> 8) == -17; - } - function f5() public pure returns (bool) { - return (-4266 >> 16) == -1; - } - function f6() public pure returns (bool) { - return (-4266 >> 17) == -1; - } - function g1() public pure returns (bool) { - return (-4267 >> 0) == -4267; - } - function g2() public pure returns (bool) { - return (-4267 >> 1) == -2134; - } - function g3() public pure returns (bool) { - return (-4267 >> 4) == -267; - } - function g4() public pure returns (bool) { - return (-4267 >> 8) == -17; - } - function g5() public pure returns (bool) { - return (-4267 >> 16) == -1; - } - function g6() public pure returns (bool) { - return (-4267 >> 17) == -1; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f5()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f6()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g1()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g2()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g3()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g4()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g5()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g6()"), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int8) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, int8 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(0)), encodeArgs(u256(-66))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(1)), encodeArgs(u256(-33))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(4)), encodeArgs(u256(-5))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(8)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(0)), encodeArgs(u256(-67))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(1)), encodeArgs(u256(-34))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(4)), encodeArgs(u256(-5))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(8)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, int8 b) public returns (int8) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16) -{ - char const* sourceCode = R"( - contract C { - function f(int16 a, int16 b) public returns (int16) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32) -{ - char const* sourceCode = R"( - contract C { - function f(int32 a, int32 b) public returns (int32) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int16) -{ - char const* sourceCode = R"( - contract C { - function f(int16 a, int16 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int32) -{ - char const* sourceCode = R"( - contract C { - function f(int32 a, int32 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_rvalue) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - return a << b; - } - function g(int a, int b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)), encodeArgs()); - ABI_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_negative_rvalue_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a <<= b; - return a; - } - function g(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)), encodeArgs()); - ABI_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint a) { - a = 0x42; - a <<= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint a) { - a = 0x4200; - a >>= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_cleanup) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint16 x) { - x = 0xffff; - x += 32; - x <<= 8; - x >>= 16; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_cleanup_garbled) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint8 x) { - assembly { - x := 0xffff - } - x >>= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_overflow) -{ - char const* sourceCode = R"( - contract C { - function leftU(uint8 x, uint8 y) public returns (uint8) { - return x << y; - } - function leftS(int8 x, int8 y) public returns (int8) { - return x << y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 8), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 1), encodeArgs(u256(254))); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 0), encodeArgs(u256(255))); - - // Result is -128 and output is sign-extended, not zero-padded. - ABI_CHECK(callContractFunction("leftS(int8,int8)", 1, 7), encodeArgs(u256(0) - 128)); - ABI_CHECK(callContractFunction("leftS(int8,int8)", 1, 6), encodeArgs(u256(64))); -} - -BOOST_AUTO_TEST_CASE(shift_bytes) -{ - char const* sourceCode = R"( - contract C { - function left(bytes20 x, uint8 y) public returns (bytes20) { - return x << y; - } - function right(bytes20 x, uint8 y) public returns (bytes20) { - return x >> y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("left(bytes20,uint8)", "12345678901234567890", 8 * 8), encodeArgs("901234567890" + string(8, 0))); - ABI_CHECK(callContractFunction("right(bytes20,uint8)", "12345678901234567890", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); -} - -BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) -{ - char const* sourceCode = R"( - contract C { - function left(uint8 y) public returns (bytes20) { - bytes20 x; - assembly { x := "12345678901234567890abcde" } - return x << y; - } - function right(uint8 y) public returns (bytes20) { - bytes20 x; - assembly { x := "12345678901234567890abcde" } - return x >> y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("left(uint8)", 8 * 8), encodeArgs("901234567890" + string(8, 0))); - ABI_CHECK(callContractFunction("right(uint8)", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); -} - -BOOST_AUTO_TEST_CASE(exp_cleanup) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - uint8 y = uint8(2) ** uint8(8); - return 0 ** y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(exp_cleanup_direct) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - return uint8(0) ** uint8(uint8(2) ** uint8(8)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(exp_cleanup_nonzero_base) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - return uint8(0x166) ** uint8(uint8(2) ** uint8(8)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(cleanup_in_compound_assign) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint, uint) { - uint32 a = 0xffffffff; - uint16 x = uint16(a); - uint16 y = x; - x /= 0x100; - y = y / 0x100; - return (x, y); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0xff), u256(0xff))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_in_modifiers) -{ - char const* sourceCode = R"( - contract C { - modifier m { - uint a = 1; - assembly { - a := 2 - } - if (a != 2) - revert(); - _; - } - function f() m public returns (bool) { - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(packed_storage_overflow) -{ - char const* sourceCode = R"( - contract C { - uint16 x = 0x1234; - uint16 a = 0xffff; - uint16 b; - function f() public returns (uint, uint, uint, uint) { - a++; - uint c = b; - delete b; - a -= 2; - return (x, c, b, a); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1234), u256(0), u256(0), u256(0xfffe))); -} - -BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) -{ - char const* sourceCode = R"( - contract C1 {} - /** - **/ - contract C2 {} - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C1"); - compileAndRun(sourceCode, 0, "C2"); - ) -} - - -BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) -{ - char const* sourceCode = R"( - contract D { - bytes a = hex"1237651237125387136581271652831736512837126583171583712358126123765123712538713658127165283173651283712658317158371235812612376512371253871365812716528317365128371265831715837123581261237651237125387136581271652831736512837126583171583712358126"; - bytes b = hex"1237651237125327136581271252831736512837126583171383712358126123765125712538713658127165253173651283712658357158371235812612376512371a5387136581271652a317365128371265a317158371235812612a765123712538a13658127165a83173651283712a58317158371235a126"; - constructor(uint) public {} - } - contract Double { - function f() public { - new D(2); - } - function g() public { - new D(3); - } - } - contract Single { - function f() public { - new D(2); - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK_LE( - double(m_compiler.object("Double").bytecode.size()), - 1.2 * double(m_compiler.object("Single").bytecode.size()) - ); -} - -BOOST_AUTO_TEST_CASE(recursive_structs) -{ - char const* sourceCode = R"( - contract C { - struct S { - S[] x; - } - S sstorage; - function f() public returns (uint) { - S memory s; - s.x = new S[](10); - delete s; - // TODO Uncomment after implemented. - // sstorage.x.push(); - delete sstorage; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(invalid_instruction) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - invalid() - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(assert_require) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assert(false); - } - function g(bool val) public returns (bool) { - assert(val == true); - return true; - } - function h(bool val) public returns (bool) { - require(val); - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs()); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(true)); - ABI_CHECK(callContractFunction("h(bool)", false), encodeArgs()); - ABI_CHECK(callContractFunction("h(bool)", true), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(revert) -{ - char const* sourceCode = R"( - contract C { - uint public a = 42; - function f() public { - a = 1; - revert(); - } - function g() public { - a = 1; - assembly { - revert(0, 0) - } - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(revert_with_cause) -{ - char const* sourceCode = R"( - contract D { - string constant msg1 = "test1234567890123456789012345678901234567890"; - string msg2 = "test1234567890123456789012345678901234567890"; - function f() public { - revert("test123"); - } - function g() public { - revert("test1234567890123456789012345678901234567890"); - } - function h() public { - revert(msg1); - } - function i() public { - revert(msg2); - } - function j() public { - string memory msg3 = "test1234567890123456789012345678901234567890"; - revert(msg3); - } - } - contract C { - D d = new D(); - function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { - uint retsize; - assembly { - success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) - retsize := returndatasize() - } - retval = new bytes(retsize); - assembly { - returndatacopy(add(retval, 0x20), 0, returndatasize()) - } - } - function f() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function g() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function h() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function i() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function j() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "C"); - bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "test123") + bytes(28, 0)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); - ABI_CHECK(callContractFunction("i()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); - ABI_CHECK(callContractFunction("j()"), encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0)); - } -} - -BOOST_AUTO_TEST_CASE(require_with_message) -{ - char const* sourceCode = R"( - contract D { - bool flag = false; - string storageError = "abc"; - string constant constantError = "abc"; - function f(uint x) public { - require(x > 7, "failed"); - } - function g() public { - // As a side-effect of internalFun, the flag will be set to true - // (even if the condition is true), - // but it will only throw in the next evaluation. - bool flagCopy = flag; - require(flagCopy == false, internalFun()); - } - function internalFun() public returns (string memory) { - flag = true; - return "only on second run"; - } - function h() public { - require(false, storageError); - } - function i() public { - require(false, constantError); - } - function j() public { - string memory errMsg = "msg"; - require(false, errMsg); - } - } - contract C { - D d = new D(); - function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { - uint retsize; - assembly { - success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) - retsize := returndatasize() - } - retval = new bytes(retsize); - assembly { - returndatacopy(add(retval, 0x20), 0, returndatasize()) - } - } - function f(uint x) public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function g() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function h() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function i() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function j() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "C"); - bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; - ABI_CHECK(callContractFunction("f(uint256)", 8), encodeArgs(1, 0x40, 0)); - ABI_CHECK(callContractFunction("f(uint256)", 5), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 6, "failed") + bytes(28, 0)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(1, 0x40, 0)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 18, "only on second run") + bytes(28, 0)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0)); - ABI_CHECK(callContractFunction("i()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0)); - ABI_CHECK(callContractFunction("j()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "msg") + bytes(28, 0)); - } -} - -BOOST_AUTO_TEST_CASE(bubble_up_error_messages) -{ - char const* sourceCode = R"( - contract D { - function f() public { - revert("message"); - } - function g() public { - this.f(); - } - } - contract C { - D d = new D(); - function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { - uint retsize; - assembly { - success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) - retsize := returndatasize() - } - retval = new bytes(retsize); - assembly { - returndatacopy(add(retval, 0x20), 0, returndatasize()) - } - } - function f() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - function g() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "C"); - bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); - } -} - -BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) -{ - char const* sourceCode = R"( - contract D { - receive() external payable { - revert("message"); - } - function f() public { - address(this).transfer(0); - } - } - contract C { - D d = new D(); - function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { - uint retsize; - assembly { - success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) - retsize := returndatasize() - } - retval = new bytes(retsize); - assembly { - returndatacopy(add(retval, 0x20), 0, returndatasize()) - } - } - function f() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "C"); - bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); - } -} - -BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) -{ - char const* sourceCode = R"( - contract E { - constructor() public { - revert("message"); - } - } - contract D { - function f() public { - E x = new E(); - } - } - contract C { - D d = new D(); - function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { - uint retsize; - assembly { - success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) - retsize := returndatasize() - } - retval = new bytes(retsize); - assembly { - returndatacopy(add(retval, 0x20), 0, returndatasize()) - } - } - function f() public returns (bool, bytes memory) { - return forward(address(d), msg.data); - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "C"); - bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0)); - } -} - -BOOST_AUTO_TEST_CASE(negative_stack_height) -{ - // This code was causing negative stack height during code generation - // because the stack height was not adjusted at the beginning of functions. - char const* sourceCode = R"( - contract C { - mapping(uint => Invoice) public invoices; - struct Invoice { - uint AID; - bool Aboola; - bool Aboolc; - bool exists; - } - function nredit(uint startindex) public pure returns(uint[500] memory CIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Cboolas, uint[500] memory amounts){} - function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory AIDs, bool[500] memory Aboolas, uint[500] memory dates, bytes32[3][500] memory Abytesas, bytes32[3][500] memory bytesbs, bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Aboolbs, bool[500] memory Aboolcs){} - function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory BIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Bboolas, bytes32[3][500] memory bytesbs,bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Bboolbs){} - } - )"; - compileAndRun(sourceCode, 0, "C"); -} - -BOOST_AUTO_TEST_CASE(literal_empty_string) -{ - char const* sourceCode = R"( - contract C { - bytes32 public x; - uint public a; - function f(bytes32 _x, uint _a) public { - x = _x; - a = _a; - } - function g() public { - this.f("", 2); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(scientific_notation) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return 2e10 wei; - } - function g() public returns (uint) { - return 200e-2 wei; - } - function h() public returns (uint) { - return 2.5e1; - } - function i() public returns (int) { - return -2e10; - } - function j() public returns (int) { - return -200e-2; - } - function k() public returns (int) { - return -2.5e1; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(20000000000))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(25))); - ABI_CHECK(callContractFunction("i()"), encodeArgs(u256(-20000000000))); - ABI_CHECK(callContractFunction("j()"), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("k()"), encodeArgs(u256(-25))); - ) -} - -BOOST_AUTO_TEST_CASE(interface_contract) -{ - char const* sourceCode = R"( - interface I { - event A(); - function f() external returns (bool); - fallback() external payable; - } - - contract A is I { - function f() public override returns (bool) { - return g(); - } - - function g() public returns (bool) { - return true; - } - - fallback() override external payable { - } - } - - contract C { - function f(address payable _interfaceAddress) public returns (bool) { - I i = I(_interfaceAddress); - return i.f(); - } - } - )"; - compileAndRun(sourceCode, 0, "A"); - u160 const recipient = m_contractAddress; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(address)", recipient), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(keccak256_assembly) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (bytes32 ret) { - assembly { - ret := keccak256(0, 0) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ) -} - -BOOST_AUTO_TEST_CASE(multi_modifiers) -{ - // This triggered a bug in some version because the variable in the modifier was not - // unregistered correctly. - char const* sourceCode = R"( - contract C { - uint public x; - modifier m1 { - address a1 = msg.sender; - x++; - _; - } - function f1() m1() public { - x += 7; - } - function f2() m1() public { - x += 3; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f1()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f2()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(12))); -} - -BOOST_AUTO_TEST_CASE(inlineasm_empty_let) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint a, uint b) { - assembly { - let x - let y, z - a := x - b := z - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(0))); - ) -} - -BOOST_AUTO_TEST_CASE(bare_call_invalid_address) -{ - char const* sourceCode = R"YY( - contract C { - /// Calling into non-existent account is successful (creates the account) - function f() external returns (bool) { - (bool success,) = address(0x4242).call(""); - return success; - } - function h() external returns (bool) { - (bool success,) = address(0x4242).delegatecall(""); - return success; - } - } - )YY"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1))); - - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - char const* sourceCode = R"YY( - contract C { - function f() external returns (bool, bytes memory) { - return address(0x4242).staticcall(""); - } - } - )YY"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), 0x40, 0x00)); - } -} - -BOOST_AUTO_TEST_CASE(bare_call_return_data) -{ - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - vector calltypes = {"call", "delegatecall"}; - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - calltypes.emplace_back("staticcall"); - for (string const& calltype: calltypes) - { - string sourceCode = R"DELIMITER( - contract A { - constructor() public { - } - function return_bool() public pure returns(bool) { - return true; - } - function return_int32() public pure returns(int32) { - return -32; - } - function return_uint32() public pure returns(uint32) { - return 0x3232; - } - function return_int256() public pure returns(int256) { - return -256; - } - function return_uint256() public pure returns(uint256) { - return 0x256256; - } - function return_bytes4() public pure returns(bytes4) { - return 0xabcd0012; - } - function return_multi() public pure returns(bool, uint32, bytes4) { - return (false, 0x3232, 0xabcd0012); - } - function return_bytes() public pure returns(bytes memory b) { - b = new bytes(2); - b[0] = 0x42; - b[1] = 0x21; - } - } - contract C { - A addr; - constructor() public { - addr = new A(); - } - function f(string memory signature) public returns (bool, bytes memory) { - return address(addr).)DELIMITER" + calltype + R"DELIMITER((abi.encodeWithSignature(signature)); - } - function check_bool() external returns (bool) { - (bool success, bytes memory data) = f("return_bool()"); - assert(success); - bool a = abi.decode(data, (bool)); - assert(a); - return true; - } - function check_int32() external returns (bool) { - (bool success, bytes memory data) = f("return_int32()"); - assert(success); - int32 a = abi.decode(data, (int32)); - assert(a == -32); - return true; - } - function check_uint32() external returns (bool) { - (bool success, bytes memory data) = f("return_uint32()"); - assert(success); - uint32 a = abi.decode(data, (uint32)); - assert(a == 0x3232); - return true; - } - function check_int256() external returns (bool) { - (bool success, bytes memory data) = f("return_int256()"); - assert(success); - int256 a = abi.decode(data, (int256)); - assert(a == -256); - return true; - } - function check_uint256() external returns (bool) { - (bool success, bytes memory data) = f("return_uint256()"); - assert(success); - uint256 a = abi.decode(data, (uint256)); - assert(a == 0x256256); - return true; - } - function check_bytes4() external returns (bool) { - (bool success, bytes memory data) = f("return_bytes4()"); - assert(success); - bytes4 a = abi.decode(data, (bytes4)); - assert(a == 0xabcd0012); - return true; - } - function check_multi() external returns (bool) { - (bool success, bytes memory data) = f("return_multi()"); - assert(success); - (bool a, uint32 b, bytes4 c) = abi.decode(data, (bool, uint32, bytes4)); - assert(a == false && b == 0x3232 && c == 0xabcd0012); - return true; - } - function check_bytes() external returns (bool) { - (bool success, bytes memory data) = f("return_bytes()"); - assert(success); - (bytes memory d) = abi.decode(data, (bytes)); - assert(d.length == 2 && d[0] == 0x42 && d[1] == 0x21); - return true; - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bool()"))), encodeArgs(true, 0x40, 0x20, true)); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int32()"))), encodeArgs(true, 0x40, 0x20, u256(-32))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint32()"))), encodeArgs(true, 0x40, 0x20, u256(0x3232))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int256()"))), encodeArgs(true, 0x40, 0x20, u256(-256))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint256()"))), encodeArgs(true, 0x40, 0x20, u256(0x256256))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes4()"))), encodeArgs(true, 0x40, 0x20, u256(0xabcd0012) << (28*8))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_multi()"))), encodeArgs(true, 0x40, 0x60, false, u256(0x3232), u256(0xabcd0012) << (28*8))); - ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes()"))), encodeArgs(true, 0x40, 0x60, 0x20, 0x02, encode(bytes{0x42,0x21}, false))); - ABI_CHECK(callContractFunction("check_bool()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_int32()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_uint32()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_int256()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_uint256()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_bytes4()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_multi()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("check_bytes()"), encodeArgs(true)); - } - } -} - -BOOST_AUTO_TEST_CASE(delegatecall_return_value) -{ - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("get()")); - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("assert0()")); - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 0x00)); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 1)); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 42)); - } - else - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); - return success; - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); - return success; - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - } -} - -BOOST_AUTO_TEST_CASE(function_types_sig) -{ - char const* sourceCode = R"( - contract C { - uint public x; - function f() public pure returns (bytes4) { - return this.f.selector; - } - function g() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function h() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function i() public pure returns (bytes4) { - return this.x.selector; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("i()"), encodeArgs(asString(FixedHash<4>(util::keccak256("x()")).asBytes()))); -} - -BOOST_AUTO_TEST_CASE(constant_string) -{ - char const* sourceCode = R"( - contract C { - bytes constant a = "\x03\x01\x02"; - bytes constant b = hex"030102"; - string constant c = "hello"; - function f() public returns (bytes memory) { - return a; - } - function g() public returns (bytes memory) { - return b; - } - function h() public returns (bytes memory) { - return bytes(c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeDyn(string("\x03\x01\x02"))); - ABI_CHECK(callContractFunction("g()"), encodeDyn(string("\x03\x01\x02"))); - ABI_CHECK(callContractFunction("h()"), encodeDyn(string("hello"))); -} - -BOOST_AUTO_TEST_CASE(address_overload_resolution) -{ - char const* sourceCode = R"( - contract C { - function balance() public returns (uint) { - return 1; - } - function transfer(uint amount) public returns (uint) { - return amount; - } - } - contract D { - function f() public returns (uint) { - return (new C()).balance(); - } - function g() public returns (uint) { - return (new C()).transfer(5); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(5))); -} - -BOOST_AUTO_TEST_CASE(abi_encode) -{ - char const* sourceCode = R"( - contract C { - function f0() public returns (bytes memory) { - return abi.encode(); - } - function f1() public returns (bytes memory) { - return abi.encode(1, 2); - } - function f2() public returns (bytes memory) { - string memory x = "abc"; - return abi.encode(1, x, 2); - } - function f3() public returns (bytes memory r) { - // test that memory is properly allocated - string memory x = "abc"; - r = abi.encode(1, x, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - function f4() public returns (bytes memory) { - bytes4 x = "abcd"; - return abi.encode(bytes2(x)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x20, "ab")); -} - -BOOST_AUTO_TEST_CASE(abi_encode_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; uint[] b; } - function f0() public pure returns (bytes memory) { - return abi.encode(); - } - function f1() public pure returns (bytes memory) { - return abi.encode(1, 2); - } - function f2() public pure returns (bytes memory) { - string memory x = "abc"; - return abi.encode(1, x, 2); - } - function f3() public pure returns (bytes memory r) { - // test that memory is properly allocated - string memory x = "abc"; - r = abi.encode(1, x, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - S s; - function f4() public returns (bytes memory r) { - string memory x = "abc"; - s.a = 7; - s.b.push(2); - s.b.push(3); - r = abi.encode(1, x, s, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x160, 1, 0x80, 0xc0, 2, 3, "abc", 7, 0x40, 2, 2, 3)); -} - - -BOOST_AUTO_TEST_CASE(abi_encodePacked) -{ - char const* sourceCode = R"( - contract C { - function f0() public pure returns (bytes memory) { - return abi.encodePacked(); - } - function f1() public pure returns (bytes memory) { - return abi.encodePacked(uint8(1), uint8(2)); - } - function f2() public pure returns (bytes memory) { - string memory x = "abc"; - return abi.encodePacked(uint8(1), x, uint8(2)); - } - function f3() public pure returns (bytes memory r) { - // test that memory is properly allocated - string memory x = "abc"; - r = abi.encodePacked(uint8(1), x, uint8(2)); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - function f4() public pure returns (bytes memory) { - string memory x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; - return abi.encodePacked(uint16(0x0701), x, uint16(0x1201)); - } - function f_literal() public pure returns (bytes memory) { - return abi.encodePacked(uint8(0x01), "abc", uint8(0x02)); - } - function f_calldata() public pure returns (bytes memory) { - return abi.encodePacked(uint8(0x01), msg.data, uint8(0x02)); - } - } - )"; - for (auto v2: {false, true}) - { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs( - 0x20, - 2 + 26 + 26 + 2, - "\x07\x01" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\x12\x01" - )); - ABI_CHECK(callContractFunction("f_literal()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); - ABI_CHECK(callContractFunction("f_calldata()"), encodeArgs(0x20, 6, "\x01" "\xa5\xbf\xa1\xee" "\x02")); - } -} - -BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage) -{ - char const* sourceCode = R"( - contract C { - uint24[9] small_fixed; - int24[9] small_fixed_signed; - uint24[] small_dyn; - uint248[5] large_fixed; - uint248[] large_dyn; - bytes bytes_storage; - function sf() public returns (bytes memory) { - small_fixed[0] = 0xfffff1; - small_fixed[2] = 0xfffff2; - small_fixed[5] = 0xfffff3; - small_fixed[8] = 0xfffff4; - return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02)); - } - function sd() public returns (bytes memory) { - small_dyn.push(0xfffff1); - small_dyn.push(0x00); - small_dyn.push(0xfffff2); - small_dyn.push(0x00); - small_dyn.push(0x00); - small_dyn.push(0xfffff3); - small_dyn.push(0x00); - small_dyn.push(0x00); - small_dyn.push(0xfffff4); - return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02)); - } - function sfs() public returns (bytes memory) { - small_fixed_signed[0] = -2; - small_fixed_signed[2] = 0xffff2; - small_fixed_signed[5] = -200; - small_fixed_signed[8] = 0xffff4; - return abi.encodePacked(uint8(0x01), small_fixed_signed, uint8(0x02)); - } - function lf() public returns (bytes memory) { - large_fixed[0] = 2**248-1; - large_fixed[1] = 0xfffff2; - large_fixed[2] = 2**248-2; - large_fixed[4] = 0xfffff4; - return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02)); - } - function ld() public returns (bytes memory) { - large_dyn.push(2**248-1); - large_dyn.push(0xfffff2); - large_dyn.push(2**248-2); - large_dyn.push(0); - large_dyn.push(0xfffff4); - return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02)); - } - function bytes_short() public returns (bytes memory) { - bytes_storage = "abcd"; - return abi.encodePacked(uint8(0x01), bytes_storage, uint8(0x02)); - } - function bytes_long() public returns (bytes memory) { - bytes_storage = "0123456789012345678901234567890123456789"; - return abi.encodePacked(uint8(0x01), bytes_storage, uint8(0x02)); - } - } - )"; - for (auto v2: {false, true}) - { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); - bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); - bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); - ABI_CHECK(callContractFunction("sf()"), encoded); - ABI_CHECK(callContractFunction("sd()"), encoded); - ABI_CHECK(callContractFunction("sfs()"), encodeArgs(0x20, 0x122, "\x01" + asString(encodeArgs( - u256(-2), 0, 0xffff2, 0, 0, u256(-200), 0, 0, 0xffff4 - )) + "\x02")); - payload = encodeArgs( - u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), - 0xfffff2, - u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), - 0, - 0xfffff4 - ); - ABI_CHECK(callContractFunction("lf()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); - ABI_CHECK(callContractFunction("ld()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); - ABI_CHECK(callContractFunction("bytes_short()"), encodeArgs(0x20, 6, "\x01" "abcd\x02")); - ABI_CHECK( - callContractFunction("bytes_long()"), - encodeArgs(0x20, 42, "\x01" "0123456789012345678901234567890123456789\x02") - ); - } -} - -BOOST_AUTO_TEST_CASE(abi_encodePacked_from_memory) -{ - char const* sourceCode = R"( - contract C { - function sf() public pure returns (bytes memory) { - uint24[9] memory small_fixed; - small_fixed[0] = 0xfffff1; - small_fixed[2] = 0xfffff2; - small_fixed[5] = 0xfffff3; - small_fixed[8] = 0xfffff4; - return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02)); - } - function sd() public pure returns (bytes memory) { - uint24[] memory small_dyn = new uint24[](9); - small_dyn[0] = 0xfffff1; - small_dyn[2] = 0xfffff2; - small_dyn[5] = 0xfffff3; - small_dyn[8] = 0xfffff4; - return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02)); - } - function sfs() public pure returns (bytes memory) { - int24[9] memory small_fixed_signed; - small_fixed_signed[0] = -2; - small_fixed_signed[2] = 0xffff2; - small_fixed_signed[5] = -200; - small_fixed_signed[8] = 0xffff4; - return abi.encodePacked(uint8(0x01), small_fixed_signed, uint8(0x02)); - } - function lf() public pure returns (bytes memory) { - uint248[5] memory large_fixed; - large_fixed[0] = 2**248-1; - large_fixed[1] = 0xfffff2; - large_fixed[2] = 2**248-2; - large_fixed[4] = 0xfffff4; - return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02)); - } - function ld() public pure returns (bytes memory) { - uint248[] memory large_dyn = new uint248[](5); - large_dyn[0] = 2**248-1; - large_dyn[1] = 0xfffff2; - large_dyn[2] = 2**248-2; - large_dyn[4] = 0xfffff4; - return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02)); - } - } - )"; - for (auto v2: {false, true}) - { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); - bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); - bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); - ABI_CHECK(callContractFunction("sf()"), encoded); - ABI_CHECK(callContractFunction("sd()"), encoded); - ABI_CHECK(callContractFunction("sfs()"), encodeArgs(0x20, 0x122, "\x01" + asString(encodeArgs( - u256(-2), 0, 0xffff2, 0, 0, u256(-200), 0, 0, 0xffff4 - )) + "\x02")); - payload = encodeArgs( - u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), - 0xfffff2, - u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), - 0, - 0xfffff4 - ); - ABI_CHECK(callContractFunction("lf()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); - ABI_CHECK(callContractFunction("ld()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); - } -} - -BOOST_AUTO_TEST_CASE(abi_encodePacked_functionPtr) -{ - char const* sourceCode = R"( - contract C { - C other = C(0x1112131400000000000011121314000000000087); - function testDirect() public view returns (bytes memory) { - return abi.encodePacked(uint8(8), other.f, uint8(2)); - } - function testFixedArray() public view returns (bytes memory) { - function () external pure returns (bytes memory)[1] memory x; - x[0] = other.f; - return abi.encodePacked(uint8(8), x, uint8(2)); - } - function testDynamicArray() public view returns (bytes memory) { - function () external pure returns (bytes memory)[] memory x = new function() external pure returns (bytes memory)[](1); - x[0] = other.f; - return abi.encodePacked(uint8(8), x, uint8(2)); - } - function f() public pure returns (bytes memory) {} - } - )"; - for (auto v2: {false, true}) - { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); - string directEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "02")); - ABI_CHECK(callContractFunction("testDirect()"), encodeArgs(0x20, directEncoding.size(), directEncoding)); - string arrayEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "0000000000000000" "02")); - ABI_CHECK(callContractFunction("testFixedArray()"), encodeArgs(0x20, arrayEncoding.size(), arrayEncoding)); - ABI_CHECK(callContractFunction("testDynamicArray()"), encodeArgs(0x20, arrayEncoding.size(), arrayEncoding)); - } -} - -BOOST_AUTO_TEST_CASE(abi_encodePackedV2_structs) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { - uint8 a; - int16 b; - uint8[2] c; - int16[] d; - } - S s; - event E(S indexed); - constructor() public { - s.a = 0x12; - s.b = -7; - s.c[0] = 2; - s.c[1] = 3; - s.d.push(-7); - s.d.push(-8); - } - function testStorage() public { - emit E(s); - } - function testMemory() public { - S memory m = s; - emit E(m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bytes structEnc = encodeArgs(int(0x12), u256(-7), int(2), int(3), u256(-7), u256(-8)); - ABI_CHECK(callContractFunction("testStorage()"), encodeArgs()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16,uint8[2],int16[]))"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); - ABI_CHECK(callContractFunction("testMemory()"), encodeArgs()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16,uint8[2],int16[]))"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); -} - -BOOST_AUTO_TEST_CASE(abi_encodePackedV2_nestedArray) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { - uint8 a; - int16 b; - } - event E(S[2][][3] indexed); - function testNestedArrays() public { - S[2][][3] memory x; - x[1] = new S[2][](2); - x[1][0][0].a = 1; - x[1][0][0].b = 2; - x[1][0][1].a = 3; - x[1][1][1].b = 4; - emit E(x); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bytes structEnc = encodeArgs(1, 2, 3, 0, 0, 0, 0, 4); - ABI_CHECK(callContractFunction("testNestedArrays()"), encodeArgs()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16)[2][][3])"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); -} - -BOOST_AUTO_TEST_CASE(abi_encodePackedV2_arrayOfStrings) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - string[] x; - event E(string[] indexed); - constructor() public { - x.push("abc"); - x.push("0123456789012345678901234567890123456789"); - } - function testStorage() public { - emit E(x); - } - function testMemory() public { - string[] memory y = x; - emit E(y); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bytes arrayEncoding = encodeArgs("abc", "0123456789012345678901234567890123456789"); - ABI_CHECK(callContractFunction("testStorage()"), encodeArgs()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string[])"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(arrayEncoding))); - ABI_CHECK(callContractFunction("testMemory()"), encodeArgs()); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string[])"))); - BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(arrayEncoding))); -} - -BOOST_AUTO_TEST_CASE(event_signature_in_library) -{ - // This tests a bug that was present where the "internal signature" - // for structs was also used for events. - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - library L { - struct S { - uint8 a; - int16 b; - } - event E(S indexed, S); - function f() internal { - S memory s; - emit E(s, s); - } - } - contract C { - constructor() public { - L.f(); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16),(uint8,int16))"))); -} - - -BOOST_AUTO_TEST_CASE(abi_encode_with_selector) -{ - char const* sourceCode = R"( - contract C { - function f0() public pure returns (bytes memory) { - return abi.encodeWithSelector(0x12345678); - } - function f1() public pure returns (bytes memory) { - return abi.encodeWithSelector(0x12345678, "abc"); - } - function f2() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, "abc"); - } - function f3() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, uint(-1)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); - bytes expectation; - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f1()"), expectation); - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f2()"), expectation); - expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f3()"), expectation); -} - -BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f0() public pure returns (bytes memory) { - return abi.encodeWithSelector(0x12345678); - } - function f1() public pure returns (bytes memory) { - return abi.encodeWithSelector(0x12345678, "abc"); - } - function f2() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, "abc"); - } - function f3() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, uint(-1)); - } - struct S { uint a; string b; uint16 c; } - function f4() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - S memory s; - s.a = 0x1234567; - s.b = "Lorem ipsum dolor sit ethereum........"; - s.c = 0x1234; - return abi.encodeWithSelector(x, uint(-1), s, uint(3)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); - bytes expectation; - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f1()"), expectation); - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f2()"), expectation); - expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f3()"), expectation); - expectation = - encodeArgs(0x20, 4 + 0x120) + - bytes{0x12, 0x34, 0x56, 0x78} + - encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + - bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f4()"), expectation); -} - -BOOST_AUTO_TEST_CASE(abi_encode_with_signature) -{ - char const* sourceCode = R"T( - contract C { - function f0() public pure returns (bytes memory) { - return abi.encodeWithSignature("f(uint256)"); - } - function f1() public pure returns (bytes memory) { - string memory x = "f(uint256)"; - return abi.encodeWithSignature(x, "abc"); - } - string xstor; - function f1s() public returns (bytes memory) { - xstor = "f(uint256)"; - return abi.encodeWithSignature(xstor, "abc"); - } - function f2() public pure returns (bytes memory r, uint[] memory ar) { - string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; - uint[] memory y = new uint[](4); - y[0] = uint(-1); - y[1] = uint(-2); - y[2] = uint(-3); - y[3] = uint(-4); - r = abi.encodeWithSignature(x, y); - // The hash uses temporary memory. This allocation re-uses the memory - // and should initialize it properly. - ar = new uint[](2); - } - } - )T"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); - bytes expectation; - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f1()"), expectation); - ABI_CHECK(callContractFunction("f1s()"), expectation); - expectation = - encodeArgs(0x40, 0x140, 4 + 0xc0) + - (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + - encodeArgs(2, 0, 0); - ABI_CHECK(callContractFunction("f2()"), expectation); -} - -BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) -{ - char const* sourceCode = R"T( - pragma experimental ABIEncoderV2; - contract C { - function f0() public pure returns (bytes memory) { - return abi.encodeWithSignature("f(uint256)"); - } - function f1() public pure returns (bytes memory) { - string memory x = "f(uint256)"; - return abi.encodeWithSignature(x, "abc"); - } - string xstor; - function f1s() public returns (bytes memory) { - xstor = "f(uint256)"; - return abi.encodeWithSignature(xstor, "abc"); - } - function f2() public pure returns (bytes memory r, uint[] memory ar) { - string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; - uint[] memory y = new uint[](4); - y[0] = uint(-1); - y[1] = uint(-2); - y[2] = uint(-3); - y[3] = uint(-4); - r = abi.encodeWithSignature(x, y); - // The hash uses temporary memory. This allocation re-uses the memory - // and should initialize it properly. - ar = new uint[](2); - } - struct S { uint a; string b; uint16 c; } - function f4() public pure returns (bytes memory) { - bytes4 x = 0x12345678; - S memory s; - s.a = 0x1234567; - s.b = "Lorem ipsum dolor sit ethereum........"; - s.c = 0x1234; - return abi.encodeWithSignature(s.b, uint(-1), s, uint(3)); - } - } - )T"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); - bytes expectation; - expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f1()"), expectation); - ABI_CHECK(callContractFunction("f1s()"), expectation); - expectation = - encodeArgs(0x40, 0x140, 4 + 0xc0) + - (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + - encodeArgs(2, 0, 0); - ABI_CHECK(callContractFunction("f2()"), expectation); - expectation = - encodeArgs(0x20, 4 + 0x120) + - bytes{0x7c, 0x79, 0x30, 0x02} + - encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + - bytes(0x20 - 4); - ABI_CHECK(callContractFunction("f4()"), expectation); -} - -BOOST_AUTO_TEST_CASE(abi_encode_empty_string) -{ - char const* sourceCode = R"( - // Tests that this will not end up using a "bytes0" type - // (which would assert) - contract C { - function f() public pure returns (bytes memory, bytes memory) { - return (abi.encode(""), abi.encodePacked("")); - } + vector calltypes = {"call", "delegatecall"}; + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) + calltypes.emplace_back("staticcall"); + for (string const& calltype: calltypes) + { + string sourceCode = R"DELIMITER( + contract A { + constructor() public { + } + function return_bool() public pure returns(bool) { + return true; + } + function return_int32() public pure returns(int32) { + return -32; + } + function return_uint32() public pure returns(uint32) { + return 0x3232; + } + function return_int256() public pure returns(int256) { + return -256; + } + function return_uint256() public pure returns(uint256) { + return 0x256256; + } + function return_bytes4() public pure returns(bytes4) { + return 0xabcd0012; + } + function return_multi() public pure returns(bool, uint32, bytes4) { + return (false, 0x3232, 0xabcd0012); + } + function return_bytes() public pure returns(bytes memory b) { + b = new bytes(2); + b[0] = 0x42; + b[1] = 0x21; + } + } + contract C { + A addr; + constructor() public { + addr = new A(); + } + function f(string memory signature) public returns (bool, bytes memory) { + return address(addr).)DELIMITER" + calltype + R"DELIMITER((abi.encodeWithSignature(signature)); + } + function check_bool() external returns (bool) { + (bool success, bytes memory data) = f("return_bool()"); + assert(success); + bool a = abi.decode(data, (bool)); + assert(a); + return true; + } + function check_int32() external returns (bool) { + (bool success, bytes memory data) = f("return_int32()"); + assert(success); + int32 a = abi.decode(data, (int32)); + assert(a == -32); + return true; + } + function check_uint32() external returns (bool) { + (bool success, bytes memory data) = f("return_uint32()"); + assert(success); + uint32 a = abi.decode(data, (uint32)); + assert(a == 0x3232); + return true; + } + function check_int256() external returns (bool) { + (bool success, bytes memory data) = f("return_int256()"); + assert(success); + int256 a = abi.decode(data, (int256)); + assert(a == -256); + return true; + } + function check_uint256() external returns (bool) { + (bool success, bytes memory data) = f("return_uint256()"); + assert(success); + uint256 a = abi.decode(data, (uint256)); + assert(a == 0x256256); + return true; + } + function check_bytes4() external returns (bool) { + (bool success, bytes memory data) = f("return_bytes4()"); + assert(success); + bytes4 a = abi.decode(data, (bytes4)); + assert(a == 0xabcd0012); + return true; + } + function check_multi() external returns (bool) { + (bool success, bytes memory data) = f("return_multi()"); + assert(success); + (bool a, uint32 b, bytes4 c) = abi.decode(data, (bool, uint32, bytes4)); + assert(a == false && b == 0x3232 && c == 0xabcd0012); + return true; + } + function check_bytes() external returns (bool) { + (bool success, bytes memory data) = f("return_bytes()"); + assert(success); + (bytes memory d) = abi.decode(data, (bytes)); + assert(d.length == 2 && d[0] == 0x42 && d[1] == 0x21); + return true; + } + } + )DELIMITER"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bool()"))), encodeArgs(true, 0x40, 0x20, true)); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int32()"))), encodeArgs(true, 0x40, 0x20, u256(-32))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint32()"))), encodeArgs(true, 0x40, 0x20, u256(0x3232))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int256()"))), encodeArgs(true, 0x40, 0x20, u256(-256))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint256()"))), encodeArgs(true, 0x40, 0x20, u256(0x256256))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes4()"))), encodeArgs(true, 0x40, 0x20, u256(0xabcd0012) << (28*8))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_multi()"))), encodeArgs(true, 0x40, 0x60, false, u256(0x3232), u256(0xabcd0012) << (28*8))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes()"))), encodeArgs(true, 0x40, 0x60, 0x20, 0x02, encode(bytes{0x42,0x21}, false))); + ABI_CHECK(callContractFunction("check_bool()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_int32()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_uint32()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_int256()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_uint256()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_bytes4()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_multi()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_bytes()"), encodeArgs(true)); } - )"; - - compileAndRun(sourceCode, 0, "C"); - if (!solidity::test::CommonOptions::get().useABIEncoderV2) - { - // ABI Encoder V2 has slightly different padding, tested below. - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x40, 0xc0, - 0x60, 0x20, 0x00, 0x00, - 0x00 - )); } } -BOOST_AUTO_TEST_CASE(abi_encode_empty_string_v2) -{ - char const* sourceCode = R"( - // Tests that this will not end up using a "bytes0" type - // (which would assert) - pragma experimental ABIEncoderV2; - contract C { - function f() public pure returns (bytes memory, bytes memory) { - return (abi.encode(""), abi.encodePacked("")); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x40, 0xa0, - 0x40, 0x20, 0x00, - 0x00 - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_rational) -{ - char const* sourceCode = R"( - // Tests that rational numbers (even negative ones) are encoded properly. - contract C { - function f() public pure returns (bytes memory) { - return abi.encode(1, -2); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x20, - 0x40, u256(1), u256(-2) - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_rational_v2) +BOOST_AUTO_TEST_CASE(abi_encodePacked) { char const* sourceCode = R"( - // Tests that rational numbers (even negative ones) are encoded properly. - pragma experimental ABIEncoderV2; contract C { - function f() public pure returns (bytes memory) { - return abi.encode(1, -2); + function f0() public pure returns (bytes memory) { + return abi.encodePacked(); } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x20, - 0x40, u256(1), u256(-2) - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_call) -{ - char const* sourceCode = R"T( - contract C { - bool x; - function c(uint a, uint[] memory b) public { - require(a == 5); - require(b.length == 2); - require(b[0] == 6); - require(b[1] == 7); - x = true; + function f1() public pure returns (bytes memory) { + return abi.encodePacked(uint8(1), uint8(2)); } - function f() public returns (bool) { - uint a = 5; - uint[] memory b = new uint[](2); - b[0] = 6; - b[1] = 7; - (bool success,) = address(this).call(abi.encodeWithSignature("c(uint256,uint256[])", a, b)); - require(success); - return x; + function f2() public pure returns (bytes memory) { + string memory x = "abc"; + return abi.encodePacked(uint8(1), x, uint8(2)); } - } - )T"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) -{ - char const* sourceCode = R"( - contract C { - uint x; - function f() public returns (uint) { - x = 3; - return 1; + function f3() public pure returns (bytes memory r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encodePacked(uint8(1), x, uint8(2)); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); } - } - interface CView { - function f() view external returns (uint); - } - interface CPure { - function f() pure external returns (uint); - } - contract D { - function f() public returns (uint) { - return (new C()).f(); + function f4() public pure returns (bytes memory) { + string memory x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; + return abi.encodePacked(uint16(0x0701), x, uint16(0x1201)); } - function fview() public returns (uint) { - return (CView(address(new C()))).f(); + function f_literal() public pure returns (bytes memory) { + return abi.encodePacked(uint8(0x01), "abc", uint8(0x02)); } - function fpure() public returns (uint) { - return (CPure(address(new C()))).f(); + function f_calldata() public pure returns (bytes memory) { + return abi.encodePacked(uint8(0x01), msg.data, uint8(0x02)); } } )"; - compileAndRun(sourceCode, 0, "D"); - // This should work (called via CALL) - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - // These should throw (called via STATICCALL) - ABI_CHECK(callContractFunction("fview()"), encodeArgs()); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); - } - else + for (auto v2: {false, true}) { - ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); + compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 2 + 26 + 26 + 2, "\x07\x01" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\x12\x01")); + ABI_CHECK(callContractFunction("f_literal()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); + ABI_CHECK(callContractFunction("f_calldata()"), encodeArgs(0x20, 6, "\x01" "\xa5\xbf\xa1\xee" "\x02")); } } -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl(uint a, uint b) public returns (uint c) { - assembly { - c := shl(b, a) - } - } - function shr(uint a, uint b) public returns (uint c) { - assembly { - c := shr(b, a) - } - } - function sar(uint a, uint b) public returns (uint c) { - assembly { - c := sar(b, a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(4))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl_1() public returns (bool) { - uint c; - assembly { - c := shl(2, 1) - } - assert(c == 4); - return true; - } - function shl_2() public returns (bool) { - uint c; - assembly { - c := shl(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe); - return true; - } - function shl_3() public returns (bool) { - uint c; - assembly { - c := shl(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - function shr_1() public returns (bool) { - uint c; - assembly { - c := shr(1, 3) - } - assert(c == 1); - return true; - } - function shr_2() public returns (bool) { - uint c; - assembly { - c := shr(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - return true; - } - function shr_3() public returns (bool) { - uint c; - assembly { - c := shr(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_3()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople_combined) +BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage) { - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; char const* sourceCode = R"( contract C { - function shl_zero(uint a) public returns (uint c) { - assembly { - c := shl(0, a) - } - } - function shr_zero(uint a) public returns (uint c) { - assembly { - c := shr(0, a) - } - } - function sar_zero(uint a) public returns (uint c) { - assembly { - c := sar(0, a) - } - } - - function shl_large(uint a) public returns (uint c) { - assembly { - c := shl(0x110, a) - } - } - function shr_large(uint a) public returns (uint c) { - assembly { - c := shr(0x110, a) - } - } - function sar_large(uint a) public returns (uint c) { - assembly { - c := sar(0x110, a) - } - } - - function shl_combined(uint a) public returns (uint c) { - assembly { - c := shl(4, shl(12, a)) - } - } - function shr_combined(uint a) public returns (uint c) { - assembly { - c := shr(4, shr(12, a)) - } - } - function sar_combined(uint a) public returns (uint c) { - assembly { - c := sar(4, sar(12, a)) - } - } - - function shl_combined_large(uint a) public returns (uint c) { - assembly { - c := shl(0xd0, shl(0x40, a)) - } - } - function shl_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shl(0x01, shl(not(0x00), a)) - } + uint24[9] small_fixed; + int24[9] small_fixed_signed; + uint24[] small_dyn; + uint248[5] large_fixed; + uint248[] large_dyn; + bytes bytes_storage; + function sf() public returns (bytes memory) { + small_fixed[0] = 0xfffff1; + small_fixed[2] = 0xfffff2; + small_fixed[5] = 0xfffff3; + small_fixed[8] = 0xfffff4; + return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02)); } - function shr_combined_large(uint a) public returns (uint c) { - assembly { - c := shr(0xd0, shr(0x40, a)) - } + function sd() public returns (bytes memory) { + small_dyn.push(0xfffff1); + small_dyn.push(0x00); + small_dyn.push(0xfffff2); + small_dyn.push(0x00); + small_dyn.push(0x00); + small_dyn.push(0xfffff3); + small_dyn.push(0x00); + small_dyn.push(0x00); + small_dyn.push(0xfffff4); + return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02)); } - function shr_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shr(0x01, shr(not(0x00), a)) - } + function sfs() public returns (bytes memory) { + small_fixed_signed[0] = -2; + small_fixed_signed[2] = 0xffff2; + small_fixed_signed[5] = -200; + small_fixed_signed[8] = 0xffff4; + return abi.encodePacked(uint8(0x01), small_fixed_signed, uint8(0x02)); } - function sar_combined_large(uint a) public returns (uint c) { - assembly { - c := sar(0xd0, sar(0x40, a)) - } + function lf() public returns (bytes memory) { + large_fixed[0] = 2**248-1; + large_fixed[1] = 0xfffff2; + large_fixed[2] = 2**248-2; + large_fixed[4] = 0xfffff4; + return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02)); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff"))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff0000"))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000"))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x00007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(senders_balance) -{ - char const* sourceCode = R"( - contract C { - function f() public view returns (uint) { - return msg.sender.balance; + function ld() public returns (bytes memory) { + large_dyn.push(2**248-1); + large_dyn.push(0xfffff2); + large_dyn.push(2**248-2); + large_dyn.push(0); + large_dyn.push(0xfffff4); + return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02)); } - } - contract D { - C c = new C(); - constructor() public payable { } - function f() public view returns (uint) { - return c.f(); + function bytes_short() public returns (bytes memory) { + bytes_storage = "abcd"; + return abi.encodePacked(uint8(0x01), bytes_storage, uint8(0x02)); + } + function bytes_long() public returns (bytes memory) { + bytes_storage = "0123456789012345678901234567890123456789"; + return abi.encodePacked(uint8(0x01), bytes_storage, uint8(0x02)); } } )"; - compileAndRun(sourceCode, 27, "D"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27))); + for (auto v2: {false, true}) + { + compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); + bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); + ABI_CHECK(callContractFunction("sf()"), encoded); + ABI_CHECK(callContractFunction("sd()"), encoded); + ABI_CHECK(callContractFunction("sfs()"), encodeArgs(0x20, 0x122, "\x01" + asString(encodeArgs( + u256(-2), 0, 0xffff2, 0, 0, u256(-200), 0, 0, 0xffff4 + )) + "\x02")); + payload = encodeArgs( + u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + 0xfffff2, + u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), + 0, + 0xfffff4 + ); + ABI_CHECK(callContractFunction("lf()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); + ABI_CHECK(callContractFunction("ld()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); + ABI_CHECK(callContractFunction("bytes_short()"), encodeArgs(0x20, 6, "\x01" "abcd\x02")); + ABI_CHECK(callContractFunction("bytes_long()"), encodeArgs(0x20, 42, "\x01" "0123456789012345678901234567890123456789\x02")); + } } -BOOST_AUTO_TEST_CASE(abi_decode_trivial) +BOOST_AUTO_TEST_CASE(abi_encodePacked_from_memory) { char const* sourceCode = R"( contract C { - function f(bytes memory data) public pure returns (uint) { - return abi.decode(data, (uint)); + function sf() public pure returns (bytes memory) { + uint24[9] memory small_fixed; + small_fixed[0] = 0xfffff1; + small_fixed[2] = 0xfffff2; + small_fixed[5] = 0xfffff3; + small_fixed[8] = 0xfffff4; + return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02)); } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20, 33), encodeArgs(u256(33))); -} - -BOOST_AUTO_TEST_CASE(abi_encode_decode_simple) -{ - char const* sourceCode = R"XX( - contract C { - function f() public pure returns (uint, bytes memory) { - bytes memory arg = "abcdefg"; - return abi.decode(abi.encode(uint(33), arg), (uint, bytes)); + function sd() public pure returns (bytes memory) { + uint24[] memory small_dyn = new uint24[](9); + small_dyn[0] = 0xfffff1; + small_dyn[2] = 0xfffff2; + small_dyn[5] = 0xfffff3; + small_dyn[8] = 0xfffff4; + return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02)); } - } - )XX"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_simple) -{ - char const* sourceCode = R"( - contract C { - function f(bytes memory data) public pure returns (uint, bytes memory) { - return abi.decode(data, (uint, bytes)); + function sfs() public pure returns (bytes memory) { + int24[9] memory small_fixed_signed; + small_fixed_signed[0] = -2; + small_fixed_signed[2] = 0xffff2; + small_fixed_signed[5] = -200; + small_fixed_signed[8] = 0xffff4; + return abi.encodePacked(uint8(0x01), small_fixed_signed, uint8(0x02)); } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; uint[] b; } - function f() public pure returns (S memory) { - S memory s; - s.a = 8; - s.b = new uint[](3); - s.b[0] = 9; - s.b[1] = 10; - s.b[2] = 11; - return abi.decode(abi.encode(s), (S)); + function lf() public pure returns (bytes memory) { + uint248[5] memory large_fixed; + large_fixed[0] = 2**248-1; + large_fixed[1] = 0xfffff2; + large_fixed[2] = 2**248-2; + large_fixed[4] = 0xfffff4; + return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02)); + } + function ld() public pure returns (bytes memory) { + uint248[] memory large_dyn = new uint248[](5); + large_dyn[0] = 2**248-1; + large_dyn[1] = 0xfffff2; + large_dyn[2] = 2**248-2; + large_dyn[4] = 0xfffff4; + return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02)); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) - ); + for (auto v2: {false, true}) + { + compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); + bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); + ABI_CHECK(callContractFunction("sf()"), encoded); + ABI_CHECK(callContractFunction("sd()"), encoded); + ABI_CHECK(callContractFunction("sfs()"), encodeArgs(0x20, 0x122, "\x01" + asString(encodeArgs( + u256(-2), 0, 0xffff2, 0, 0, u256(-200), 0, 0, 0xffff4 + )) + "\x02")); + payload = encodeArgs( + u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + 0xfffff2, + u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), + 0, + 0xfffff4 + ); + ABI_CHECK(callContractFunction("lf()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); + ABI_CHECK(callContractFunction("ld()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); + } } -BOOST_AUTO_TEST_CASE(abi_decode_simple_storage) +BOOST_AUTO_TEST_CASE(abi_encodePacked_functionPtr) { char const* sourceCode = R"( contract C { - bytes data; - function f(bytes memory _data) public returns (uint, bytes memory) { - data = _data; - return abi.decode(data, (uint, bytes)); + C other = C(0x1112131400000000000011121314000000000087); + function testDirect() public view returns (bytes memory) { + return abi.encodePacked(uint8(8), other.f, uint8(2)); + } + function testFixedArray() public view returns (bytes memory) { + function () external pure returns (bytes memory)[1] memory x; + x[0] = other.f; + return abi.encodePacked(uint8(8), x, uint8(2)); + } + function testDynamicArray() public view returns (bytes memory) { + function () external pure returns (bytes memory)[] memory x = new function() external pure returns (bytes memory)[](1); + x[0] = other.f; + return abi.encodePacked(uint8(8), x, uint8(2)); } + function f() public pure returns (bytes memory) {} } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); + for (auto v2: {false, true}) + { + compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + string directEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "02")); + ABI_CHECK(callContractFunction("testDirect()"), encodeArgs(0x20, directEncoding.size(), directEncoding)); + string arrayEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "0000000000000000" "02")); + ABI_CHECK(callContractFunction("testFixedArray()"), encodeArgs(0x20, arrayEncoding.size(), arrayEncoding)); + ABI_CHECK(callContractFunction("testDynamicArray()"), encodeArgs(0x20, arrayEncoding.size(), arrayEncoding)); + } } -BOOST_AUTO_TEST_CASE(abi_decode_v2_storage) +BOOST_AUTO_TEST_CASE(abi_encodePackedV2_structs) { char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { - bytes data; - struct S { uint a; uint[] b; } - function f() public returns (S memory) { - S memory s; - s.a = 8; - s.b = new uint[](3); - s.b[0] = 9; - s.b[1] = 10; - s.b[2] = 11; - data = abi.encode(s); - return abi.decode(data, (S)); + struct S { + uint8 a; + int16 b; + uint8[2] c; + int16[] d; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_calldata) -{ - char const* sourceCode = R"( - contract C { - function f(bytes calldata data) external pure returns (uint, bytes memory r) { - return abi.decode(data, (uint, bytes)); + S s; + event E(S indexed); + constructor() public { + s.a = 0x12; + s.b = -7; + s.c[0] = 2; + s.c[1] = 3; + s.d.push(-7); + s.d.push(-8); + } + function testStorage() public { + emit E(s); + } + function testMemory() public { + S memory m = s; + emit E(m); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); + compileAndRun(sourceCode, 0, "C"); + bytes structEnc = encodeArgs(int(0x12), u256(-7), int(2), int(3), u256(-7), u256(-8)); + ABI_CHECK(callContractFunction("testStorage()"), encodeArgs()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16,uint8[2],int16[]))"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); + ABI_CHECK(callContractFunction("testMemory()"), encodeArgs()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16,uint8[2],int16[]))"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); } -BOOST_AUTO_TEST_CASE(abi_decode_v2_calldata) +BOOST_AUTO_TEST_CASE(abi_encodePackedV2_nestedArray) { char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { - struct S { uint a; uint[] b; } - function f(bytes calldata data) external pure returns (S memory) { - return abi.decode(data, (S)); + struct S { + uint8 a; + int16 b; + } + event E(S[2][][3] indexed); + function testNestedArrays() public { + S[2][][3] memory x; + x[1] = new S[2][](2); + x[1][0][0].a = 1; + x[1][0][0].b = 2; + x[1][0][1].a = 3; + x[1][1][1].b = 4; + emit E(x); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 7, 0x20, 33, 0x40, 3, 10, 11, 12), - encodeArgs(0x20, 33, 0x40, 3, 10, 11, 12) - ); + compileAndRun(sourceCode, 0, "C"); + bytes structEnc = encodeArgs(1, 2, 3, 0, 0, 0, 0, 4); + ABI_CHECK(callContractFunction("testNestedArrays()"), encodeArgs()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16)[2][][3])"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(structEnc))); } -BOOST_AUTO_TEST_CASE(abi_decode_static_array) +BOOST_AUTO_TEST_CASE(abi_encodePackedV2_arrayOfStrings) { char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { - function f(bytes calldata data) external pure returns (uint[2][3] memory) { - return abi.decode(data, (uint[2][3])); + string[] x; + event E(string[] indexed); + constructor() public { + x.push("abc"); + x.push("0123456789012345678901234567890123456789"); + } + function testStorage() public { + emit E(x); + } + function testMemory() public { + string[] memory y = x; + emit E(y); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), - encodeArgs(1, 2, 3, 4, 5, 6) - ); + compileAndRun(sourceCode, 0, "C"); + bytes arrayEncoding = encodeArgs("abc", "0123456789012345678901234567890123456789"); + ABI_CHECK(callContractFunction("testStorage()"), encodeArgs()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string[])"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(arrayEncoding))); + ABI_CHECK(callContractFunction("testMemory()"), encodeArgs()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string[])"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(asString(arrayEncoding))); } -BOOST_AUTO_TEST_CASE(abi_decode_static_array_v2) +BOOST_AUTO_TEST_CASE(event_signature_in_library) { + // This tests a bug that was present where the "internal signature" + // for structs was also used for events. char const* sourceCode = R"( pragma experimental ABIEncoderV2; + library L { + struct S { + uint8 a; + int16 b; + } + event E(S indexed, S); + function f() internal { + S memory s; + emit E(s, s); + } + } contract C { - function f(bytes calldata data) external pure returns (uint[2][3] memory) { - return abi.decode(data, (uint[2][3])); + constructor() public { + L.f(); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), - encodeArgs(1, 2, 3, 4, 5, 6) - ); + compileAndRun(sourceCode, 0, "C"); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16),(uint8,int16))"))); } -BOOST_AUTO_TEST_CASE(abi_decode_dynamic_array) +BOOST_AUTO_TEST_CASE(abi_encode_with_selector) { char const* sourceCode = R"( contract C { - function f(bytes calldata data) external pure returns (uint[] memory) { - return abi.decode(data, (uint[])); + function f0() public pure returns (bytes memory) { + return abi.encodeWithSelector(0x12345678); + } + function f1() public pure returns (bytes memory) { + return abi.encodeWithSelector(0x12345678, "abc"); + } + function f2() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); + } + function f3() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); } } )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 0x20, 4, 3, 4, 5, 6), - encodeArgs(0x20, 4, 3, 4, 5, 6) - ); + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); } -BOOST_AUTO_TEST_CASE(write_storage_external) +BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) { char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { - uint public x; - function f(uint y) public payable { - x = y; - } - function g(uint y) external { - x = y; + function f0() public pure returns (bytes memory) { + return abi.encodeWithSelector(0x12345678); } - function h() public { - this.g(12); + function f1() public pure returns (bytes memory) { + return abi.encodeWithSelector(0x12345678, "abc"); } - } - contract D { - C c = new C(); - function f() public payable returns (uint) { - c.g(3); - return c.x(); + function f2() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); } - function g() public returns (uint) { - c.g(8); - return c.x(); + function f3() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); } - function h() public returns (uint) { - c.h(); - return c.x(); + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSelector(x, uint(-1), s, uint(3)); } } )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(12)); + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x12, 0x34, 0x56, 0x78} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); } -BOOST_AUTO_TEST_CASE(test_underscore_in_hex) +BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { - char const* sourceCode = R"( - contract test { - function f(bool cond) public pure returns (uint) { - uint32 x = 0x1234_ab; - uint y = 0x1234_abcd_1234; - return cond ? x : y; + char const* sourceCode = R"T( + contract C { + function f0() public pure returns (bytes memory) { + return abi.encodeWithSignature("f(uint256)"); } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(u256(0x1234ab))); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(0x1234abcd1234))); -} - -BOOST_AUTO_TEST_CASE(flipping_sign_tests) -{ - char const* sourceCode = R"( - contract test { - function f() public returns (bool){ - int x = -2**255; - assert(-x == x); - return true; + function f1() public pure returns (bytes memory) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes memory) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes memory r, uint[] memory ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} -BOOST_AUTO_TEST_CASE(external_public_override) -{ - char const* sourceCode = R"( - contract A { - function f() external virtual returns (uint) { return 1; } - } - contract B is A { - function f() public override returns (uint) { return 2; } - function g() public returns (uint) { return f(); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ) + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); } -BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables) +BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) { - char const* sourceCode = R"( + char const* sourceCode = R"T( + pragma experimental ABIEncoderV2; contract C { - function () internal returns (uint) x; - function set() public { - C.x = g; + function f0() public pure returns (bytes memory) { + return abi.encodeWithSignature("f(uint256)"); + } + function f1() public pure returns (bytes memory) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes memory) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes memory r, uint[] memory ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); + } + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes memory) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSignature(s.b, uint(-1), s, uint(3)); } - function g() public pure returns (uint) { return 2; } - function h() public returns (uint) { return C.x(); } } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("h()"), encodeArgs()); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x7c, 0x79, 0x30, 0x02} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); } BOOST_AUTO_TEST_CASE(code_access) @@ -14118,91 +6599,6 @@ BOOST_AUTO_TEST_CASE(code_access) ABI_CHECK(codeRuntime1, codeRuntime2); } -BOOST_AUTO_TEST_CASE(code_access_padding) -{ - char const* sourceCode = R"( - contract C { - function diff() public pure returns (uint remainder) { - bytes memory a = type(D).creationCode; - bytes memory b = type(D).runtimeCode; - assembly { remainder := mod(sub(b, a), 0x20) } - } - } - contract D { - function f() public pure returns (uint) { return 7; } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // This checks that the allocation function pads to multiples of 32 bytes - ABI_CHECK(callContractFunction("diff()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(code_access_create) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint) { - bytes memory c = type(D).creationCode; - D d; - assembly { - d := create(0, add(c, 0x20), mload(c)) - } - return d.f(); - } - } - contract D { - uint x; - constructor() public { x = 7; } - function f() public view returns (uint) { return x; } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(7)); -} - -BOOST_AUTO_TEST_CASE(code_access_content) -{ - char const* sourceCode = R"( - contract C { - function testRuntime() public returns (bool) { - D d = new D(); - bytes32 runtimeHash = keccak256(type(D).runtimeCode); - bytes32 otherHash; - uint size; - assembly { - size := extcodesize(d) - extcodecopy(d, mload(0x40), 0, size) - otherHash := keccak256(mload(0x40), size) - } - require(size == type(D).runtimeCode.length); - require(runtimeHash == otherHash); - return true; - } - function testCreation() public returns (bool) { - D d = new D(); - bytes32 creationHash = keccak256(type(D).creationCode); - require(creationHash == d.x()); - return true; - } - } - contract D { - bytes32 public x; - constructor() public { - bytes32 codeHash; - assembly { - let size := codesize() - codecopy(mload(0x40), 0, size) - codeHash := keccak256(mload(0x40), size) - } - x = codeHash; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(contract_name) { char const* sourceCode = R"( @@ -14295,22 +6691,6 @@ BOOST_AUTO_TEST_CASE(uninitialized_internal_storage_function) BOOST_CHECK(result != encodeArgs(0)); } -BOOST_AUTO_TEST_CASE(uninitialized_internal_storage_function_call) -{ - char const* sourceCode = R"( - contract Test { - function() internal x; - function f() public returns (uint r) { - x(); - return 2; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("f()"), bytes{}); -} - BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index dcfb3e6bfbc3..0afe95c6d246 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -60,6 +61,7 @@ bytes SolidityExecutionFramework::compileContract( formatter.printErrorInformation(*error); BOOST_ERROR("Compiling contract failed"); } + std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName); evmasm::LinkerObject obj; if (m_compileViaYul) { @@ -70,9 +72,7 @@ bytes SolidityExecutionFramework::compileContract( // get code that does not exhaust the stack. OptimiserSettings::full() ); - if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized( - _contractName.empty() ? m_compiler.lastContractName() : _contractName - ))) + if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName))) { langutil::SourceReferenceFormatter formatter(std::cerr); @@ -84,7 +84,9 @@ bytes SolidityExecutionFramework::compileContract( obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); } else - obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); + obj = m_compiler.object(contractName); BOOST_REQUIRE(obj.linkReferences.empty()); + if (m_showMetadata) + cout << "metadata: " << m_compiler.metadata(contractName) << endl; return obj.bytecode; } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index dfcf4a46b705..aaa17525f401 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -41,9 +41,9 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework { public: - SolidityExecutionFramework() {} + SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion): - ExecutionFramework(_evmVersion) + ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} virtual bytes const& compileAndRunWithoutCheck( @@ -68,6 +68,7 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework protected: solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; }; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index cb0066bee2ab..cdaee104b128 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -119,13 +119,9 @@ bytes compileFirstExpression( NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); - vector inheritanceHierarchy; for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*contract), "Resolving names failed"); - inheritanceHierarchy = vector(1, contract); - } for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -144,7 +140,7 @@ bytes compileFirstExpression( RevertStrings::Default ); context.resetVisitedNodes(contract); - context.setInheritanceHierarchy(inheritanceHierarchy); + context.setMostDerivedContract(*contract); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack context.adjustStackOffset(parametersSize); for (vector const& variable: _localVariables) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 1938cfe86a33..3ec506998490 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -37,20 +37,7 @@ namespace fs = boost::filesystem; SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion) { - if (m_settings.count("optimize-yul")) - { - if (m_settings["optimize-yul"] == "true") - { - m_validatedSettings["optimize-yul"] = "true"; - m_settings.erase("optimize-yul"); - } - else if (m_settings["optimize-yul"] == "false") - { - m_validatedSettings["optimize-yul"] = "false"; - m_settings.erase("optimize-yul"); - m_optimiseYul = false; - } - } + m_optimiseYul = m_reader.boolSetting("optimize-yul", true); m_parserErrorRecovery = _parserErrorRecovery; } @@ -60,7 +47,7 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix parseAndAnalyze(); filterObtainedErrors(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } void SyntaxTest::setupCompiler() diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index f0892b83b5a6..1fd96ff7603a 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1120000 -// executionCost: 1160 -// totalCost: 1121160 +// codeDepositCost: 1094400 +// executionCost: 1134 +// totalCost: 1095534 // external: // a(): 1130 // b(uint256): infinite diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol new file mode 100644 index 000000000000..180d89ec185b --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes calldata data) external pure returns (uint256[] memory) { + return abi.decode(data, (uint256[])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 -> 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol new file mode 100644 index 000000000000..270a5abc0695 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol @@ -0,0 +1,12 @@ +contract C { + function f(bytes calldata data) + external + pure + returns (uint256[2][3] memory) + { + return abi.decode(data, (uint256[2][3])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol new file mode 100644 index 000000000000..46450f704bda --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bytes calldata data) + external + pure + returns (uint256[2][3] memory) + { + return abi.decode(data, (uint256[2][3])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol new file mode 100644 index 000000000000..7b873951f676 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes memory data) public pure returns (uint256) { + return abi.decode(data, (uint256)); + } +} + +// ---- +// f(bytes): 0x20, 0x20, 0x21 -> 33 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol new file mode 100644 index 000000000000..1dbf320bd534 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f() public pure returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint256[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + return abi.decode(abi.encode(s), (S)); + } +} + +// ---- +// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol new file mode 100644 index 000000000000..a5e7a9af8913 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f(bytes calldata data) external pure returns (S memory) { + return abi.decode(data, (S)); + } +} + +// ---- +// f(bytes): 0x20, 0xe0, 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc -> 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol new file mode 100644 index 000000000000..95b667e474cc --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + bytes data; + struct S { + uint256 a; + uint256[] b; + } + + function f() public returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint256[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + data = abi.encode(s); + return abi.decode(data, (S)); + } +} + +// ---- +// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol new file mode 100644 index 000000000000..ac17cf80e532 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol @@ -0,0 +1,36 @@ +contract C { + function f0() public returns (bytes memory) { + return abi.encode(); + } + + function f1() public returns (bytes memory) { + return abi.encode(1, 2); + } + + function f2() public returns (bytes memory) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + + function f3() public returns (bytes memory r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + + function f4() public returns (bytes memory) { + bytes4 x = "abcd"; + return abi.encode(bytes2(x)); + } +} + +// ---- +// f0() -> 0x20, 0x0 +// f1() -> 0x20, 0x40, 0x1, 0x2 +// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f4() -> 0x20, 0x20, "ab" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol new file mode 100644 index 000000000000..d73db5da6228 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol @@ -0,0 +1,26 @@ +contract C { + bool x; + + function c(uint256 a, uint256[] memory b) public { + require(a == 5); + require(b.length == 2); + require(b[0] == 6); + require(b[1] == 7); + x = true; + } + + function f() public returns (bool) { + uint256 a = 5; + uint256[] memory b = new uint256[](2); + b[0] = 6; + b[1] = 7; + (bool success, ) = address(this).call( + abi.encodeWithSignature("c(uint256,uint256[])", a, b) + ); + require(success); + return x; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol new file mode 100644 index 000000000000..1fbcfa53d80f --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (uint256, bytes memory) { + bytes memory arg = "abcdefg"; + return abi.decode(abi.encode(uint256(33), arg), (uint256, bytes)); + } +} + +// ---- +// f() -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol new file mode 100644 index 000000000000..704fd54dcf89 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol @@ -0,0 +1,9 @@ +// Tests that rational numbers (even negative ones) are encoded properly. +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } +} + +// ---- +// f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiDecodeV1/decode_slice.sol b/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol similarity index 100% rename from test/libsolidity/semanticTests/abiDecodeV1/decode_slice.sol rename to test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol diff --git a/test/libsolidity/semanticTests/abiDecodeV1/dynamic_memory_copy.sol b/test/libsolidity/semanticTests/abiEncoderV1/dynamic_memory_copy.sol similarity index 100% rename from test/libsolidity/semanticTests/abiDecodeV1/dynamic_memory_copy.sol rename to test/libsolidity/semanticTests/abiEncoderV1/dynamic_memory_copy.sol diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol new file mode 100644 index 000000000000..373334ee7f2a --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol @@ -0,0 +1,13 @@ +// Tests that this will not end up using a "bytes0" type +// (which would assert) +pragma experimental ABIEncoderV2; + + +contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } +} + +// ---- +// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol new file mode 100644 index 000000000000..55047880a9b0 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol @@ -0,0 +1,12 @@ +// Tests that rational numbers (even negative ones) are encoded properly. +pragma experimental ABIEncoderV2; + + +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } +} + +// ---- +// f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol new file mode 100644 index 000000000000..f6510b53da59 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol @@ -0,0 +1,53 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f0() public pure returns (bytes memory) { + return abi.encode(); + } + + function f1() public pure returns (bytes memory) { + return abi.encode(1, 2); + } + + function f2() public pure returns (bytes memory) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + + function f3() public pure returns (bytes memory r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + + S s; + + function f4() public returns (bytes memory r) { + string memory x = "abc"; + s.a = 7; + s.b.push(2); + s.b.push(3); + r = abi.encode(1, x, s, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } +} + +// ---- +// f0() -> 0x20, 0x0 +// f1() -> 0x20, 0x40, 0x1, 0x2 +// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f4() -> 0x20, 0x160, 0x1, 0x80, 0xc0, 0x2, 0x3, "abc", 0x7, 0x40, 0x2, 0x2, 0x3 diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol new file mode 100644 index 000000000000..6b6e55e02241 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol @@ -0,0 +1,11 @@ +contract C { + function f(bytes calldata data) + external + pure + returns (uint256, bytes memory r) + { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol new file mode 100644 index 000000000000..9ae6602f310f --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure returns (uint256, bytes memory) { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol new file mode 100644 index 000000000000..af37480a3798 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol @@ -0,0 +1,10 @@ +contract C { + bytes data; + + function f(bytes memory _data) public returns (uint256, bytes memory) { + data = _data; + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol new file mode 100644 index 000000000000..6ca284908fa2 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol @@ -0,0 +1,11 @@ +// Tests that this will not end up using a "bytes0" type +// (which would assert) +contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f() -> 0x40, 0xc0, 0x60, 0x20, 0x0, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol new file mode 100644 index 000000000000..a9402aa8641e --- /dev/null +++ b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol @@ -0,0 +1,6 @@ +contract Lotto { + uint256 public constant ticketPrice = 555; +} + +// ---- +// ticketPrice() -> 555 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol new file mode 100644 index 000000000000..e2b59b5303de --- /dev/null +++ b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol @@ -0,0 +1,8 @@ +contract Lotto { + uint256 public ticketPrice = 500; +} + +// ==== +// compileViaYul: also +// ---- +// ticketPrice() -> 500 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol new file mode 100644 index 000000000000..d1e9f5c20211 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol @@ -0,0 +1,12 @@ +contract C { + function test() public returns (uint256) { + // Note that this only works because computation on literals is done using + // unbounded integers. + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1; + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2; + return 0; + } +} + +// ---- +// test() -> 0 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol new file mode 100644 index 000000000000..7585980e1142 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol @@ -0,0 +1,24 @@ +contract C { + function f(uint256 d) public pure returns (uint256) { + addmod(1, 2, d); + return 2; + } + + function g(uint256 d) public pure returns (uint256) { + mulmod(1, 2, d); + return 2; + } + + function h() public pure returns (uint256) { + mulmod(0, 1, 2); + mulmod(1, 0, 2); + addmod(0, 1, 2); + addmod(1, 0, 2); + return 2; + } +} + +// ---- +// f(uint256): 0 -> FAILURE +// g(uint256): 0 -> FAILURE +// h() -> 2 diff --git a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol new file mode 100644 index 000000000000..127320fc676e --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol @@ -0,0 +1,15 @@ +contract C { + function div(uint256 a, uint256 b) public returns (uint256) { + return a / b; + } + + function mod(uint256 a, uint256 b) public returns (uint256) { + return a % b; + } +} + +// ---- +// div(uint256,uint256): 7, 2 -> 3 +// div(uint256,uint256): 7, 0 -> FAILURE # throws # +// mod(uint256,uint256): 7, 2 -> 1 +// mod(uint256,uint256): 7, 0 -> FAILURE # throws # diff --git a/test/libsolidity/semanticTests/array/array_copy_different_packing.sol b/test/libsolidity/semanticTests/array/array_copy_different_packing.sol new file mode 100644 index 000000000000..f0afcffc99e0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_different_packing.sol @@ -0,0 +1,21 @@ +contract c { + bytes8[] data1; // 4 per slot + bytes10[] data2; // 3 per slot + + function test() + public + returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) + { + data1 = new bytes8[](9); + for (uint256 i = 0; i < data1.length; ++i) data1[i] = bytes8(uint64(i)); + data2 = data1; + a = data2[1]; + b = data2[2]; + c = data2[3]; + d = data2[4]; + e = data2[5]; + } +} + +// ---- +// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/array_copy_nested_array.sol new file mode 100644 index 000000000000..9330d31623b3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_nested_array.sol @@ -0,0 +1,15 @@ +contract c { + uint256[4][] a; + uint256[10][] b; + uint256[][] c; + + function test(uint256[2][] calldata d) external returns (uint256) { + a = d; + b = a; + c = b; + return c[1][1] | c[1][2] | c[1][3] | c[1][4]; + } +} + +// ---- +// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol new file mode 100644 index 000000000000..0e225f1e598c --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol @@ -0,0 +1,20 @@ +// NOTE: This does not really test copying from storage to ABI directly, +// because it will always copy to memory first. +contract c { + int16[] x; + + function test() public returns (int16[] memory) { + x.push(int16(-1)); + x.push(int16(-1)); + x.push(int16(8)); + x.push(int16(-16)); + x.push(int16(-2)); + x.push(int16(6)); + x.push(int16(8)); + x.push(int16(-1)); + return x; + } +} + +// ---- +// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol new file mode 100644 index 000000000000..4432776fbc77 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol @@ -0,0 +1,14 @@ +contract c { + uint256[9] data1; + uint256[] data2; + + function test() public returns (uint256 x, uint256 y) { + data1[8] = 4; + data2 = data1; + x = data2.length; + y = data2[8]; + } +} + +// ---- +// test() -> 9, 4 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol new file mode 100644 index 000000000000..71601d477140 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol @@ -0,0 +1,17 @@ +contract c { + uint256[40] data1; + uint256[20] data2; + + function test() public returns (uint256 x, uint256 y) { + data1[30] = 4; + data1[2] = 7; + data1[3] = 9; + data2[3] = 8; + data1 = data2; + x = data1[3]; + y = data1[30]; // should be cleared + } +} + +// ---- +// test() -> 8, 0 diff --git a/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol new file mode 100644 index 000000000000..1bbd95f6fae5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol @@ -0,0 +1,22 @@ +// since the copy always copies whole slots, we have to make sure that the source size maxes +// out a whole slot and at the same time there are still elements left in the target at that point +contract c { + bytes8[4] data1; // fits into one slot + bytes10[6] data2; // 4 elements need two slots + + function test() public returns (bytes10 r1, bytes10 r2, bytes10 r3) { + data1[0] = bytes8(uint64(1)); + data1[1] = bytes8(uint64(2)); + data1[2] = bytes8(uint64(3)); + data1[3] = bytes8(uint64(4)); + for (uint256 i = 0; i < data2.length; ++i) + data2[i] = bytes10(uint80(0xffff00 | (1 + i))); + data2 = data1; + r1 = data2[3]; + r2 = data2[4]; + r3 = data2[5]; + } +} + +// ---- +// test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/array/array_copy_target_simple.sol b/test/libsolidity/semanticTests/array/array_copy_target_simple.sol new file mode 100644 index 000000000000..ab82589c22b6 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_target_simple.sol @@ -0,0 +1,21 @@ +contract c { + bytes8[9] data1; // 4 per slot + bytes17[10] data2; // 1 per slot, no offset counter + + function test() + public + returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) + { + for (uint256 i = 0; i < data1.length; ++i) data1[i] = bytes8(uint64(i)); + data2[8] = data2[9] = bytes8(uint64(2)); + data2 = data1; + a = data2[1]; + b = data2[2]; + c = data2[3]; + d = data2[4]; + e = data2[9]; + } +} + +// ---- +// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0 diff --git a/test/libsolidity/semanticTests/array/array_pop.sol b/test/libsolidity/semanticTests/array/array_pop.sol new file mode 100644 index 000000000000..5667b61d411c --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop.sol @@ -0,0 +1,16 @@ +contract c { + uint256[] data; + + function test() public returns (uint256 x, uint256 l) { + data.push(7); + data.push(3); + x = data.length; + data.pop(); + x = data.length; + data.pop(); + l = data.length; + } +} + +// ---- +// test() -> 1, 0 diff --git a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol new file mode 100644 index 000000000000..8fc018d72c5f --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol @@ -0,0 +1,11 @@ +contract c { + uint256[] data; + + function test() public returns (bool) { + data.pop(); + return true; + } +} + +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/array_pop_isolated.sol b/test/libsolidity/semanticTests/array/array_pop_isolated.sol new file mode 100644 index 000000000000..2e6eac83e524 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_isolated.sol @@ -0,0 +1,13 @@ +// This tests that the compiler knows the correct size of the function on the stack. +contract c { + uint256[] data; + + function test() public returns (uint256 x) { + x = 2; + data.pop; + x = 3; + } +} + +// ---- +// test() -> 3 diff --git a/test/libsolidity/semanticTests/array/array_push.sol b/test/libsolidity/semanticTests/array/array_push.sol new file mode 100644 index 000000000000..bd8200a37dec --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push.sol @@ -0,0 +1,19 @@ +contract c { + uint256[] data; + + function test() + public + returns (uint256 x, uint256 y, uint256 z, uint256 l) + { + data.push(5); + x = data[0]; + data.push(4); + y = data[1]; + data.push(3); + l = data.length; + z = data[2]; + } +} + +// ---- +// test() -> 5, 4, 3, 3 diff --git a/test/libsolidity/semanticTests/array/array_push_packed_array.sol b/test/libsolidity/semanticTests/array/array_push_packed_array.sol new file mode 100644 index 000000000000..dd5e2e85539a --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push_packed_array.sol @@ -0,0 +1,16 @@ +contract c { + uint80[] x; + + function test() public returns (uint80, uint80, uint80, uint80) { + x.push(1); + x.push(2); + x.push(3); + x.push(4); + x.push(5); + x.pop(); + return (x[0], x[1], x[2], x[3]); + } +} + +// ---- +// test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/array_push_struct.sol b/test/libsolidity/semanticTests/array/array_push_struct.sol new file mode 100644 index 000000000000..e407fc0258cc --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push_struct.sol @@ -0,0 +1,23 @@ +contract c { + struct S { + uint16 a; + uint16 b; + uint16[3] c; + uint16[] d; + } + S[] data; + + function test() public returns (uint16, uint16, uint16, uint16) { + S memory s; + s.a = 2; + s.b = 3; + s.c[2] = 4; + s.d = new uint16[](4); + s.d[2] = 5; + data.push(s); + return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); + } +} + +// ---- +// test() -> 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol new file mode 100644 index 000000000000..9cec148c7b75 --- /dev/null +++ b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol @@ -0,0 +1,18 @@ +contract Test { + uint24[3][] public data; + + function set(uint24[3][] memory _data) public returns (uint256) { + data = _data; + return data.length; + } + + function get() public returns (uint24[3][] memory) { + return data; + } +} +// ---- +// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 +// data(uint256,uint256): 0x02, 0x02 -> 0x09 +// data(uint256,uint256): 0x05, 0x01 -> 0x11 +// data(uint256,uint256): 0x06, 0x00 -> FAILURE +// get() -> 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol new file mode 100644 index 000000000000..d21bcc53f3c3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(uint24[3][] memory _data, uint256 a, uint256 b) + public + returns (uint256 l, uint256 e) + { + l = _data.length; + e = _data[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(uint24[3][],uint256,uint256): 0x60, 0x03, 0x02, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06, 0x0c diff --git a/test/libsolidity/semanticTests/array/byte_array_pop.sol b/test/libsolidity/semanticTests/array/byte_array_pop.sol new file mode 100644 index 000000000000..5ed849702290 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop.sol @@ -0,0 +1,17 @@ +contract c { + bytes data; + + function test() public returns (uint256 x, uint256 y, uint256 l) { + data.push(0x07); + data.push(0x03); + x = data.length; + data.pop(); + data.pop(); + data.push(0x02); + y = data.length; + l = data.length; + } +} + +// ---- +// test() -> 2, 1, 1 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol new file mode 100644 index 000000000000..2589f1f558c0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function test() public returns (bytes memory) { + for (uint256 i = 0; i < 33; i++) data.push(0x03); + for (uint256 j = 0; j < 4; j++) data.pop(); + return data; + } +} + +// ---- +// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol new file mode 100644 index 000000000000..30ffa3a4c67c --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol @@ -0,0 +1,14 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + + function test() public returns (bool) { + data.pop(); + return true; + } +} + +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol new file mode 100644 index 000000000000..a9e3fd383bac --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol @@ -0,0 +1,13 @@ +// This tests that the compiler knows the correct size of the function on the stack. +contract c { + bytes data; + + function test() public returns (uint256 x) { + x = 2; + data.pop; + x = 3; + } +} + +// ---- +// test() -> 3 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol new file mode 100644 index 000000000000..1f6d500bd2a0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function test() public returns (bytes memory) { + for (uint256 i = 0; i < 34; i++) data.push(0x03); + data.pop(); + return data; + } +} + +// ---- +// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_push.sol b/test/libsolidity/semanticTests/array/byte_array_push.sol new file mode 100644 index 000000000000..67ed87b691e1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_push.sol @@ -0,0 +1,18 @@ +contract c { + bytes data; + + function test() public returns (bool x) { + data.push(0x05); + if (data.length != 1) return true; + if (data[0] != 0x05) return true; + data.push(0x04); + if (data[1] != 0x04) return true; + data.push(0x03); + uint256 l = data.length; + if (data[2] != 0x03) return true; + if (l != 0x03) return true; + } +} + +// ---- +// test() -> false diff --git a/test/libsolidity/semanticTests/array/byte_array_push_transition.sol b/test/libsolidity/semanticTests/array/byte_array_push_transition.sol new file mode 100644 index 000000000000..ceecf6726c27 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_push_transition.sol @@ -0,0 +1,18 @@ +// Tests transition between short and long encoding +contract c { + bytes data; + + function test() public returns (uint256) { + for (uint8 i = 1; i < 40; i++) { + data.push(bytes1(i)); + if (data.length != i) return 0x1000 + i; + if (data[data.length - 1] != bytes1(i)) return i; + } + for (uint8 i = 1; i < 40; i++) + if (data[i - 1] != bytes1(i)) return 0x1000000 + i; + return 0; + } +} + +// ---- +// test() -> 0 diff --git a/test/libsolidity/semanticTests/array/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/bytes_delete_element.sol new file mode 100644 index 000000000000..e3a8ec19d196 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_delete_element.sol @@ -0,0 +1,19 @@ +contract c { + bytes data; + + function test1() external returns (bool) { + data = new bytes(100); + for (uint256 i = 0; i < data.length; i++) data[i] = bytes1(uint8(i)); + delete data[94]; + delete data[96]; + delete data[98]; + return + data[94] == 0 && + uint8(data[95]) == 95 && + data[96] == 0 && + uint8(data[97]) == 97; + } +} + +// ---- +// test1() -> true diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol new file mode 100644 index 000000000000..8c48720175a0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -0,0 +1,17 @@ +contract c { + function set() public returns (bool) { + data = msg.data; + return true; + } + + function getLength() public returns (uint256) { + return data.length; + } + + bytes data; +} + +// ---- +// getLength() -> 0 +// set(): 1, 2 -> true +// getLength() -> 68 diff --git a/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol new file mode 100644 index 000000000000..5b7fd7ac3f2d --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(bytes memory _data, uint256 i) + public + returns (uint256 l, bytes1 c) + { + l = _data.length; + c = _data[i]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(bytes,uint256): 0x40, 0x03, 0x08, "abcdefgh" -> 0x08, "d" diff --git a/test/libsolidity/semanticTests/array/calldata_array.sol b/test/libsolidity/semanticTests/array/calldata_array.sol new file mode 100644 index 000000000000..c9c6dbda0981 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[2] calldata s) + external + pure + returns (uint256 a, uint256 b) + { + a = s[0]; + b = s[1]; + } +} + +// ---- +// f(uint256[2]): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol new file mode 100644 index 000000000000..d643d39739f0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol @@ -0,0 +1,21 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][] calldata a) external returns (uint256) { + return 42; + } + + function g(uint256[][] calldata a) external returns (uint256) { + a[0]; + return 42; + } +} + +// ---- +// f(uint256[][]): 0x20, 0x0 -> 42 # valid access stub # +// f(uint256[][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # +// f(uint256[][]): 0x20, 0x1, 0x20 -> 42 # invalid on outer access # +// g(uint256[][]): 0x20, 0x1, 0x20 -> FAILURE +// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x42 -> 42 # invalid on inner access # +// g(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x42 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol new file mode 100644 index 000000000000..3efd177b2ca4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol @@ -0,0 +1,30 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][1][] calldata a) external returns (uint256) { + return 42; + } + + function g(uint256[][1][] calldata a) external returns (uint256) { + a[0]; + return 42; + } + + function h(uint256[][1][] calldata a) external returns (uint256) { + a[0][0]; + return 42; + } +} + +// ---- +// f(uint256[][1][]): 0x20, 0x0 -> 42 # valid access stub # +// f(uint256[][1][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # +// f(uint256[][1][]): 0x20, 0x1, 0x20 -> 42 # invalid on outer access # +// g(uint256[][1][]): 0x20, 0x1, 0x20 -> FAILURE +// f(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> 42 # invalid on inner access # +// g(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> 42 +// h(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> FAILURE +// f(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> 42 +// g(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> 42 +// h(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol new file mode 100644 index 000000000000..33311226500a --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S[] calldata s) + external + pure + returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) + { + l = s.length; + a = s[0].a; + b = s[0].b; + c = s[1].a; + d = s[1].b; + } +} + +// ---- +// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol b/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol new file mode 100644 index 000000000000..f408746f5f15 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol @@ -0,0 +1,25 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S[] calldata s) + external + pure + returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) + { + S[] memory m = s; + l = m.length; + a = m[0].a; + b = m[0].b; + c = m[1].a; + d = m[1].b; + } +} + +// ---- +// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol b/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol new file mode 100644 index 000000000000..7b225a33b13a --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][] calldata a) + external + returns (uint256, uint256[] memory) + { + uint256[] memory m = a[0]; + return (a.length, m); + } +} + +// ---- +// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x17, 0x2a -> 0x1, 0x40, 0x2, 0x17, 0x2a diff --git a/test/libsolidity/semanticTests/array/calldata_slice_access.sol b/test/libsolidity/semanticTests/array/calldata_slice_access.sol index 8e8a398de268..7eb975677aa7 100644 --- a/test/libsolidity/semanticTests/array/calldata_slice_access.sol +++ b/test/libsolidity/semanticTests/array/calldata_slice_access.sol @@ -6,6 +6,8 @@ contract C { return (x[start:end][index], x[start:][0:end-start][index], x[:end][start:][index]); } } +// ==== +// compileViaYul: also // ---- // f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> // f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 -> diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol new file mode 100644 index 000000000000..017de5d7b32a --- /dev/null +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -0,0 +1,14 @@ +contract C { + uint256 constant LEN = 3; + uint256[LEN] public a; + + constructor(uint256[LEN] memory _a) public { + a = _a; + } +} + +// ---- +// constructor(): 1, 2, 3 -> +// a(uint256): 0 -> 1 +// a(uint256): 1 -> 2 +// a(uint256): 2 -> 3 diff --git a/test/libsolidity/semanticTests/array/copy_function_storage_array.sol b/test/libsolidity/semanticTests/array/copy_function_storage_array.sol new file mode 100644 index 000000000000..eba61ad1afa1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copy_function_storage_array.sol @@ -0,0 +1,18 @@ +contract C { + function() internal returns (uint)[] x; + function() internal returns (uint)[] y; + + function test() public returns (uint256) { + x = new function() internal returns (uint)[](10); + x[9] = a; + y = x; + return y[9](); + } + + function a() public returns (uint256) { + return 7; + } +} + +// ---- +// test() -> 7 diff --git a/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol b/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol new file mode 100644 index 000000000000..076cbbf69a77 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol @@ -0,0 +1,22 @@ +contract C { + function() internal returns (uint)[20] x; + int256 mutex; + + function one() public returns (uint256) { + function() internal returns (uint)[20] memory xmem; + x = xmem; + return 3; + } + + function two() public returns (uint256) { + if (mutex > 0) return 7; + mutex = 1; + // If this test fails, it might re-execute this function. + x[0](); + return 2; + } +} + +// ---- +// one() -> 3 +// two() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol new file mode 100644 index 000000000000..7c2ccde6e28e --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256) { + uint256[][] memory a = new uint256[][](0); + return 7; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/array/create_memory_array.sol b/test/libsolidity/semanticTests/array/create_memory_array.sol new file mode 100644 index 000000000000..fd4fe943cb65 --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_memory_array.sol @@ -0,0 +1,21 @@ +contract C { + struct S { + uint256[2] a; + bytes b; + } + + function f() public returns (bytes1, uint256, uint256, bytes1) { + bytes memory x = new bytes(200); + x[199] = "A"; + uint256[2][] memory y = new uint256[2][](300); + y[203][1] = 8; + S[] memory z = new S[](180); + z[170].a[1] = 4; + z[170].b = new bytes(102); + z[170].b[99] = "B"; + return (x[199], y[203][1], z[170].a[1], z[170].b[99]); + } +} + +// ---- +// f() -> "A", 8, 4, "B" diff --git a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol new file mode 100644 index 000000000000..c42f8486217b --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol @@ -0,0 +1,24 @@ +contract C { + function f() public returns (uint256) { + uint256 l = 2**256 / 32; + // This used to work without causing an error. + uint256[] memory x = new uint256[](l); + uint256[] memory y = new uint256[](1); + x[1] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[1]; + } + function g() public returns (uint256) { + uint256 l = 2**256 / 2 + 1; + // This used to work without causing an error. + uint16[] memory x = new uint16[](l); + uint16[] memory y = new uint16[](1); + x[2] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[2]; + }} +// ---- +// f() -> FAILURE +// g() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol new file mode 100644 index 000000000000..731fb43128b7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol @@ -0,0 +1,34 @@ +contract C { + function f() public returns (uint256) { + uint256[][] memory x = new uint256[][](42); + assert(x[0].length == 0); + x[0] = new uint256[](1); + x[0][0] = 1; + assert(x[4].length == 0); + x[4] = new uint256[](1); + x[4][0] = 2; + assert(x[10].length == 0); + x[10] = new uint256[](1); + x[10][0] = 44; + uint256[][] memory y = new uint256[][](24); + assert(y[0].length == 0); + y[0] = new uint256[](1); + y[0][0] = 1; + assert(y[4].length == 0); + y[4] = new uint256[](1); + y[4][0] = 2; + assert(y[10].length == 0); + y[10] = new uint256[](1); + y[10][0] = 88; + if ( + (x[0][0] == y[0][0]) && + (x[4][0] == y[4][0]) && + (x[10][0] == 44) && + (y[10][0] == 88) + ) return 7; + return 0; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol b/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol new file mode 100644 index 000000000000..316b8d9efd1e --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol @@ -0,0 +1,20 @@ +// Test for a bug where we did not increment the counter properly while deleting a dynamic array. +contract C { + struct S { + uint256 x; + uint256[] y; + } + S[] data; + + function f() public returns (bool) { + S storage s1 = data.push(); + s1.x = 2**200; + S storage s2 = data.push(); + s2.x = 2**200; + delete data; + return true; + } +} + +// ---- +// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access # diff --git a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol new file mode 100644 index 000000000000..6680ec5d910a --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol @@ -0,0 +1,53 @@ +contract c { + struct Data { + uint256 x; + uint256 y; + } + Data[] data; + uint256[] ids; + + function setIDStatic(uint256 id) public { + ids[2] = id; + } + + function setID(uint256 index, uint256 id) public { + ids[index] = id; + } + + function setData(uint256 index, uint256 x, uint256 y) public { + data[index].x = x; + data[index].y = y; + } + + function getID(uint256 index) public returns (uint256) { + return ids[index]; + } + + function getData(uint256 index) public returns (uint256 x, uint256 y) { + x = data[index].x; + y = data[index].y; + } + + function getLengths() public returns (uint256 l1, uint256 l2) { + l1 = data.length; + l2 = ids.length; + } + + function setLengths(uint256 l1, uint256 l2) public { + while (data.length < l1) data.push(); + while (ids.length < l2) ids.push(); + } +} + +// ---- +// getLengths() -> 0, 0 +// setLengths(uint256,uint256): 48, 49 -> +// getLengths() -> 48, 49 +// setIDStatic(uint256): 11 -> +// getID(uint256): 2 -> 11 +// setID(uint256,uint256): 7, 8 -> +// getID(uint256): 7 -> 8 +// setData(uint256,uint256,uint256): 7, 8, 9 -> +// setData(uint256,uint256,uint256): 8, 10, 11 -> +// getData(uint256): 7 -> 8, 9 +// getData(uint256): 8 -> 10, 11 diff --git a/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol new file mode 100644 index 000000000000..b2c6893a5340 --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol @@ -0,0 +1,34 @@ +contract c { + uint256[] data; + + function enlarge(uint256 amount) public returns (uint256) { + while (data.length < amount) data.push(); + return data.length; + } + + function set(uint256 index, uint256 value) public returns (bool) { + data[index] = value; + return true; + } + + function get(uint256 index) public returns (uint256) { + return data[index]; + } + + function length() public returns (uint256) { + return data.length; + } +} + +// ==== +// compileViaYul: also +// ---- +// length() -> 0 +// get(uint256): 3 -> FAILURE +// enlarge(uint256): 4 -> 4 +// length() -> 4 +// set(uint256,uint256): 3, 4 -> true +// get(uint256): 3 -> 4 +// length() -> 4 +// set(uint256,uint256): 4, 8 -> FAILURE +// length() -> 4 diff --git a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol new file mode 100644 index 000000000000..af14b48b4016 --- /dev/null +++ b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol @@ -0,0 +1,19 @@ +contract A { + uint256[3] arr; + bool public test = false; + + function getElement(uint256 i) public returns (uint256) { + return arr[i]; + } + + function testIt() public returns (bool) { + uint256 i = this.getElement(5); + test = true; + return true; + } +} + +// ---- +// test() -> false +// testIt() -> FAILURE +// test() -> false diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol new file mode 100644 index 000000000000..61d1a33f55d2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -0,0 +1,21 @@ +contract A { + function f(uint16 input) public pure returns (uint16[5] memory arr) { + arr[0] = input; + arr[1] = ++input; + arr[2] = ++input; + arr[3] = ++input; + arr[4] = ++input; + } +} + + +contract B { + function f() public returns (uint16[5] memory res, uint16[5] memory res2) { + A a = new A(); + res = a.f(2); + res2 = a.f(1000); + } +} + +// ---- +// f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol new file mode 100644 index 000000000000..ff13db5be126 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -0,0 +1,14 @@ +contract Creator { + uint256 public r; + address public ch; + + constructor(address[3] memory s, uint256 x) public { + r = x; + ch = s[2]; + } +} + +// ---- +// constructor(): 1, 2, 3, 4 -> +// r() -> 4 +// ch() -> 3 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol new file mode 100644 index 000000000000..b65a3d254d54 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol @@ -0,0 +1,45 @@ +contract c { + struct Data { + uint256 x; + uint256 y; + } + Data[2**10] data; + uint256[2**10 + 3] ids; + + function setIDStatic(uint256 id) public { + ids[2] = id; + } + + function setID(uint256 index, uint256 id) public { + ids[index] = id; + } + + function setData(uint256 index, uint256 x, uint256 y) public { + data[index].x = x; + data[index].y = y; + } + + function getID(uint256 index) public returns (uint256) { + return ids[index]; + } + + function getData(uint256 index) public returns (uint256 x, uint256 y) { + x = data[index].x; + y = data[index].y; + } + + function getLengths() public returns (uint256 l1, uint256 l2) { + l1 = data.length; + l2 = ids.length; + } +} +// ---- +// setIDStatic(uint256): 0xb -> +// getID(uint256): 0x2 -> 0xb +// setID(uint256,uint256): 0x7, 0x8 -> +// getID(uint256): 0x7 -> 0x8 +// setData(uint256,uint256,uint256): 0x7, 0x8, 0x9 -> +// setData(uint256,uint256,uint256): 0x8, 0xa, 0xb -> +// getData(uint256): 0x7 -> 0x8, 0x9 +// getData(uint256): 0x8 -> 0xa, 0xb +// getLengths() -> 0x400, 0x403 diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol new file mode 100644 index 000000000000..9ab74eaf363a --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol @@ -0,0 +1,17 @@ +contract C { + bytes16[] public data; + + function f(bytes32 x) public returns (bytes1) { + return x[2]; + } + + function g(bytes32 x) public returns (uint256) { + data = [x[0], x[1], x[2]]; + data[0] = "12345"; + return uint256(uint8(data[0][4])); + } +} +// ---- +// f(bytes32): "789" -> "9" +// g(bytes32): "789" -> 0x35 +// data(uint256): 0x01 -> "8" diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol new file mode 100644 index 000000000000..7a6afbae7de3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol @@ -0,0 +1,10 @@ +contract C { + bytes1 a; + + function f(bytes32 x) public returns (uint256, uint256, uint256) { + return (x.length, bytes16(uint128(2)).length, a.length + 7); + } +} + +// ---- +// f(bytes32): "789" -> 32, 16, 8 diff --git a/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol new file mode 100644 index 000000000000..06246cdc6160 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol @@ -0,0 +1,28 @@ +contract c { + uint256[4] data; + + function set(uint256 index, uint256 value) public returns (bool) { + data[index] = value; + return true; + } + + function get(uint256 index) public returns (uint256) { + return data[index]; + } + + function length() public returns (uint256) { + return data.length; + } +} + +// ==== +// compileViaYul: also +// ---- +// length() -> 4 +// set(uint256,uint256): 3, 4 -> true +// set(uint256,uint256): 4, 5 -> FAILURE +// set(uint256,uint256): 400, 5 -> FAILURE +// get(uint256): 3 -> 4 +// get(uint256): 4 -> FAILURE +// get(uint256): 400 -> FAILURE +// length() -> 4 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol new file mode 100644 index 000000000000..64f8c74028fb --- /dev/null +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -0,0 +1,45 @@ +contract D { + function f(function() external returns (function() external returns (uint))[] memory x) + public returns (function() external returns (uint)[3] memory r) { + r[0] = x[0](); + r[1] = x[1](); + r[2] = x[2](); + } +} + + +contract C { + function test() public returns (uint256, uint256, uint256) { + function() external returns (function() external returns (uint))[] memory x = + new function() external returns (function() external returns (uint))[](10); + for (uint256 i = 0; i < x.length; i++) x[i] = this.h; + x[0] = this.htwo; + function() external returns (uint)[3] memory y = (new D()).f(x); + return (y[0](), y[1](), y[2]()); + } + + function e() public returns (uint256) { + return 5; + } + + function f() public returns (uint256) { + return 6; + } + + function g() public returns (uint256) { + return 7; + } + + uint256 counter; + + function h() public returns (function() external returns (uint)) { + return counter++ == 0 ? this.f : this.g; + } + + function htwo() public returns (function() external returns (uint)) { + return this.e; + } +} + +// ---- +// test() -> 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/function_memory_array.sol b/test/libsolidity/semanticTests/array/function_memory_array.sol new file mode 100644 index 000000000000..cc6b3cf468e3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/function_memory_array.sol @@ -0,0 +1,40 @@ +contract C { + function a(uint256 x) public returns (uint256) { + return x + 1; + } + + function b(uint256 x) public returns (uint256) { + return x + 2; + } + + function c(uint256 x) public returns (uint256) { + return x + 3; + } + + function d(uint256 x) public returns (uint256) { + return x + 5; + } + + function e(uint256 x) public returns (uint256) { + return x + 8; + } + + function test(uint256 x, uint256 i) public returns (uint256) { + function(uint) internal returns (uint)[] memory arr = + new function(uint) internal returns (uint)[](10); + arr[0] = a; + arr[1] = b; + arr[2] = c; + arr[3] = d; + arr[4] = e; + return arr[i](x); + } +} + +// ---- +// test(uint256,uint256): 10, 0 -> 11 +// test(uint256,uint256): 10, 1 -> 12 +// test(uint256,uint256): 10, 2 -> 13 +// test(uint256,uint256): 10, 3 -> 15 +// test(uint256,uint256): 10, 4 -> 18 +// test(uint256,uint256): 10, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol b/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol new file mode 100644 index 000000000000..8dc5a7451b9f --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (uint256) { + return ([1, 2, 3, 4][2]); + } +} + +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol b/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol new file mode 100644 index 000000000000..19dfcf3b75e1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol @@ -0,0 +1,15 @@ +contract C { + string public tester; + + function f() public returns (string memory) { + return (["abc", "def", "g"][0]); + } + + function test() public { + tester = f(); + } +} + +// ---- +// test() -> +// tester() -> 0x20, 0x3, "abc" diff --git a/test/libsolidity/semanticTests/array/inline_array_return.sol b/test/libsolidity/semanticTests/array/inline_array_return.sol new file mode 100644 index 000000000000..e247d15586c8 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_return.sol @@ -0,0 +1,15 @@ +contract C { + uint8[] tester; + + function f() public returns (uint8[5] memory) { + return ([1, 2, 3, 4, 5]); + } + + function test() public returns (uint8, uint8, uint8, uint8, uint8) { + tester = f(); + return (tester[0], tester[1], tester[2], tester[3], tester[4]); + } +} + +// ---- +// f() -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/array/inline_array_singleton.sol b/test/libsolidity/semanticTests/array/inline_array_singleton.sol new file mode 100644 index 000000000000..5925aba0f28e --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_singleton.sol @@ -0,0 +1,9 @@ +// This caused a failure since the type was not converted to its mobile type. +contract C { + function f() public returns (uint256) { + return [4][0]; + } +} + +// ---- +// f() -> 4 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol new file mode 100644 index 000000000000..f3f37ea20edb --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint256 x, uint256 y) { + x = 3; + y = 6; + uint256[2] memory z = [x, y]; + return (z[0], z[1]); + } +} + +// ---- +// f() -> 3, 6 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol new file mode 100644 index 000000000000..fa9f24a7a856 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol @@ -0,0 +1,12 @@ +contract C { + string s = "doh"; + + function f() public returns (string memory, string memory) { + string memory t = "ray"; + string[3] memory x = [s, t, "mi"]; + return (x[1], x[2]); + } +} + +// ---- +// f() -> 0x40, 0x80, 0x3, "ray", 0x2, "mi" diff --git a/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol new file mode 100644 index 000000000000..ebcae638a105 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 i) public returns (string memory) { + string[4] memory x = ["This", "is", "an", "array"]; + return (x[i]); + } +} + +// ---- +// f(uint256): 0 -> 0x20, 0x4, "This" +// f(uint256): 1 -> 0x20, 0x2, "is" +// f(uint256): 2 -> 0x20, 0x2, "an" +// f(uint256): 3 -> 0x20, 0x5, "array" diff --git a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol new file mode 100644 index 000000000000..be824b759fc6 --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol @@ -0,0 +1,19 @@ +contract Test { + uint24[3][][4] data; + + function set(uint24[3][][4] memory x) + internal + returns (uint24[3][][4] memory) + { + x[1][2][2] = 1; + x[1][3][2] = 7; + return x; + } + + function f() public returns (uint24[3][] memory) { + while (data[1].length < 4) data[1].push(); + return set(data)[1]; + } +} +// ---- +// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol new file mode 100644 index 000000000000..7a8a18670ea5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol @@ -0,0 +1,14 @@ +contract Test { + function set(uint24[3][4] memory x) public { + x[2][2] = 1; + x[3][2] = 7; + } + + function f() public returns (uint24[3][4] memory) { + uint24[3][4] memory data; + set(data); + return data; + } +} +// ---- +// f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol new file mode 100644 index 000000000000..b641ec1daa9c --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol @@ -0,0 +1,17 @@ +// Computes binomial coefficients the chinese way +contract C { + function f(uint256 n, uint256 k) public returns (uint256) { + uint256[][] memory rows = new uint256[][](n + 1); + for (uint256 i = 1; i <= n; i++) { + rows[i] = new uint256[](i); + rows[i][0] = rows[i][rows[i].length - 1] = 1; + for (uint256 j = 1; j < i - 1; j++) + rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; + } + return rows[n][k - 1]; + } +} + +// ---- +// f(uint256,uint256): 3, 1 -> 1 +// f(uint256,uint256): 9, 5 -> 70 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol new file mode 100644 index 000000000000..b2876eeb335a --- /dev/null +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -0,0 +1,26 @@ +// Invoke some features that use memory and test that they do not interfere with each other. +contract Helper { + uint256 public flag; + + constructor(uint256 x) public { + flag = x; + } +} + + +contract Main { + mapping(uint256 => uint256) map; + + function f(uint256 x) public returns (uint256) { + map[x] = x; + return + (new Helper(uint256(keccak256(abi.encodePacked(this.g(map[x])))))) + .flag(); + } + + function g(uint256 a) public returns (uint256) { + return map[a]; + } +} +// ---- +// f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 diff --git a/test/libsolidity/semanticTests/array/storage_array_ref.sol b/test/libsolidity/semanticTests/array/storage_array_ref.sol new file mode 100644 index 000000000000..0b0224bfc490 --- /dev/null +++ b/test/libsolidity/semanticTests/array/storage_array_ref.sol @@ -0,0 +1,60 @@ +contract BinarySearch { + /// Finds the position of _value in the sorted list _data. + /// Note that "internal" is important here, because storage references only work for internal or private functions + function find(uint256[] storage _data, uint256 _value) + internal + returns (uint256 o_position) + { + return find(_data, 0, _data.length, _value); + } + + function find( + uint256[] storage _data, + uint256 _begin, + uint256 _len, + uint256 _value + ) private returns (uint256 o_position) { + if (_len == 0 || (_len == 1 && _data[_begin] != _value)) + return uint256(-1); // failure + uint256 halfLen = _len / 2; + uint256 v = _data[_begin + halfLen]; + if (_value < v) return find(_data, _begin, halfLen, _value); + else if (_value > v) + return find(_data, _begin + halfLen + 1, halfLen - 1, _value); + else return _begin + halfLen; + } +} + + +contract Store is BinarySearch { + uint256[] data; + + function add(uint256 v) public { + data.push(0); + data[data.length - 1] = v; + } + + function find(uint256 v) public returns (uint256) { + return find(data, v); + } +} + +// ==== +// compileViaYul: also +// ---- +// find(uint256): 7 -> -1 +// add(uint256): 7 -> +// find(uint256): 7 -> 0 +// add(uint256): 11 -> +// add(uint256): 17 -> +// add(uint256): 27 -> +// add(uint256): 31 -> +// add(uint256): 32 -> +// add(uint256): 66 -> +// add(uint256): 177 -> +// find(uint256): 7 -> 0 +// find(uint256): 27 -> 3 +// find(uint256): 32 -> 5 +// find(uint256): 176 -> -1 +// find(uint256): 0 -> -1 +// find(uint256): 400 -> -1 diff --git a/test/libsolidity/semanticTests/array/string_allocation_bug.sol b/test/libsolidity/semanticTests/array/string_allocation_bug.sol new file mode 100644 index 000000000000..9c2ec2e5352a --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_allocation_bug.sol @@ -0,0 +1,20 @@ +contract Sample { + struct s { + uint16 x; + uint16 y; + string a; + string b; + } + s[2] public p; + + constructor() public { + s memory m; + m.x = 0xbbbb; + m.y = 0xcccc; + m.a = "hello"; + m.b = "world"; + p[0] = m; + } +} +// ---- +// p(uint256): 0x0 -> 0xbbbb, 0xcccc, 0x80, 0xc0, 0x05, "hello", 0x05, "world" diff --git a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol new file mode 100644 index 000000000000..9578fc4f758a --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol @@ -0,0 +1,17 @@ +contract Test { + string s; + bytes b; + + function f(string memory _s, uint256 n) public returns (bytes1) { + b = bytes(_s); + s = string(b); + return bytes(s)[n]; + } + + function l() public returns (uint256) { + return bytes(s).length; + } +} +// ---- +// f(string,uint256): 0x40, 0x02, 0x06, "abcdef" -> "c" +// l() -> 0x06 diff --git a/test/libsolidity/semanticTests/array/strings_in_struct.sol b/test/libsolidity/semanticTests/array/strings_in_struct.sol new file mode 100644 index 000000000000..a6ec607f442e --- /dev/null +++ b/test/libsolidity/semanticTests/array/strings_in_struct.sol @@ -0,0 +1,35 @@ +contract buggystruct { + Buggy public bug; + + struct Buggy { + uint256 first; + uint256 second; + uint256 third; + string last; + } + + constructor() public { + bug = Buggy(10, 20, 30, "asdfghjkl"); + } + + function getFirst() public returns (uint256) { + return bug.first; + } + + function getSecond() public returns (uint256) { + return bug.second; + } + + function getThird() public returns (uint256) { + return bug.third; + } + + function getLast() public returns (string memory) { + return bug.last; + } +} +// ---- +// getFirst() -> 0x0a +// getSecond() -> 0x14 +// getThird() -> 0x1e +// getLast() -> 0x20, 0x09, "asdfghjkl" diff --git a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol new file mode 100644 index 000000000000..2fc479d0e10a --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol @@ -0,0 +1,9 @@ +contract C { + bytes32 constant x = keccak256("abc"); + + function f() public returns (bytes32) { + return x; + } +} +// ---- +// f() -> 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 diff --git a/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol new file mode 100644 index 000000000000..2e771032bc7e --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol @@ -0,0 +1,26 @@ +contract C { + uint256 public x; + + function f() public pure returns (bytes4) { + return this.f.selector; + } + + function g() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function h() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function i() public pure returns (bytes4) { + return this.x.selector; + } +} +// ---- +// f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// g() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// h() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// i() -> 0x0c55699c00000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol new file mode 100644 index 000000000000..91a26945cd59 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function foo() public returns (bytes32) { + data.push("x"); + data.push("y"); + data.push("z"); + return keccak256(abi.encodePacked("b", keccak256(data), "a")); + } +} +// ---- +// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol new file mode 100644 index 000000000000..1374538c2c04 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (bytes32) { + return keccak256(""); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol new file mode 100644 index 000000000000..972aee839e0e --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint256 b, uint256 c) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, c)); + } +} +// ---- +// foo(uint256,uint256,uint256): 0xa, 0xc, 0xd -> 0xbc740a98aae5923e8f04c9aa798c9ee82f69e319997699f2782c40828db9fd81 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol new file mode 100644 index 000000000000..01397f55f85e --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145))); + } +} +// ---- +// foo(uint256,uint16): 0xa, 0xc -> 0x88acd45f75907e7c560318bc1a5249850a0999c4896717b1167d05d116e6dbad diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol new file mode 100644 index 000000000000..b157178fb857 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol @@ -0,0 +1,12 @@ +contract c { + function foo() public returns (bytes32 d) { + d = keccak256("foo"); + } + + function bar(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); + } +} +// ---- +// foo() -> 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d +// bar(uint256,uint16): 0xa, 0xc -> 0x6990f36476dc412b1c4baa48e2d9f4aa4bb313f61fda367c8fdbbb2232dc6146 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol new file mode 100644 index 000000000000..725d984d86f8 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol @@ -0,0 +1,13 @@ +contract c { + bytes data; + + function foo() public returns (bool) { + data.push("f"); + data.push("o"); + data.push("o"); + return keccak256(data) == keccak256("foo"); + } +} + +// ---- +// foo() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol new file mode 100644 index 000000000000..11b9a5339e79 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol @@ -0,0 +1,9 @@ +contract test { + function foo(uint256 a) public returns (bytes4 value) { + return msg.sig; + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol new file mode 100644 index 000000000000..646f8f6e91a6 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol @@ -0,0 +1,13 @@ +contract test { + function boo() public returns (bytes4 value) { + return msg.sig; + } + + function foo(uint256 a) public returns (bytes4 value) { + return boo(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol new file mode 100644 index 000000000000..c79625d3cc5b --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (bytes20) { + return ripemd160(""); + } +} + +// ---- +// f() -> 0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol new file mode 100644 index 000000000000..69b9e15f535c --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (bytes32) { + return sha256(""); + } +} + +// ---- +// f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol new file mode 100644 index 000000000000..c2c2fa70a678 --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol @@ -0,0 +1,74 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f1(bytes[1] calldata a) + external + returns (uint256, uint256, uint256, uint256) + { + return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); + } + + function f2(bytes[1] calldata a, bytes[1] calldata b) + external + returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + b[0].length, + uint8(b[0][0]), + uint8(b[0][1]) + ); + } + + function g1(bytes[2] calldata a) + external + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ); + } + + function g2(bytes[] calldata a) external returns (uint256[8] memory) { + return [ + a.length, + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ]; + } +} + +// found expectation comments: +// same offset for both arrays @ ABI_CHECK( + +// ---- +// f1(bytes[1]): 0x20, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3 +// f2(bytes[1],bytes[1]): 0x40, 0xa0, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x20, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x2, 0x1, 0x2 +// g1(bytes[2]): 0x20, 0x40, 0x80, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x4, 0x5, 0x6 +// g1(bytes[2]): 0x20, 0x40, 0x40, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x1, 0x2, 0x3 +// g2(bytes[]): 0x20, 0x2, 0x40, 0x80, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x2, 0x2, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol new file mode 100644 index 000000000000..04b2ed15d541 --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bytes[] calldata a) + external + returns (uint256, uint256, bytes memory) + { + bytes memory m = a[0]; + return (a.length, m.length, m); + } +} +// ---- +// f(bytes[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 0x1, 0x2, 0x60, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" -> 0x1, 0x20, 0x60, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" -> 0x1, 0x20, 0x60, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" -> 0x1, 0x20, 0x60, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" -> 0x1, 0x20, 0x60, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" diff --git a/test/libsolidity/semanticTests/calldata/calldata_string_array.sol b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol new file mode 100644 index 000000000000..ae7d1e2ef804 --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(string[] calldata a) + external + returns (uint256, uint256, uint256, string memory) + { + string memory s1 = a[0]; + bytes memory m1 = bytes(s1); + return (a.length, m1.length, uint8(m1[0]), s1); + } +} +// ---- +// f(string[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 1, 2, 97, 0x80, 2, "ab" diff --git a/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol new file mode 100644 index 000000000000..dbf6e44405e2 --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint8 a; + bytes1 b; + } + + function f(S calldata s) external pure returns (uint256 a, bytes32 b) { + uint8 tmp1 = s.a; + bytes1 tmp2 = s.b; + assembly { + a := tmp1 + b := tmp2 + } + } +} +// ---- +// f((uint8,bytes1)): 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" -> 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" # double check that the valid case goes through # +// f((uint8,bytes1)): 0x1234, hex"5678000000000000000000000000000000000000000000000000000000000000" -> FAILURE +// f((uint8,bytes1)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion.sol new file mode 100644 index 000000000000..20f594764339 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion.sol @@ -0,0 +1,23 @@ +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> 0x1 +// f(bool): 0x3 -> 0x1 +// f(bool): 0xff -> 0x1 +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> 0x1 +// g(bool): 0x3 -> 0x1 +// g(bool): 0xff -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol new file mode 100644 index 000000000000..a8fd898333c4 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> FAILURE +// f(bool): 0x3 -> FAILURE +// f(bool): 0xff -> FAILURE +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> FAILURE +// g(bool): 0x3 -> FAILURE +// g(bool): 0xff -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol new file mode 100644 index 000000000000..c94cfc776c7d --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol @@ -0,0 +1,17 @@ +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol new file mode 100644 index 000000000000..074b3ff6b49b --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol @@ -0,0 +1,33 @@ +contract C { + function f() public pure returns (address r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { + y := x + } + address z = address(y); + assembly { + r := z + } + require(z == 0x1122334455667788990011223344556677889900); + } + + function g() public pure returns (address payable r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { + y := x + } + address payable z = address(y); + assembly { + r := z + } + require(z == 0x1122334455667788990011223344556677889900); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1122334455667788990011223344556677889900 +// g() -> 0x1122334455667788990011223344556677889900 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol new file mode 100644 index 000000000000..beff156f757a --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol new file mode 100644 index 000000000000..32b90748e805 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol @@ -0,0 +1,13 @@ +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> 0x0 # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol new file mode 100644 index 000000000000..dc7c7f8905ab --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol @@ -0,0 +1,15 @@ +contract C { + function f() public pure returns (bytes32 r) { + bytes4 x = 0xffffffff; + bytes2 y = bytes2(x); + assembly { + r := y + } + // At this point, r and y both store four bytes, but + // y is properly cleaned before the equality check + require(y == bytes2(0xffff)); + } +} + +// ---- +// f() -> "\xff\xff\xff\xff" diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol new file mode 100644 index 000000000000..5adc97378754 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> FAILURE # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol new file mode 100644 index 000000000000..c1901c73839d --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol @@ -0,0 +1,13 @@ +contract C { + function test() public returns (uint256, uint256) { + uint32 a = 0xffffffff; + uint16 x = uint16(a); + uint16 y = x; + x /= 0x100; + y = y / 0x100; + return (x, y); + } +} + +// ---- +// test() -> 0xff, 0xff diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol new file mode 100644 index 000000000000..a81e2755ef8e --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (uint8 x) { + uint8 y = uint8(2)**uint8(8); + return 0**y; + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol new file mode 100644 index 000000000000..a9b5e81b54fb --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint8 x) { + return uint8(0)**uint8(uint8(2)**uint8(8)); + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol new file mode 100644 index 000000000000..4f817d18622a --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint8 x) { + return uint8(0x166)**uint8(uint8(2)**uint8(8)); + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/constants/constant_string.sol b/test/libsolidity/semanticTests/constants/constant_string.sol new file mode 100644 index 000000000000..56cbf982d120 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constant_string.sol @@ -0,0 +1,22 @@ +contract C { + bytes constant a = "\x03\x01\x02"; + bytes constant b = hex"030102"; + string constant c = "hello"; + + function f() public returns (bytes memory) { + return a; + } + + function g() public returns (bytes memory) { + return b; + } + + function h() public returns (bytes memory) { + return bytes(c); + } +} + +// ---- +// f() -> 0x20, 3, "\x03\x01\x02" +// g() -> 0x20, 3, "\x03\x01\x02" +// h() -> 0x20, 5, "hello" diff --git a/test/libsolidity/semanticTests/constants/constant_variables.sol b/test/libsolidity/semanticTests/constants/constant_variables.sol new file mode 100644 index 000000000000..98b4773c79ba --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constant_variables.sol @@ -0,0 +1,11 @@ +contract Foo { + uint256 constant x = 56; + enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} + ActionChoices constant choices = ActionChoices.GoLeft; + bytes32 constant st = "abc\x00\xff__"; +} + +// ==== +// compileViaYul: also +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol new file mode 100644 index 000000000000..c05060b7e391 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol @@ -0,0 +1,10 @@ +contract Foo { + function getX() public returns (uint256 r) { + return x; + } + + uint256 constant x = 56; +} + +// ---- +// getX() -> 56 diff --git a/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol new file mode 100644 index 000000000000..2384c061f108 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol @@ -0,0 +1,24 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } +} + + +contract Base is BaseBase(7) { + constructor() public { + m_a *= m_a; + } +} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } +} + +// ---- +// getA() -> 49 diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol new file mode 100644 index 000000000000..ec30bacf6c16 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -0,0 +1,22 @@ +contract Main { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + +// ---- +// constructor(): "abc", true +// getFlag() -> true +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol new file mode 100644 index 000000000000..756478e342d0 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol @@ -0,0 +1,38 @@ +contract Helper { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + + +contract Main { + Helper h; + + constructor() public { + h = new Helper("abc", true); + } + + function getFlag() public returns (bool ret) { + return h.getFlag(); + } + + function getName() public returns (bytes3 ret) { + return h.getName(); + } +} + +// ---- +// getFlag() -> true +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol new file mode 100644 index 000000000000..10243f8979b2 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -0,0 +1,16 @@ +contract C { + uint256 public a; + uint256[3] public b; + + constructor(uint256 _a, uint256[3] memory _b) public { + a = _a; + b = _b; + } +} + +// ---- +// constructor(): 1, 2, 3, 4 -> +// a() -> 1 +// b(uint256): 0 -> 2 +// b(uint256): 1 -> 3 +// b(uint256): 2 -> 4 diff --git a/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol new file mode 100644 index 000000000000..2192dabf32bc --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol @@ -0,0 +1,19 @@ +contract A { + constructor() public { + address(this).call("123"); + } +} + + +contract B { + uint256 public test = 1; + + function testIt() public { + A a = new A(); + ++test; + } +} + +// ---- +// testIt() -> +// test() -> 2 diff --git a/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol new file mode 100644 index 000000000000..e92a37b19583 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol @@ -0,0 +1,24 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } + + function g() public returns (uint256 r) { + return 2; + } +} + + +contract Base is BaseBase(BaseBase.g()) {} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } +} + +// ---- +// getA() -> 2 diff --git a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol new file mode 100644 index 000000000000..42db8582aed1 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol @@ -0,0 +1,21 @@ +contract Test { + bytes3 name; + bool flag; + + constructor() public { + setName("abc"); + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function setName(bytes3 _name) private { + name = _name; + } +} + +// ==== +// compileViaYul: also +// ---- +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol new file mode 100644 index 000000000000..0aea44e6a493 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol @@ -0,0 +1,20 @@ +contract Base { + uint256 m_base = 5; + + function getBMember() public returns (uint256 i) { + return m_base; + } +} + + +contract Derived is Base { + uint256 m_derived = 6; + + function getDMember() public returns (uint256 i) { + return m_derived; + } +} + +// ---- +// getBMember() -> 5 +// getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/constructor/payable_constructor.sol b/test/libsolidity/semanticTests/constructor/payable_constructor.sol new file mode 100644 index 000000000000..9a3c56ebee66 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/payable_constructor.sol @@ -0,0 +1,8 @@ +contract C { + constructor() public payable {} +} + +// ==== +// compileViaYul: also +// ---- +// constructor(), 27 wei -> diff --git a/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol new file mode 100644 index 000000000000..ab8ad47f9be4 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol @@ -0,0 +1,21 @@ +contract C { + uint256 public result_in_constructor; + function(uint256) returns (uint256) internal x; + + constructor() public { + x = double; + result_in_constructor = use(2); + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } + + function use(uint256 _arg) public returns (uint256) { + return x(_arg); + } +} + +// ---- +// use(uint256): 3 -> 6 +// result_in_constructor() -> 4 diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol new file mode 100644 index 000000000000..7492a42811d7 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol @@ -0,0 +1,18 @@ +contract C { + function() returns (uint256) internal x; + + constructor() public { + x = unused; + } + + function unused() internal returns (uint256) { + return 7; + } + + function t() public returns (uint256) { + return x(); + } +} + +// ---- +// t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol new file mode 100644 index 000000000000..0a4295114f4d --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol @@ -0,0 +1,21 @@ +library L { + function x() internal returns (uint256) { + return 7; + } +} + + +contract C { + function() returns (uint256) internal x; + + constructor() public { + x = L.x; + } + + function t() public returns (uint256) { + return x(); + } +} + +// ---- +// t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol new file mode 100644 index 000000000000..19f0bdb574c4 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -0,0 +1,15 @@ +contract C { + uint public i; + uint public k; + + constructor(uint newI, uint newK) public { + i = newI; + k = newK; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor(): 2, 0 -> +// i() -> 2 +// k() -> 0 diff --git a/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol new file mode 100644 index 000000000000..9ff75fad6064 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol @@ -0,0 +1,12 @@ +contract c { + enum Truth {False, True} + + function test() public returns (uint256) { + return uint256(Truth(uint8(0x701))); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol new file mode 100644 index 000000000000..fd4c96ce7658 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol @@ -0,0 +1,31 @@ +contract test { + enum ActionChoices {GoLeft, GoRight, GoStraight} + + constructor() public {} + + function getChoiceExp(uint256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromSigned(int256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromNegativeLiteral() public returns (uint256 d) { + choice = ActionChoices(-1); + d = uint256(choice); + } + + ActionChoices choice; +} + +// ==== +// compileViaYul: also +// ---- +// getChoiceExp(uint256): 3 -> FAILURE # These should throw # +// getChoiceFromSigned(int256): -1 -> FAILURE +// getChoiceFromNegativeLiteral() -> FAILURE +// getChoiceExp(uint256): 2 -> 2 # These should work # +// getChoiceExp(uint256): 0 -> 0 diff --git a/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol new file mode 100644 index 000000000000..5d60a746650e --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol @@ -0,0 +1,10 @@ +contract test { + enum Choice {A, B, C} + + function answer() public returns (test.Choice _ret) { + _ret = test.Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_enums.sol b/test/libsolidity/semanticTests/enums/using_enums.sol new file mode 100644 index 000000000000..ae497f40997f --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_enums.sol @@ -0,0 +1,16 @@ +contract test { + enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} + + constructor() public { + choices = ActionChoices.GoStraight; + } + + function getChoice() public returns (uint256 d) { + d = uint256(choices); + } + + ActionChoices choices; +} + +// ---- +// getChoice() -> 2 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol new file mode 100644 index 000000000000..c39bfe064a1e --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol @@ -0,0 +1,13 @@ +contract base { + enum Choice {A, B, C} +} + + +contract test is base { + function answer() public returns (Choice _ret) { + _ret = Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol new file mode 100644 index 000000000000..0be3f80d4810 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol @@ -0,0 +1,13 @@ +contract base { + enum Choice {A, B, C} +} + + +contract test is base { + function answer() public returns (base.Choice _ret) { + _ret = base.Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol new file mode 100644 index 000000000000..5d93ba0ee7dd --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol @@ -0,0 +1,28 @@ +contract test { + function f0() public returns (uint) { + return 2; + } + + function f1() internal returns (function() internal returns (uint)) { + return f0; + } + + function f2() internal returns (function() internal returns (function () internal returns (uint))) { + return f1; + } + + function f3() internal returns (function() internal returns (function () internal returns (function () internal returns (uint)))) { + return f2; + } + + function f() public returns (uint) { + function() internal returns(function() internal returns(function() internal returns(function() internal returns(uint)))) x; + x = f3; + return x()()()(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol new file mode 100644 index 000000000000..2ccfc064c81d --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol @@ -0,0 +1,28 @@ +abstract contract D { + function g() public virtual; +} + + +contract C { + D d = D(0x1212); + + function f() public returns (uint256) { + d.g(); + return 7; + } + + function g() public returns (uint256) { + d.g.gas(200)(); + return 7; + } + + function h() public returns (uint256) { + address(d).call(""); // this does not throw (low-level) + return 7; + } +} + +// ---- +// f() -> FAILURE +// g() -> FAILURE +// h() -> 7 diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol new file mode 100644 index 000000000000..e1cab8fe8af4 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol @@ -0,0 +1,17 @@ +contract C { + function intern() public returns (uint256) { + function (uint) internal returns (uint) x; + x(2); + return 7; + } + + function extern() public returns (uint256) { + function (uint) external returns (uint) x; + x(2); + return 7; + } +} + +// ---- +// intern() -> FAILURE # This should throw exceptions # +// extern() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol new file mode 100644 index 000000000000..0ae431b24b83 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol @@ -0,0 +1,20 @@ +contract C { + function() returns (uint256) internal x; + int256 mutex; + + function t() public returns (uint256) { + if (mutex > 0) { + assembly { + mstore(0, 7) + return(0, 0x20) + } + } + mutex = 1; + // Avoid re-executing this function if we jump somewhere. + x(); + return 2; + } +} + +// ---- +// t() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol new file mode 100644 index 000000000000..26a2a79f4c5c --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol @@ -0,0 +1,20 @@ +contract C { + int256 mutex; + + function t() public returns (uint256) { + if (mutex > 0) { + assembly { + mstore(0, 7) + return(0, 0x20) + } + } + mutex = 1; + // Avoid re-executing this function if we jump somewhere. + function() internal returns (uint)[200] memory x; + x[0](); + return 2; + } +} + +// ---- +// t() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol new file mode 100644 index 000000000000..ffa22b8a27ea --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol @@ -0,0 +1,37 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("get()")); + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("assert0()")); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// get() -> 0x00 +// assert0_delegated() -> 0x01, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x0 +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x1 +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x2a diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol new file mode 100644 index 000000000000..498449d17d1b --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); + return success; + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); + return success; + } +} +// ==== +// EVMVersion: 0x00 +// assert0_delegated() -> true +// get_delegated() -> true +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> false +// get_delegated() -> true +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> false +// get_delegated() -> true diff --git a/test/libsolidity/semanticTests/functionCall/external_call_value.sol b/test/libsolidity/semanticTests/functionCall/external_call_value.sol new file mode 100644 index 000000000000..47e2bfb764f3 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_call_value.sol @@ -0,0 +1,17 @@ +pragma solidity >= 0.6.0; + +contract C { + function g(uint n) external payable returns (uint, uint) { + return (msg.value * 1000, n); + } + + function f(uint n) public payable returns (uint, uint) { + return this.g{value: 10}(n); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(uint256), 1 ether: 4 -> 1000000000000000000000, 4 +// f(uint256), 11 ether: 2 -> 10000, 2 diff --git a/test/libsolidity/semanticTests/functionCall/external_function.sol b/test/libsolidity/semanticTests/functionCall/external_function.sol new file mode 100644 index 000000000000..3625864477f0 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_function.sol @@ -0,0 +1,18 @@ +contract c { + function f(uint256 a) public returns (uint256) { + return a; + } + + function test(uint256 a, uint256 b) + external + returns (uint256 r_a, uint256 r_b) + { + r_a = f(a + 7); + r_b = b; + } +} + +// ==== +// compileViaYul: also +// ---- +// test(uint256,uint256): 2, 3 -> 9, 3 diff --git a/test/libsolidity/semanticTests/functionCall/external_public_override.sol b/test/libsolidity/semanticTests/functionCall/external_public_override.sol new file mode 100644 index 000000000000..7909f8e324a3 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_public_override.sol @@ -0,0 +1,22 @@ +contract A { + function f() external virtual returns (uint256) { + return 1; + } +} + + +contract B is A { + function f() public override returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return f(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 2 +// g() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol new file mode 100644 index 000000000000..aaafb6f28293 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -0,0 +1,44 @@ +contract helper { + bool flag; + + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } + + function setFlag() public { + flag = true; + } + + function getFlag() public returns (bool fl) { + return flag; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public payable returns (uint256 bal) { + return h.getBalance.value(amount)(); + } + + function outOfGas() public returns (bool ret) { + h.setFlag.gas(2)(); // should fail due to OOG + return true; + } + + function checkState() public returns (bool flagAfter, uint256 myBal) { + flagAfter = h.getFlag(); + myBal = address(this).balance; + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 5 +// outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # +// checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol new file mode 100644 index 000000000000..dbd524deb744 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -0,0 +1,43 @@ +contract helper { + bool flag; + + function getBalance() payable public returns(uint256 myBalance) { + return address(this).balance; + } + + function setFlag() public { + flag = true; + } + + function getFlag() public returns(bool fl) { + return flag; + } +} +contract test { + helper h; + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint amount) public payable returns(uint256 bal) { + return h.getBalance{value: amount}(); + } + + function outOfGas() public returns(bool ret) { + h.setFlag { + gas: 2 + }(); // should fail due to OOG + return true; + } + + function checkState() public returns(bool flagAfter, uint myBal) { + flagAfter = h.getFlag(); + myBal = address(this).balance; + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 5 +// outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # +// checkState() -> false, 15 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol new file mode 100644 index 000000000000..e0c6ff5e03aa --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -0,0 +1,19 @@ +// Sending zero ether to a contract should still invoke the receive ether function +// (it previously did not because the gas stipend was not provided by the EVM) +contract Receiver { + receive() external payable {} +} + + +contract Main { + constructor() public payable {} + + function s() public returns (bool) { + Receiver r = new Receiver(); + return address(r).send(0); + } +} + +// ---- +// constructor(), 20 wei -> +// s() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol new file mode 100644 index 000000000000..96a66fce2969 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol @@ -0,0 +1,16 @@ +contract C { + function a() public returns (uint256) { + return 7; + } + + function test() public returns (uint256) { + function() returns (uint256) y = a; + delete y; + y(); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol new file mode 100644 index 000000000000..6c7ecf4f693c --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol @@ -0,0 +1,27 @@ +contract C { + function a() public returns (uint256) { + return 7; + } + + function() returns (uint256) internal y; + + function set() public returns (uint256) { + y = a; + return y(); + } + + function d() public returns (uint256) { + delete y; + return 1; + } + + function ca() public returns (uint256) { + return y(); + } +} + +// ---- +// set() -> 7 +// ca() -> 7 +// d() -> 1 +// ca() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol b/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol new file mode 100644 index 000000000000..f096a4979444 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol @@ -0,0 +1,26 @@ +library Utils { + function reduce( + uint256[] memory array, + function(uint, uint) internal returns (uint) f, + uint256 init + ) internal returns (uint256) { + for (uint256 i = 0; i < array.length; i++) { + init = f(array[i], init); + } + return init; + } + + function sum(uint256 a, uint256 b) internal returns (uint256) { + return a + b; + } +} + + +contract C { + function f(uint256[] memory x) public returns (uint256) { + return Utils.reduce(x, Utils.sum, 0); + } +} + +// ---- +// f(uint256[]): 0x20, 0x3, 0x1, 0x7, 0x3 -> 11 diff --git a/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol new file mode 100644 index 000000000000..ac802ee9e5da --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol @@ -0,0 +1,34 @@ +contract Flow { + bool public success; + + mapping(address => function() internal) stages; + + function stage0() internal { + stages[msg.sender] = stage1; + } + + function stage1() internal { + stages[msg.sender] = stage2; + } + + function stage2() internal { + success = true; + } + + constructor() public { + stages[msg.sender] = stage0; + } + + function f() public returns (uint256) { + stages[msg.sender](); + return 7; + } +} + +// ---- +// success() -> false +// f() -> 7 +// f() -> 7 +// success() -> false +// f() -> 7 +// success() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol new file mode 100644 index 000000000000..ebef9a2549a8 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol @@ -0,0 +1,21 @@ +contract C { + function f(uint256 x) public returns (uint256) { + return this.eval(this.g, x); + } + + function f2(uint256 x) public returns (uint256) { + return eval(this.g, x); + } + + function eval(function(uint) external returns (uint) x, uint a) public returns (uint) { + return x(a); + } + + function g(uint256 x) public returns (uint256) { + return x + 1; + } +} + +// ---- +// f(uint256): 7 -> 8 +// f2(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol new file mode 100644 index 000000000000..6fb4f5f6e439 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint256 x) public returns (uint256) { + return eval(g, x); + } + + function eval(function(uint) internal returns (uint) x, uint a) internal returns (uint) { + return x(a); + } + + function g(uint256 x) public returns (uint256) { + return x + 1; + } +} + +// ---- +// f(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol new file mode 100644 index 000000000000..a79cb2d76ff3 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol @@ -0,0 +1,19 @@ +contract C { + uint256 public initial; + + constructor() public { + initial = double(2); + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } + + function runtime(uint256 _arg) public returns (uint256) { + return double(_arg); + } +} + +// ---- +// runtime(uint256): 3 -> 6 +// initial() -> 4 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol new file mode 100644 index 000000000000..f8230b21aff4 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol @@ -0,0 +1,18 @@ +contract C { + function(uint256) returns (uint256) internal x; + + constructor() public { + x = double; + } + + function test() public returns (bool) { + return x == double; + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol new file mode 100644 index 000000000000..a09404fce004 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -0,0 +1,28 @@ +contract Other { + function addTwo(uint256 x) public returns (uint256) { + return x + 2; + } +} + + +contract C { + function (function (uint) external returns (uint)) internal returns (uint) ev; + function (uint) external returns (uint) x; + + function store(function(uint) external returns (uint) y) public { + x = y; + } + + function eval(function(uint) external returns (uint) y) public returns (uint) { + return y(7); + } + + function t() public returns (uint256) { + ev = eval; + this.store((new Other()).addTwo); + return ev(x); + } +} + +// ---- +// t() -> 9 diff --git a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol new file mode 100644 index 000000000000..ca1055fd6e2b --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol @@ -0,0 +1,11 @@ +contract Test { + function() internal x; + + function f() public returns (uint256 r) { + x(); + return 2; + } +} + +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol new file mode 100644 index 000000000000..3f71a31e0fe0 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol @@ -0,0 +1,8 @@ +contract A { + uint8 immutable a = 2; + function f() public view returns (uint) { + return a; + } +} +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/immutable/getter.sol b/test/libsolidity/semanticTests/immutable/getter.sol new file mode 100644 index 000000000000..bb4b191cf9f6 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable public x = 1; +} +// ---- +// x() -> 1 diff --git a/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol new file mode 100644 index 000000000000..e4aa474f2b05 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol @@ -0,0 +1,17 @@ +contract A { + uint immutable public x = 1; + uint public y; + constructor() public { + y = this.x(); + } +} +contract C { + function f() public returns (bool) { + try new A() { return false; } + catch { return true; } + } +} +// ==== +// EVMVersion: >=tangerineWhistle +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/immutable/inheritance.sol b/test/libsolidity/semanticTests/immutable/inheritance.sol new file mode 100644 index 000000000000..f61009f60040 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/inheritance.sol @@ -0,0 +1,30 @@ +contract A { + uint8 immutable a; + constructor() public { + a = 4; + } +} +contract B is A { + uint8 immutable b; + constructor() public { + b = 3; + } +} +contract C is A { + uint8 immutable c; + constructor() public { + c = 2; + } +} +contract D is B, C { + uint8 immutable d; + + constructor() public { + d = 1; + } + function f() public view returns (uint256, uint256, uint, uint) { + return (a, b, c, d); + } +} +// ---- +// f() -> 4, 3, 2, 1 diff --git a/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol new file mode 100644 index 000000000000..0673aafb5672 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol @@ -0,0 +1,15 @@ +contract C { + function() internal view returns(uint256) immutable z; + constructor() public { + z = f; + } + function f() public view returns (uint256) { + return 7; + } + function callZ() public view returns (uint) { + return z(); + } +} +// ---- +// f() -> 7 +// callZ() -> 7 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol new file mode 100644 index 000000000000..b9e362dbd5ce --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -0,0 +1,31 @@ +contract A { + uint immutable a; + constructor() public { + a = 7; + } + function f() public view returns (uint) { return a; } +} +contract B { + uint immutable a; + constructor() public { + a = 5; + } + function f() public view returns (uint) { return a; } +} +contract C { + uint immutable a; + uint public x; + uint public y; + constructor() public { + a = 3; + x = (new A()).f(); + y = (new B()).f(); + } + function f() public returns (uint256, uint, uint) { + return (a, (new A()).f(), (new B()).f()); + } +} +// ---- +// f() -> 3, 7, 5 +// x() -> 7 +// y() -> 5 diff --git a/test/libsolidity/semanticTests/immutable/stub.sol b/test/libsolidity/semanticTests/immutable/stub.sol new file mode 100644 index 000000000000..387541066e56 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/stub.sol @@ -0,0 +1,13 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + constructor() public { + x = 42; + y = 23; + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// f() -> 84, 23 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol new file mode 100644 index 000000000000..d83da476d423 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -0,0 +1,19 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + mapping(uint => uint) public m; + constructor(uint _a) public { + x = 42; + y = 23; + m[_a] = 7; + new uint[](4); + + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// constructor(): 3 -> +// f() -> 84, 23 +// m(uint256): 3 -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol new file mode 100644 index 000000000000..3d31c1addfa3 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -0,0 +1,27 @@ +contract C { + function f() public { + assembly { + let d:= 0x10 + + function asmfun(a, b, c) - > x, y, z { + x := g(a) + function g(r) - > s { + s := mul(r, r) + } + y := g(b) + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return (0, 0x80) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1, 0x4, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol new file mode 100644 index 000000000000..451ecbbe50b8 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol @@ -0,0 +1,26 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + function fac(n) -> nf { + nf := 1 + for { + let i := n + } gt(i, 0) { + i := sub(i, 1) + } { + nf := mul(nf, i) + } + } + b := fac(a) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 1 +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 6 +// f(uint256): 4 -> 24 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol new file mode 100644 index 000000000000..2c05f1556086 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol @@ -0,0 +1,29 @@ +contract C { + uint256 st; + + function f(uint256 a) public returns (uint256 b, uint256 c, uint256 d) { + st = 0; + assembly { + function sideeffect(r) -> x { + sstore(0, add(sload(0), r)) + x := 1 + } + for { + let i := a + } eq(i, sideeffect(2)) { + d := add(d, 3) + } { + b := i + i := 0 + } + } + c = st; + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 0, 2, 0 +// f(uint256): 1 -> 1, 4, 3 +// f(uint256): 2 -> 0, 2, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol new file mode 100644 index 000000000000..17a271d5b333 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol @@ -0,0 +1,21 @@ +contract C { + function f() public { + assembly { + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return (0, 0x60) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol new file mode 100644 index 000000000000..26d3d43b7def --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol @@ -0,0 +1,24 @@ +contract C { + function f() public { + assembly { + let d := 0x10 + + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return (0, 0x80) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1, 0x2, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol new file mode 100644 index 000000000000..2dec9761bbfb --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol @@ -0,0 +1,23 @@ +contract C { + function f() public { + assembly { + let a1, b1, c1 + + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return (0, 0x60) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol new file mode 100644 index 000000000000..9f94f3c1991b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol @@ -0,0 +1,17 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + if gt(a, 1) { + b := 2 + } + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 0 +// f(uint256): 1 -> 0 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 2 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol new file mode 100644 index 000000000000..3c9f3a2f371f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol @@ -0,0 +1,19 @@ +contract C { + modifier m { + uint256 a = 1; + assembly { + a := 2 + } + if (a != 2) revert(); + _; + } + + function f() public m returns (bool) { + return true; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol new file mode 100644 index 000000000000..b43b5f611a02 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol @@ -0,0 +1,13 @@ +contract C { + function test() public returns (bytes memory) { + bytes memory x = new bytes(5); + for (uint256 i = 0; i < x.length; ++i) x[i] = bytes1(uint8(i + 1)); + assembly { + mstore(add(x, 32), "12345678901234567890123456789012") + } + return x; + } +} + +// ---- +// test() -> 0x20, 0x5, "12345" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol new file mode 100644 index 000000000000..c48d749676cb --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint256 r) { + for (uint256 x = 0; x < 10; ++x) + assembly { + r := add(r, x) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 45 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol new file mode 100644 index 000000000000..a5e3c4675661 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol @@ -0,0 +1,28 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + function fac(n) -> nf { + switch n + case 0 { + nf := 1 + } + case 1 { + nf := 1 + } + default { + nf := mul(n, fac(sub(n, 1))) + } + } + b := fac(a) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 1 +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 6 +// f(uint256): 4 -> 24 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol new file mode 100644 index 000000000000..823afe2bdac6 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol @@ -0,0 +1,22 @@ +contract C { + uint16 x; + uint16 public y; + uint256 public z; + + function f() public returns (bool) { + uint256 off1; + uint256 off2; + assembly { + sstore(z_slot, 7) + off1 := z_offset + off2 := y_offset + } + assert(off1 == 0); + assert(off2 == 2); + return true; + } +} + +// ---- +// f() -> true +// z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol new file mode 100644 index 000000000000..ce56dbaa1965 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol @@ -0,0 +1,23 @@ +contract C { + uint16 x; + uint16 public y; + uint256 public z; + + function f() public returns (bool) { + uint256 off1; + uint256 off2; + assembly { + function f() -> o1 { + sstore(z_slot, 7) + o1 := y_offset + } + off2 := f() + } + assert(off2 == 2); + return true; + } +} + +// ---- +// f() -> true +// z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol new file mode 100644 index 000000000000..3893c8e89b2f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol @@ -0,0 +1,25 @@ +contract C { + struct Data { + uint256 contents; + } + uint256 public separator; + Data public a; + uint256 public separator2; + + function f() public returns (bool) { + Data storage x = a; + uint256 off; + assembly { + sstore(x_slot, 7) + off := x_offset + } + assert(off == 0); + return true; + } +} + +// ---- +// f() -> true +// a() -> 7 +// separator() -> 0 +// separator2() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol new file mode 100644 index 000000000000..9b9a76109c7a --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol @@ -0,0 +1,24 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + switch a + case 1 { + b := 8 + } + case 2 { + b := 9 + } + default { + b := 2 + } + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 2 +// f(uint256): 1 -> 8 +// f(uint256): 2 -> 9 +// f(uint256): 3 -> 2 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol new file mode 100644 index 000000000000..fa981fbd3352 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint256 r, bytes32 r2) { + assembly { + r := 7 + r2 := "abcdef" + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 7, "abcdef" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol new file mode 100644 index 000000000000..250e0ce8a0c6 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol @@ -0,0 +1,15 @@ +contract C { + function f() public pure returns (uint a, uint b) { + assembly { + let x + let y, z + a := x + b := z + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol b/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol new file mode 100644 index 000000000000..d4034f10bee5 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure returns (bytes32 ret) { + assembly { + ret := keccak256(0, 0) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 diff --git a/test/libsolidity/semanticTests/intheritance/access_base_storage.sol b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol new file mode 100644 index 000000000000..a083ccd985b5 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol @@ -0,0 +1,30 @@ +contract Base { + uint256 dataBase; + + function getViaBase() public returns (uint256 i) { + return dataBase; + } +} + + +contract Derived is Base { + uint256 dataDerived; + + function setData(uint256 base, uint256 derived) public returns (bool r) { + dataBase = base; + dataDerived = derived; + return true; + } + + function getViaDerived() public returns (uint256 base, uint256 derived) { + base = dataBase; + derived = dataDerived; + } +} + +// ==== +// compileViaYul: also +// ---- +// setData(uint256,uint256): 1, 2 -> true +// getViaBase() -> 1 +// getViaDerived() -> 1, 2 diff --git a/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol new file mode 100644 index 000000000000..9b68cc154c4d --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol @@ -0,0 +1,24 @@ +contract C { + function balance() public returns (uint256) { + return 1; + } + + function transfer(uint256 amount) public returns (uint256) { + return amount; + } +} + + +contract D { + function f() public returns (uint256) { + return (new C()).balance(); + } + + function g() public returns (uint256) { + return (new C()).transfer(5); + } +} + +// ---- +// f() -> 1 +// g() -> 5 diff --git a/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol new file mode 100644 index 000000000000..753e9bd1e520 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol @@ -0,0 +1,21 @@ +contract C { + function() returns (uint256) internal x; + + function set() public { + C.x = g; + } + + function g() public pure returns (uint256) { + return 2; + } + + function h() public returns (uint256) { + return C.x(); + } +} + +// ---- +// g() -> 2 +// h() -> FAILURE +// set() -> +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol new file mode 100644 index 000000000000..5628501d9a2d --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol @@ -0,0 +1,21 @@ +contract B { + function f() public returns (uint256) { + return 10; + } +} + + +contract C is B { + function f(uint256 i) public returns (uint256) { + return 2 * i; + } + + function g() public returns (uint256) { + return f(1); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol new file mode 100644 index 000000000000..949024f43e8f --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol @@ -0,0 +1,29 @@ +contract A { + function f(uint256 a) public returns (uint256) { + return 2 * a; + } +} + + +contract B { + function f() public returns (uint256) { + return 10; + } +} + + +contract C is A, B { + function g() public returns (uint256) { + return f(); + } + + function h() public returns (uint256) { + return f(1); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 10 +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol new file mode 100644 index 000000000000..44553b3f1adb --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol @@ -0,0 +1,27 @@ +contract BaseBase { + function g() public virtual returns (uint256 r) { + return 1; + } +} + + +contract Base is BaseBase { + function g() public virtual override returns (uint256 r) { + return 2; + } +} + + +contract Derived is Base { + function f() public returns (uint256 r) { + return BaseBase.g(); + } + + function g() public override returns (uint256 r) { + return 3; + } +} + +// ---- +// g() -> 3 +// f() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol new file mode 100644 index 000000000000..e25db0dcbbc1 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol @@ -0,0 +1,13 @@ +contract A { + uint256 constant x = 7; +} + + +contract B is A { + function f() public returns (uint256) { + return A.x; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function.sol b/test/libsolidity/semanticTests/intheritance/inherited_function.sol new file mode 100644 index 000000000000..23f9bee371ac --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function.sol @@ -0,0 +1,19 @@ +contract A { + function f() internal virtual returns (uint256) { + return 1; + } +} + + +contract B is A { + function f() internal override returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return A.f(); + } +} + +// ---- +// g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol new file mode 100644 index 000000000000..9812ca5208d8 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol @@ -0,0 +1,25 @@ +interface I { + function f(uint256[] calldata a) external returns (uint256); +} + + +contract A is I { + function f(uint256[] calldata a) external override returns (uint256) { + return 42; + } +} + + +contract B { + function f(uint256[] memory a) public returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + I i = I(new A()); + return i.f(new uint256[](2)); + } +} + +// ---- +// g() -> 42 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol new file mode 100644 index 000000000000..0fd02e8f3a9f --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol @@ -0,0 +1,22 @@ +contract A { + function f(uint256[] calldata a) external virtual returns (uint256) { + return a[0]; + } +} + + +contract B is A { + function f(uint256[] memory a) public override returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + uint256[] memory m = new uint256[](2); + m[0] = 42; + m[1] = 23; + return A(this).f(m); + } +} + +// ---- +// g() -> 23 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol new file mode 100644 index 000000000000..9a2c1a1e7d07 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol @@ -0,0 +1,25 @@ +interface I { + function f(uint256[] calldata a) external returns (uint256); +} + + +contract A is I { + function f(uint256[] memory a) public override returns (uint256) { + return 42; + } +} + + +contract B { + function f(uint256[] memory a) public returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + I i = I(new A()); + return i.f(new uint256[](2)); + } +} + +// ---- +// g() -> 42 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol new file mode 100644 index 000000000000..42d4f711c9b9 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol @@ -0,0 +1,19 @@ +library A { + function f() internal returns (uint256) { + return 1; + } +} + + +contract B { + function f() internal returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return A.f(); + } +} + +// ---- +// g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol new file mode 100644 index 000000000000..7f65eb4adb7d --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol @@ -0,0 +1,18 @@ +contract test { + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function g() public returns (uint256 d) { + return f(3); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 3 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol new file mode 100644 index 000000000000..bee0e8fa5723 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol @@ -0,0 +1,18 @@ +contract test { + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function g() public returns (uint256 d) { + return f(3, 7); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol new file mode 100644 index 000000000000..df437a32c8ce --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol @@ -0,0 +1,20 @@ +contract test { + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function g(bool flag) public returns (uint256 d) { + if (flag) return f(3); + else return f(3, 7); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(bool): true -> 3 +// g(bool): false -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol new file mode 100644 index 000000000000..26de9ce7087a --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol @@ -0,0 +1,18 @@ +contract Base { + constructor(uint256 i) public { + m_i = i; + } + + uint256 public m_i; +} + + +contract Derived is Base { + constructor(uint256 i) public Base(i) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol new file mode 100644 index 000000000000..6349bd814ffd --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol @@ -0,0 +1,23 @@ +contract Base { + constructor(uint256 j) public { + m_i = j; + } + + uint256 public m_i; +} + + +contract Base1 is Base { + constructor(uint256 k) public Base(k) {} +} + + +contract Derived is Base, Base1 { + constructor(uint256 i) public Base1(i) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol new file mode 100644 index 000000000000..4556cf5c49eb --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol @@ -0,0 +1,23 @@ +contract Base { + constructor(uint256 i) public { + m_i = i; + } + + uint256 public m_i; +} + + +abstract contract Base1 is Base { + constructor(uint256 k) public {} +} + + +contract Derived is Base, Base1 { + constructor(uint256 i) public Base(i) Base1(7) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol new file mode 100644 index 000000000000..33e270c9ea1b --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol @@ -0,0 +1,35 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 4; + } +} + + +contract D is B, C { + uint256 data; + + constructor() public { + data = super.f() | 8; + } + + function f() public override (B, C) returns (uint256 r) { + return data; + } +} + +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_overload.sol b/test/libsolidity/semanticTests/intheritance/super_overload.sol new file mode 100644 index 000000000000..6a1f6dc3d1d3 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_overload.sol @@ -0,0 +1,27 @@ +contract A { + function f(uint256 a) public returns (uint256) { + return 2 * a; + } +} + + +contract B { + function f(bool b) public returns (uint256) { + return 10; + } +} + + +contract C is A, B { + function g() public returns (uint256) { + return super.f(true); + } + + function h() public returns (uint256) { + return super.f(1); + } +} + +// ---- +// g() -> 10 +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol new file mode 100644 index 000000000000..5cd0bc1772f8 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol @@ -0,0 +1,45 @@ +contract Helper { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public payable { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + + +contract Main { + Helper h; + + constructor() public payable { + h = (new Helper).value(10)("abc", true); + } + + function getFlag() public returns (bool ret) { + return h.getFlag(); + } + + function getName() public returns (bytes3 ret) { + return h.getName(); + } + + function getBalances() public returns (uint256 me, uint256 them) { + me = address(this).balance; + them = address(h).balance; + } +} + +// ---- +// constructor(), 22 wei -> +// getFlag() -> true +// getName() -> "abc" +// getBalances() -> 12, 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function.sol b/test/libsolidity/semanticTests/libraries/internal_library_function.sol new file mode 100644 index 000000000000..a3c8a870018e --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function.sol @@ -0,0 +1,21 @@ +// tests that internal library functions can be called from outside +// and retain the same memory context (i.e. are pulled into the caller's code) +// This has to work without linking, because everything will be inlined. +library L { + function f(uint256[] memory _data) internal { + _data[3] = 2; + } +} + + +contract C { + function f() public returns (uint256) { + uint256[] memory x = new uint256[](7); + x[3] = 8; + L.f(x); + return x[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol new file mode 100644 index 000000000000..f59ec9440dff --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol @@ -0,0 +1,26 @@ +// This has to work without linking, because everything will be inlined. +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal { + _s.data[3] = 2; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + x.f(); + return x.data[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol new file mode 100644 index 000000000000..2283c30ff5fc --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol @@ -0,0 +1,26 @@ +// tests that internal library functions that are called from outside and that +// themselves call private functions are still able to (i.e. the private function +// also has to be pulled into the caller's code) +// This has to work without linking, because everything will be inlined. +library L { + function g(uint256[] memory _data) private { + _data[3] = 2; + } + + function f(uint256[] memory _data) internal { + g(_data); + } +} + + +contract C { + function f() public returns (uint256) { + uint256[] memory x = new uint256[](7); + x[3] = 8; + L.f(x); + return x[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol new file mode 100644 index 000000000000..21417b599b43 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol @@ -0,0 +1,26 @@ +// This has to work without linking, because everything will be inlined. +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal returns (uint256[] memory) { + _s.data[3] = 2; + return _s.data; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + return x.f()[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol new file mode 100644 index 000000000000..f24d93c2d940 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol @@ -0,0 +1,14 @@ +library Arst { + enum Foo {Things, Stuff} +} + + +contract Tsra { + function f() public returns (uint256) { + Arst.Foo; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol new file mode 100644 index 000000000000..d7df52434136 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol @@ -0,0 +1,17 @@ +library Arst { + struct Foo { + int256 Things; + int256 Stuff; + } +} + + +contract Tsra { + function f() public returns (uint256) { + Arst.Foo; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/literals/scientific_notation.sol b/test/libsolidity/semanticTests/literals/scientific_notation.sol new file mode 100644 index 000000000000..e79fca70b80c --- /dev/null +++ b/test/libsolidity/semanticTests/literals/scientific_notation.sol @@ -0,0 +1,36 @@ +contract C { + function f() public returns(uint) { + return 2e10 wei; + } + + function g() public returns(uint) { + return 200e-2 wei; + } + + function h() public returns(uint) { + return 2.5e1; + } + + function i() public returns(int) { + return -2e10; + } + + function j() public returns(int) { + return -200e-2; + } + + function k() public returns(int) { + return -2.5e1; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 20000000000 +// g() -> 2 +// h() -> 25 +// i() -> -20000000000 +// j() -> -2 +// k() -> -25 + diff --git a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol new file mode 100644 index 000000000000..1d1036c2c8d2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + _; + break; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol new file mode 100644 index 000000000000..1db0f2e9ff69 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + if (i % 2 == 1) continue; + _; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 5 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier.sol b/test/libsolidity/semanticTests/modifiers/function_modifier.sol new file mode 100644 index 000000000000..fcb8f64a23f2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier.sol @@ -0,0 +1,13 @@ +contract C { + function getOne() public payable nonFree returns (uint256 r) { + return 1; + } + + modifier nonFree { + if (msg.value > 0) _; + } +} + +// ---- +// getOne() -> 0 +// getOne(), 1 wei -> 1 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol new file mode 100644 index 000000000000..f4a73ebebc8a --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol @@ -0,0 +1,49 @@ +contract A { + uint256 data; + + constructor() public mod1 { + f1(); + } + + function f1() public mod2 { + data |= 0x1; + } + + function f2() public { + data |= 0x20; + } + + function f3() public virtual {} + + modifier mod1 virtual { + f2(); + _; + } + modifier mod2 { + f3(); + if (false) _; + } + + function getData() public returns (uint256 r) { + return data; + } +} + + +contract C is A { + modifier mod1 override { + f4(); + _; + } + + function f3() public override { + data |= 0x300; + } + + function f4() public { + data |= 0x4000; + } +} + +// ---- +// getData() -> 0x4300 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol new file mode 100644 index 000000000000..ac99b0cf74f8 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol @@ -0,0 +1,27 @@ +contract A { + uint256 data; + + constructor() public mod1 { + data |= 2; + } + + modifier mod1 virtual { + data |= 1; + _; + } + + function getData() public returns (uint256 r) { + return data; + } +} + + +contract C is A { + modifier mod1 override { + data |= 4; + _; + } +} + +// ---- +// getData() -> 6 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol new file mode 100644 index 000000000000..f10ebb0e74bb --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol @@ -0,0 +1,28 @@ +library L { + struct S { + uint256 v; + } + modifier mod(S storage s) { + s.v++; + _; + } + + function libFun(S storage s) internal mod(s) { + s.v += 0x100; + } +} + + +contract Test { + using L for *; + L.S s; + + function f() public returns (uint256) { + s.libFun(); + L.libFun(s); + return s.v; + } +} + +// ---- +// f() -> 0x202 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol new file mode 100644 index 000000000000..3d5e97de00f2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol @@ -0,0 +1,34 @@ +// Tests that virtual lookup for modifiers in libraries does not consider +// the current inheritance hierarchy. +library L { + struct S { + uint256 v; + } + modifier mod(S storage s) { + s.v++; + _; + } + + function libFun(S storage s) internal mod(s) { + s.v += 0x100; + } +} + + +contract Test { + using L for *; + L.S s; + modifier mod(L.S storage) { + revert(); + _; + } + + function f() public returns (uint256) { + s.libFun(); + L.libFun(s); + return s.v; + } +} + +// ---- +// f() -> 0x202 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol new file mode 100644 index 000000000000..1df8b0874161 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol @@ -0,0 +1,19 @@ +contract C { + modifier mod1 { + uint8 a = 1; + uint8 b = 2; + _; + } + modifier mod2(bool a) { + if (a) return; + else _; + } + + function f(bool a) public mod1 mod2(a) returns (uint256 r) { + return 3; + } +} + +// ---- +// f(bool): true -> 0 +// f(bool): false -> 3 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol new file mode 100644 index 000000000000..0c4c07828af2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol @@ -0,0 +1,13 @@ +contract C { + modifier repeat(uint256 count) { + uint256 i; + for (i = 0; i < count; ++i) _; + } + + function f() public repeat(10) returns (uint256 r) { + r += 1; + } +} + +// ---- +// f() -> 10 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol new file mode 100644 index 000000000000..124e1597001e --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol @@ -0,0 +1,14 @@ +contract C { + modifier repeat(bool twice) { + if (twice) _; + _; + } + + function f(bool twice) public repeat(twice) returns (uint256 r) { + r += 1; + } +} + +// ---- +// f(bool): false -> 1 +// f(bool): true -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol new file mode 100644 index 000000000000..286476820029 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol @@ -0,0 +1,17 @@ +// Note that return sets the return variable and jumps to the end of the current function or +// modifier code block. +contract C { + modifier repeat(bool twice) { + if (twice) _; + _; + } + + function f(bool twice) public repeat(twice) returns (uint256 r) { + r += 1; + return r; + } +} + +// ---- +// f(bool): false -> 1 +// f(bool): true -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol new file mode 100644 index 000000000000..722334ec9250 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol @@ -0,0 +1,15 @@ +contract C { + uint256 public a; + modifier mod(uint256 x) { + a += x; + _; + } + + function f(uint256 x) public mod(2) mod(5) mod(x) returns (uint256) { + return a; + } +} + +// ---- +// f(uint256): 3 -> 10 +// a() -> 10 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol new file mode 100644 index 000000000000..64354ba5e250 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol @@ -0,0 +1,18 @@ +contract C { + uint256 public a; + modifier mod(uint256 x) { + uint256 b = x; + a += b; + _; + a -= b; + assert(b == x); + } + + function f(uint256 x) public mod(2) mod(5) mod(x) returns (uint256) { + return a; + } +} + +// ---- +// f(uint256): 3 -> 10 +// a() -> 0 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol new file mode 100644 index 000000000000..c91869615517 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol @@ -0,0 +1,19 @@ +contract A { + function f() public mod returns (bool r) { + return true; + } + + modifier mod virtual { + _; + } +} + + +contract C is A { + modifier mod override { + if (false) _; + } +} + +// ---- +// f() -> false diff --git a/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol b/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol new file mode 100644 index 000000000000..c437922e4e2d --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol @@ -0,0 +1,16 @@ +contract C { + uint256 public x; + modifier setsx { + _; + x = 9; + } + + function f() public setsx returns (uint256) { + return 2; + } +} + +// ---- +// x() -> 0 +// f() -> 2 +// x() -> 9 diff --git a/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol new file mode 100644 index 000000000000..81fbd794e3e4 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 1; i < 10; i++) { + if (i == 5) return; + _; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 4 diff --git a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol new file mode 100644 index 000000000000..1d1036c2c8d2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + _; + break; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/reverts/assert_require.sol b/test/libsolidity/semanticTests/reverts/assert_require.sol new file mode 100644 index 000000000000..6bd146a7a3aa --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/assert_require.sol @@ -0,0 +1,24 @@ +contract C { + function f() public { + assert(false); + } + + function g(bool val) public returns (bool) { + assert(val == true); + return true; + } + + function h(bool val) public returns (bool) { + require(val); + return true; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE +// g(bool): false -> FAILURE +// g(bool): true -> true +// h(bool): false -> FAILURE +// h(bool): true -> true diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol new file mode 100644 index 000000000000..d0adf2785d41 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol @@ -0,0 +1,20 @@ +contract C { + enum X {A, B} + + function tested(X x) public returns (uint256) { + return 1; + } + + function test() public returns (uint256) { + X garbled; + + assembly { + garbled := 5 + } + + return this.tested(garbled); + } +} + +// ---- +// test() -> FAILURE # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol new file mode 100644 index 000000000000..6bdf14298809 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol @@ -0,0 +1,32 @@ +contract C { + enum X {A, B} + + function test_return() public returns (X) { + X garbled; + assembly { + garbled := 5 + } + return garbled; + } + + function test_inline_assignment() public returns (X _ret) { + assembly { + _ret := 5 + } + } + + function test_assignment() public returns (X _ret) { + X tmp; + assembly { + tmp := 5 + } + _ret = tmp; + } +} + +// ==== +// compileViaYul: also +// ---- +// test_return() -> FAILURE # both should throw # +// test_inline_assignment() -> FAILURE +// test_assignment() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol new file mode 100644 index 000000000000..17ee32bb889e --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol @@ -0,0 +1,29 @@ +contract C { + enum X {A, B} + + function test_eq() public returns (bool) { + X garbled; + assembly { + garbled := 5 + } + return garbled == garbled; + } + + function test_eq_ok() public returns (bool) { + X garbled = X.A; + return garbled == garbled; + } + + function test_neq() public returns (bool) { + X garbled; + assembly { + garbled := 5 + } + return garbled != garbled; + } +} + +// ---- +// test_eq_ok() -> 1 +// test_eq() -> FAILURE # both should throw # +// test_neq() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol new file mode 100644 index 000000000000..2b90451ec639 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol @@ -0,0 +1,23 @@ +contract C { + enum X {A, B} + X public x; + + function test_store() public returns (uint256) { + X garbled = X.A; + assembly { + garbled := 5 + } + x = garbled; + return 1; + } + + function test_store_ok() public returns (uint256) { + x = X.A; + return 1; + } +} + +// ---- +// test_store_ok() -> 1 +// x() -> 0 +// test_store() -> FAILURE # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_instruction.sol b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol new file mode 100644 index 000000000000..839296ca193a --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol @@ -0,0 +1,12 @@ +contract C { + function f() public { + assembly { + invalid() + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/revert.sol b/test/libsolidity/semanticTests/reverts/revert.sol new file mode 100644 index 000000000000..eae49c799bc6 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/revert.sol @@ -0,0 +1,21 @@ +contract C { + uint256 public a = 42; + + function f() public { + a = 1; + revert(); + } + + function g() public { + a = 1; + assembly { + revert(0, 0) + } + } +} + +// ---- +// f() -> FAILURE +// a() -> 42 +// g() -> FAILURE +// a() -> 42 diff --git a/test/libsolidity/semanticTests/reverts/simple_throw.sol b/test/libsolidity/semanticTests/reverts/simple_throw.sol new file mode 100644 index 000000000000..ad88deca6c0c --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/simple_throw.sol @@ -0,0 +1,11 @@ +contract Test { + function f(uint256 x) public returns (uint256) { + if (x > 10) return x + 10; + else revert(); + return 2; + } +} + +// ---- +// f(uint256): 11 -> 21 +// f(uint256): 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol new file mode 100644 index 000000000000..c6ada8c6a3d1 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol @@ -0,0 +1,34 @@ +contract C { + function shl(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shl(b, a) + } + } + + function shr(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shr(b, a) + } + } + + function sar(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := sar(b, a) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl(uint256,uint256): 0x01, 0x02 -> 0x04 +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// shr(uint256,uint256): 0x03, 0x01 -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// sar(uint256,uint256): 0x03, 0x01 -> 0x01 +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol new file mode 100644 index 000000000000..6cacbe30034e --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol @@ -0,0 +1,122 @@ +contract C { + function shl_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0, a) + } + } + + function shr_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0, a) + } + } + + function sar_zero(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0, a) + } + } + + function shl_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x110, a) + } + } + + function shr_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x110, a) + } + } + + function sar_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0x110, a) + } + } + + function shl_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shl(4, shl(12, a)) + } + } + + function shr_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shr(4, shr(12, a)) + } + } + + function sar_combined(uint256 a) public returns (uint256 c) { + assembly { + c := sar(4, sar(12, a)) + } + } + + function shl_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0xd0, shl(0x40, a)) + } + } + + function shl_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x01, shl(not(0x00), a)) + } + } + + function shr_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0xd0, shr(0x40, a)) + } + } + + function shr_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x01, shr(not(0x00), a)) + } + } + + function sar_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0xd0, sar(0x40, a)) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_zero(uint256): 0x00 -> 0x00 +// shl_zero(uint256): 0xffff -> 0xffff +// shl_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr_zero(uint256): 0x00 -> 0x00 +// shr_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_zero(uint256): 0x00 -> 0x00 +// sar_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_large(uint256): 0x00 -> 0x00 +// shl_large(uint256): 0xffff -> 0x00 +// shl_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_large(uint256): 0x00 -> 0x00 +// shr_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0x00 -> 0x00 +// sar_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined(uint256): 0x00 -> 0x00 +// shl_combined(uint256): 0xffff -> 0xffff0000 +// shl_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000 +// shr_combined(uint256): 0x00 -> 0x00 +// shr_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0x00 -> 0x00 +// sar_combined(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined_large(uint256): 0x00 -> 0x00 +// shl_combined_large(uint256): 0xffff -> 0x00 +// shl_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shl_combined_overflow(uint256): 0x02 -> 0x00 +// shr_combined_large(uint256): 0x00 -> 0x00 +// shr_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_combined_overflow(uint256): 0x02 -> 0x00 +// sar_combined_large(uint256): 0x00 -> 0x00 +// sar_combined_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol new file mode 100644 index 000000000000..bbc5ddaa4b87 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol @@ -0,0 +1,83 @@ +contract C { + function shl_1() public returns (bool) { + uint256 c; + assembly { + c := shl(2, 1) + } + assert(c == 4); + return true; + } + + function shl_2() public returns (bool) { + uint256 c; + assembly { + c := shl( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + ); + return true; + } + + function shl_3() public returns (bool) { + uint256 c; + assembly { + c := shl( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } + + function shr_1() public returns (bool) { + uint256 c; + assembly { + c := shr(1, 3) + } + assert(c == 1); + return true; + } + + function shr_2() public returns (bool) { + uint256 c; + assembly { + c := shr( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); + return true; + } + + function shr_3() public returns (bool) { + uint256 c; + assembly { + c := shr( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_1() -> 0x01 +// shl_2() -> 0x01 +// shl_3() -> 0x01 +// shr_1() -> 0x01 +// shr_2() -> 0x01 +// shr_3() -> 0x01 diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup.sol new file mode 100644 index 000000000000..81296ba9520f --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_cleanup.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint16 x) { + x = 0xffff; + x += 32; + x <<= 8; + x >>= 16; + } +} + +// ---- +// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol new file mode 100644 index 000000000000..cc81c15e4df3 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint8 x) { + assembly { + x := 0xffff + } + x >>= 8; + } +} + +// ---- +// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol new file mode 100644 index 000000000000..4c6f3737f654 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol @@ -0,0 +1,6 @@ +contract C { + uint256 public a = 0x42 << 8; +} + +// ---- +// a() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol new file mode 100644 index 000000000000..e5a4152b57b1 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256 a) { + a = 0x42; + a <<= 8; + } +} + +// ---- +// f() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol new file mode 100644 index 000000000000..766a8522e514 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol @@ -0,0 +1,6 @@ +contract C { + uint256 public a = 0x4200 >> 8; +} + +// ---- +// a() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol new file mode 100644 index 000000000000..0f36c10ee0a3 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256 a) { + a = 0x4200; + a >>= 8; + } +} + +// ---- +// f() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_left.sol b/test/libsolidity/semanticTests/shifts/shift_left.sol new file mode 100644 index 000000000000..15d2a972a491 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint256): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint256): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint256): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 +// f(uint256,uint256): 0x4266, 0x100 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol new file mode 100644 index 000000000000..06cb386067be --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + a <<= b; + return a; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint256): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint256): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint256): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 +// f(uint256,uint256): 0x4266, 0x100 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol new file mode 100644 index 000000000000..5cc15c1a85e3 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint256 a, uint8 b) public returns (uint256) { + a <<= b; + return a; + } +} + +// ---- +// f(uint256,uint8): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint8): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint8): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint8): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint8): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol new file mode 100644 index 000000000000..99ff376d6a63 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol @@ -0,0 +1,11 @@ +// This basically tests proper cleanup and conversion. It should not convert x to int8. +contract C { + function f() public returns (int8) { + uint8 x = 254; + int8 y = 1; + return y << x; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol new file mode 100644 index 000000000000..0f35077b15de --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint32 a, uint32 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint32,uint32): 0x4266, 0x0 -> 0x4266 +// f(uint32,uint32): 0x4266, 0x8 -> 0x426600 +// f(uint32,uint32): 0x4266, 0x10 -> 0x42660000 +// f(uint32,uint32): 0x4266, 0x11 -> 0x84cc0000 +// f(uint32,uint32): 0x4266, 0x20 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol new file mode 100644 index 000000000000..3070314f8454 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint8,uint8): 0x66, 0x0 -> 0x66 +// f(uint8,uint8): 0x66, 0x8 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol new file mode 100644 index 000000000000..b92fb2229cf8 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol @@ -0,0 +1,6 @@ +contract C { + int256 public a = -0x42 << 8; +} + +// ---- +// a() -> -16896 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol new file mode 100644 index 000000000000..b084633339b9 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol @@ -0,0 +1,6 @@ +contract C { + int256 public a = -0x4200 >> 8; +} + +// ---- +// a() -> -66 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol new file mode 100644 index 000000000000..77c18c44b1f2 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol @@ -0,0 +1,13 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + return a << b; + } + + function g(int256 a, int256 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int256,int256): 1, -1 -> FAILURE +// g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol new file mode 100644 index 000000000000..e63a9a57eafb --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol @@ -0,0 +1,15 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a <<= b; + return a; + } + + function g(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): 1, -1 -> FAILURE +// g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_overflow.sol b/test/libsolidity/semanticTests/shifts/shift_overflow.sol new file mode 100644 index 000000000000..f1b4bca0a920 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_overflow.sol @@ -0,0 +1,16 @@ +contract C { + function leftU(uint8 x, uint8 y) public returns (uint8) { + return x << y; + } + + function leftS(int8 x, int8 y) public returns (int8) { + return x << y; + } +} + +// ---- +// leftU(uint8,uint8): 255, 8 -> 0 +// leftU(uint8,uint8): 255, 1 -> 254 +// leftU(uint8,uint8): 255, 0 -> 255 +// leftS(int8,int8): 1, 7 -> -128 # Result is -128 and output is sign-extended, not zero-padded. # +// leftS(int8,int8): 1, 6 -> 64 diff --git a/test/libsolidity/semanticTests/shifts/shift_right.sol b/test/libsolidity/semanticTests/shifts/shift_right.sol new file mode 100644 index 000000000000..d78d18abaeeb --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x42 +// f(uint256,uint256): 0x4266, 0x10 -> 0 +// f(uint256,uint256): 0x4266, 0x11 -> 0 +// f(uint256,uint256): 57896044618658097711785492504343953926634992332820282019728792003956564819968, 5 -> 1809251394333065553493296640760748560207343510400633813116524750123642650624 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol new file mode 100644 index 000000000000..cfee67301925 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + a >>= b; + return a; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x42 +// f(uint256,uint256): 0x4266, 0x10 -> 0 +// f(uint256,uint256): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol new file mode 100644 index 000000000000..ba819fbcedb2 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol @@ -0,0 +1,12 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): 0x4266, 0x0 -> 0x4266 +// f(int256,int256): 0x4266, 0x8 -> 0x42 +// f(int256,int256): 0x4266, 0x10 -> 0 +// f(int256,int256): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol new file mode 100644 index 000000000000..c0d6be8bd5b2 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> 0x0f diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol new file mode 100644 index 000000000000..df0570cb7536 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol @@ -0,0 +1,30 @@ +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol new file mode 100644 index 000000000000..0c1949a59dc3 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol @@ -0,0 +1,31 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> FAILURE +// f(int8,uint8): 0x00, 0x1004 -> FAILURE +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> FAILURE +// g(int8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol new file mode 100644 index 000000000000..54ac9540fc89 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol new file mode 100644 index 000000000000..2ae9647e262e --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol @@ -0,0 +1,65 @@ +contract C { + function f1() public pure returns (bool) { + return (-4266 >> 0) == -4266; + } + + function f2() public pure returns (bool) { + return (-4266 >> 1) == -2133; + } + + function f3() public pure returns (bool) { + return (-4266 >> 4) == -267; + } + + function f4() public pure returns (bool) { + return (-4266 >> 8) == -17; + } + + function f5() public pure returns (bool) { + return (-4266 >> 16) == -1; + } + + function f6() public pure returns (bool) { + return (-4266 >> 17) == -1; + } + + function g1() public pure returns (bool) { + return (-4267 >> 0) == -4267; + } + + function g2() public pure returns (bool) { + return (-4267 >> 1) == -2134; + } + + function g3() public pure returns (bool) { + return (-4267 >> 4) == -267; + } + + function g4() public pure returns (bool) { + return (-4267 >> 8) == -17; + } + + function g5() public pure returns (bool) { + return (-4267 >> 16) == -1; + } + + function g6() public pure returns (bool) { + return (-4267 >> 17) == -1; + } +} + +// ==== +// compileViaYul: also +// ---- +// f1() -> true +// f2() -> true +// f3() -> true +// f4() -> true +// f5() -> true +// f6() -> true +// g1() -> true +// g2() -> true +// g3() -> true +// g4() -> true +// g5() -> true +// g6() -> true diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol new file mode 100644 index 000000000000..73aae5bc906a --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol @@ -0,0 +1,19 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int256,int256): -4266, 0 -> -4266 +// f(int256,int256): -4266, 1 -> -2133 +// f(int256,int256): -4266, 4 -> -267 +// f(int256,int256): -4266, 8 -> -17 +// f(int256,int256): -4266, 16 -> -1 +// f(int256,int256): -4266, 17 -> -1 +// f(int256,int256): -4267, 0 -> -4267 +// f(int256,int256): -4267, 1 -> -2134 +// f(int256,int256): -4267, 4 -> -267 +// f(int256,int256): -4267, 8 -> -17 +// f(int256,int256): -4267, 16 -> -1 +// f(int256,int256): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol new file mode 100644 index 000000000000..7f3beb59f1a7 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol @@ -0,0 +1,20 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): -4266, 0 -> -4266 +// f(int256,int256): -4266, 1 -> -2133 +// f(int256,int256): -4266, 4 -> -267 +// f(int256,int256): -4266, 8 -> -17 +// f(int256,int256): -4266, 16 -> -1 +// f(int256,int256): -4266, 17 -> -1 +// f(int256,int256): -4267, 0 -> -4267 +// f(int256,int256): -4267, 1 -> -2134 +// f(int256,int256): -4267, 4 -> -267 +// f(int256,int256): -4267, 8 -> -17 +// f(int256,int256): -4267, 16 -> -1 +// f(int256,int256): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol new file mode 100644 index 000000000000..24ab541230ae --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol @@ -0,0 +1,19 @@ +contract C { + function f(int16 a, int16 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int16,int16): -4266, 0 -> -4266 +// f(int16,int16): -4266, 1 -> -2133 +// f(int16,int16): -4266, 4 -> -267 +// f(int16,int16): -4266, 8 -> -17 +// f(int16,int16): -4266, 16 -> -1 +// f(int16,int16): -4266, 17 -> -1 +// f(int16,int16): -4267, 0 -> -4267 +// f(int16,int16): -4267, 1 -> -2134 +// f(int16,int16): -4267, 4 -> -267 +// f(int16,int16): -4267, 8 -> -17 +// f(int16,int16): -4267, 16 -> -1 +// f(int16,int16): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol new file mode 100644 index 000000000000..7ff669dc9f08 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol @@ -0,0 +1,19 @@ +contract C { + function f(int32 a, int32 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int32,int32): -4266, 0 -> -4266 +// f(int32,int32): -4266, 1 -> -2133 +// f(int32,int32): -4266, 4 -> -267 +// f(int32,int32): -4266, 8 -> -17 +// f(int32,int32): -4266, 16 -> -1 +// f(int32,int32): -4266, 17 -> -1 +// f(int32,int32): -4267, 0 -> -4267 +// f(int32,int32): -4267, 1 -> -2134 +// f(int32,int32): -4267, 4 -> -267 +// f(int32,int32): -4267, 8 -> -17 +// f(int32,int32): -4267, 16 -> -1 +// f(int32,int32): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol new file mode 100644 index 000000000000..c6424f141e1b --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol @@ -0,0 +1,19 @@ +contract C { + function f(int8 a, int8 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int8,int8): -66, 0 -> -66 +// f(int8,int8): -66, 1 -> -33 +// f(int8,int8): -66, 4 -> -5 +// f(int8,int8): -66, 8 -> -1 +// f(int8,int8): -66, 16 -> -1 +// f(int8,int8): -66, 17 -> -1 +// f(int8,int8): -67, 0 -> -67 +// f(int8,int8): -67, 1 -> -34 +// f(int8,int8): -67, 4 -> -5 +// f(int8,int8): -67, 8 -> -1 +// f(int8,int8): -67, 16 -> -1 +// f(int8,int8): -67, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol new file mode 100644 index 000000000000..c59107f52f3d --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol @@ -0,0 +1,13 @@ +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int16,int16): 0xff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int16,int16): 0xff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int16,int16): 0xff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int16,int16): 0xff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int16,int16): 0xff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol new file mode 100644 index 000000000000..6e462e704494 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ---- +// f(int16,int16): 0xff99, 0x00 -> FAILURE +// f(int16,int16): 0xff99, 0x01 -> FAILURE +// f(int16,int16): 0xff99, 0x02 -> FAILURE +// f(int16,int16): 0xff99, 0x04 -> FAILURE +// f(int16,int16): 0xff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol new file mode 100644 index 000000000000..74e9d53b0ddf --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol @@ -0,0 +1,13 @@ +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int32,int32): 0xffffff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int32,int32): 0xffffff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int32,int32): 0xffffff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int32,int32): 0xffffff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol new file mode 100644 index 000000000000..2466298f02ea --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> FAILURE +// f(int32,int32): 0xffffff99, 0x01 -> FAILURE +// f(int32,int32): 0xffffff99, 0x02 -> FAILURE +// f(int32,int32): 0xffffff99, 0x04 -> FAILURE +// f(int32,int32): 0xffffff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol new file mode 100644 index 000000000000..06dcf8eb5be0 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol @@ -0,0 +1,13 @@ +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,int8): 0x99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int8,int8): 0x99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int8,int8): 0x99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int8,int8): 0x99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int8,int8): 0x99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol new file mode 100644 index 000000000000..643bc5e62b77 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ---- +// f(int8,int8): 0x99, 0x00 -> FAILURE +// f(int8,int8): 0x99, 0x01 -> FAILURE +// f(int8,int8): 0x99, 0x02 -> FAILURE +// f(int8,int8): 0x99, 0x04 -> FAILURE +// f(int8,int8): 0x99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol new file mode 100644 index 000000000000..03573d985c7d --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol @@ -0,0 +1,11 @@ +contract C { + function f(uint32 a, uint32 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint32,uint32): 0x4266, 0x0 -> 0x4266 +// f(uint32,uint32): 0x4266, 0x8 -> 0x42 +// f(uint32,uint32): 0x4266, 0x10 -> 0 +// f(uint32,uint32): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol new file mode 100644 index 000000000000..8457b10e169b --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint8,uint8): 0x66, 0x0 -> 0x66 +// f(uint8,uint8): 0x66, 0x8 -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts.sol b/test/libsolidity/semanticTests/shifts/shifts.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts.sol rename to test/libsolidity/semanticTests/shifts/shifts.sol diff --git a/test/libsolidity/semanticTests/storage/packed_functions.sol b/test/libsolidity/semanticTests/storage/packed_functions.sol new file mode 100644 index 000000000000..4a49a614f3b5 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_functions.sol @@ -0,0 +1,48 @@ +contract C { + // these should take the same slot + function() internal returns (uint) a; + function() external returns (uint) b; + function() external returns (uint) c; + function() internal returns (uint) d; + uint8 public x; + + function set() public { + x = 2; + d = g; + c = this.h; + b = this.h; + a = g; + } + + function t1() public returns (uint256) { + return a(); + } + + function t2() public returns (uint256) { + return b(); + } + + function t3() public returns (uint256) { + return a(); + } + + function t4() public returns (uint256) { + return b(); + } + + function g() public returns (uint256) { + return 7; + } + + function h() public returns (uint256) { + return 8; + } +} + +// ---- +// set() -> +// t1() -> 7 +// t2() -> 8 +// t3() -> 7 +// t4() -> 8 +// x() -> 2 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol new file mode 100644 index 000000000000..9b20dbfd2c55 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol @@ -0,0 +1,16 @@ +contract C { + uint16 x = 0x1234; + uint16 a = 0xffff; + uint16 b; + + function f() public returns (uint256, uint256, uint256, uint256) { + a++; + uint256 c = b; + delete b; + a -= 2; + return (x, c, b, a); + } +} + +// ---- +// f() -> 0x1234, 0x0, 0x0, 0xfffe diff --git a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol new file mode 100644 index 000000000000..8db0c757ed3b --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol @@ -0,0 +1,22 @@ +contract C { + int8 a; + uint8 b; + int8 c; + uint8 d; + + function test() + public + returns (uint256 x1, uint256 x2, uint256 x3, uint256 x4) + { + a = -2; + b = -uint8(a) * 2; + c = a * int8(120) * int8(121); + x1 = uint256(a); + x2 = b; + x3 = uint256(c); + x4 = d; + } +} + +// ---- +// test() -> -2, 4, -112, 0 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol new file mode 100644 index 000000000000..daeee6ba6f4d --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol @@ -0,0 +1,45 @@ +contract C { + struct s1 { + bytes1 a; + bytes1 b; + bytes10 c; + bytes9 d; + bytes10 e; + } + struct s2 { + bytes1 a; + s1 inner; + bytes1 b; + bytes1 c; + } + bytes1 x; + s2 data; + bytes1 y; + + function test() public returns (bool) { + x = 0x01; + data.a = 0x02; + data.inner.a = 0x03; + data.inner.b = 0x04; + data.inner.c = "1234567890"; + data.inner.d = "123456789"; + data.inner.e = "abcdefghij"; + data.b = 0x05; + data.c = bytes1(0x06); + y = 0x07; + return + x == 0x01 && + data.a == 0x02 && + data.inner.a == 0x03 && + data.inner.b == 0x04 && + data.inner.c == "1234567890" && + data.inner.d == "123456789" && + data.inner.e == "abcdefghij" && + data.b == 0x05 && + data.c == bytes1(0x06) && + y == 0x07; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol new file mode 100644 index 000000000000..9c9f778f0f85 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol @@ -0,0 +1,33 @@ +contract C { + enum small {A, B, C, D} + enum larger {A, B, C, D, E} + struct str { + small a; + small b; + larger c; + larger d; + } + str data; + + function test() public returns (uint256) { + data.a = small.B; + if (data.a != small.B) return 2; + data.b = small.C; + if (data.b != small.C) return 3; + data.c = larger.D; + if (data.c != larger.D) return 4; + if (data.a != small.B) return 5; + data.a = small.C; + if (data.a != small.C) return 6; + if (data.b != small.C) return 7; + data.b = small.D; + if (data.b != small.D) return 8; + if (data.c != larger.D) return 9; + data.c = larger.B; + if (data.c != larger.B) return 10; + return 1; + } +} + +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol new file mode 100644 index 000000000000..8c91576f5003 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol @@ -0,0 +1,30 @@ +contract C { + struct str { + uint8 a; + uint16 b; + uint248 c; + } + str data; + + function test() public returns (uint256) { + data.a = 2; + if (data.a != 2) return 2; + data.b = 0xabcd; + if (data.b != 0xabcd) return 3; + data.c = 0x1234567890; + if (data.c != 0x1234567890) return 4; + if (data.a != 2) return 5; + data.a = 8; + if (data.a != 8) return 6; + if (data.b != 0xabcd) return 7; + data.b = 0xdcab; + if (data.b != 0xdcab) return 8; + if (data.c != 0x1234567890) return 9; + data.c = 0x9876543210; + if (data.c != 0x9876543210) return 10; + return 1; + } +} + +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol new file mode 100644 index 000000000000..a7ecf2bd02d6 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S calldata s) external pure returns (uint256 a, uint256 b) { + a = s.a; + b = s.b; + } +} + +// ---- +// f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol new file mode 100644 index 000000000000..c3b8249e8f61 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(uint256 a, S calldata s, uint256 b) + external + pure + returns (uint256, uint256, uint256, uint256) + { + return (a, s.a, s.b, b); + } +} + +// ---- +// f(uint256,(uint256,uint256),uint256): 1, 2, 3, 4 -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol new file mode 100644 index 000000000000..3e8613fad9b5 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[2] b; + uint256 c; + } + + function f(S calldata s) + external + pure + returns (uint256 a, uint256 b0, uint256 b1, uint256 c) + { + a = s.a; + b0 = s.b[0]; + b1 = s.b[1]; + c = s.c; + } +} + +// ---- +// f((uint256,uint256[2],uint256)): 42, 1, 2, 23 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol new file mode 100644 index 000000000000..db8171a202d0 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S calldata s) external pure returns (uint256, uint256) { + S memory m = s; + return (m.a, m.b); + } +} + +// ---- +// f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol new file mode 100644 index 000000000000..4139c1073b65 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol @@ -0,0 +1,27 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + } + + function f(S1 calldata s1, S2 calldata s2, S1 calldata s3) + external + pure + returns (uint256 a, uint256 b, uint256 c, uint256 d, uint256 e) + { + a = s1.a; + b = s1.b; + c = s2.a; + d = s3.a; + e = s3.b; + } +} + +// ---- +// f((uint256,uint256),(uint256),(uint256,uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol new file mode 100644 index 000000000000..829345d2e9f9 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol @@ -0,0 +1,16 @@ +contract C { + struct s { + uint256 a; + uint256 b; + } + + function f() public returns (uint256) { + s[7][]; // This is only the type, should not have any effect + return 3; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol new file mode 100644 index 000000000000..1173a3796590 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol @@ -0,0 +1,32 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + + function test() public returns (uint256 x, uint256 y, uint256 z) { + S memory data = combine(1, 2, 3); + x = extract(data, 0); + y = extract(data, 1); + z = extract(data, 2); + } + + function extract(S memory s, uint256 which) internal returns (uint256 x) { + if (which == 0) return s.x; + else if (which == 1) return s.y; + else return s.z; + } + + function combine(uint8 x, uint16 y, uint256 z) + internal + returns (S memory s) + { + s.x = x; + s.y = y; + s.z = z; + } +} + +// ---- +// test() -> 1, 2, 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol new file mode 100644 index 000000000000..f5c41a232b09 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol @@ -0,0 +1,42 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + struct X { + uint8 x; + S s; + } + + function test() + public + returns (uint256 a, uint256 x, uint256 y, uint256 z) + { + X memory d = combine(1, 2, 3, 4); + a = extract(d, 0); + x = extract(d, 1); + y = extract(d, 2); + z = extract(d, 3); + } + + function extract(X memory s, uint256 which) internal returns (uint256 x) { + if (which == 0) return s.x; + else if (which == 1) return s.s.x; + else if (which == 2) return s.s.y; + else return s.s.z; + } + + function combine(uint8 a, uint8 x, uint16 y, uint256 z) + internal + returns (X memory s) + { + s.x = a; + s.s.x = x; + s.s.y = y; + s.s.z = z; + } +} + +// ---- +// test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol new file mode 100644 index 000000000000..c16705643f1a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol @@ -0,0 +1,69 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + struct X { + uint8 x; + S s; + uint8[2] a; + } + X m_x; + + function load() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + m_x.x = 1; + m_x.s.x = 2; + m_x.s.y = 3; + m_x.s.z = 4; + m_x.a[0] = 5; + m_x.a[1] = 6; + X memory d = m_x; + a = d.x; + x = d.s.x; + y = d.s.y; + z = d.s.z; + a1 = d.a[0]; + a2 = d.a[1]; + } + + function store() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + X memory d; + d.x = 1; + d.s.x = 2; + d.s.y = 3; + d.s.z = 4; + d.a[0] = 5; + d.a[1] = 6; + m_x = d; + a = m_x.x; + x = m_x.s.x; + y = m_x.s.y; + z = m_x.s.z; + a1 = m_x.a[0]; + a2 = m_x.a[1]; + } +} +// ---- +// load() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 +// store() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol b/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol new file mode 100644 index 000000000000..105f599099fb --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol @@ -0,0 +1,56 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + uint8[2] a; + } + S[5] data; + + function testInit() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a, bool flag) + { + S[2] memory d; + x = d[0].x; + y = d[0].y; + z = d[0].z; + a = d[0].a[1]; + flag = true; + } + + function testCopyRead() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a) + { + data[2].x = 1; + data[2].y = 2; + data[2].z = 3; + data[2].a[1] = 4; + S memory s = data[2]; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } + + function testAssign() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a) + { + S memory s; + s.x = 1; + s.y = 2; + s.z = 3; + s.a[1] = 4; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } +} + +// ---- +// testInit() -> 0, 0, 0, 0, true +// testCopyRead() -> 1, 2, 3, 4 +// testAssign() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol b/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol new file mode 100644 index 000000000000..3aaa354f88f1 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol @@ -0,0 +1,24 @@ +contract Test { + struct S { + uint8 a; + mapping(uint256 => uint256) b; + uint8 c; + } + S s; + + function f() public returns (uint256) { + S memory x; + if (x.a != 0 || x.c != 0) return 1; + x.a = 4; + x.c = 5; + s = x; + if (s.a != 4 || s.c != 5) return 2; + x = S(2, 3); + if (x.a != 2 || x.c != 3) return 3; + x = s; + if (s.a != 4 || s.c != 5) return 4; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/structs/recursive_structs.sol b/test/libsolidity/semanticTests/structs/recursive_structs.sol new file mode 100644 index 000000000000..da568a25548d --- /dev/null +++ b/test/libsolidity/semanticTests/structs/recursive_structs.sol @@ -0,0 +1,19 @@ +contract C { + struct S { + S[] x; + } + S sstorage; + + function f() public returns (uint256) { + S memory s; + s.x = new S[](10); + delete s; + // TODO Uncomment after implemented. + // sstorage.x.push(); + delete sstorage; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol new file mode 100644 index 000000000000..8591131be896 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol @@ -0,0 +1,36 @@ +contract test { + struct testStruct { + uint256 m_value; + } + testStruct data1; + testStruct data2; + testStruct data3; + + constructor() public { + data1.m_value = 2; + } + + function assign() + public + returns ( + uint256 ret_local, + uint256 ret_global, + uint256 ret_global3, + uint256 ret_global1 + ) + { + testStruct storage x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 + data2 = data1; // should copy data. data2.m_value == 2 + + ret_local = x.m_value; // = 2 + ret_global = data2.m_value; // = 2 + + x.m_value = 3; + data3 = x; //should copy the data. data3.m_value == 3 + ret_global3 = data3.m_value; // = 3 + ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value + } +} + +// ---- +// assign() -> 2, 2, 3, 3 diff --git a/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol new file mode 100644 index 000000000000..dbd67ab586c0 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol @@ -0,0 +1,30 @@ +contract C { + struct X { + uint256 x1; + uint256 x2; + } + struct S { + uint256 s1; + uint256[3] s2; + X s3; + } + S s; + + constructor() public { + uint256[3] memory s2; + s2[1] = 9; + s = S(1, s2, X(4, 5)); + } + + function get() + public + returns (uint256 s1, uint256[3] memory s2, uint256 x1, uint256 x2) + { + s1 = s.s1; + s2 = s.s2; + x1 = s.s3.x1; + x2 = s.s3.x2; + } +} +// ---- +// get() -> 0x01, 0x00, 0x09, 0x00, 0x04, 0x05 diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol new file mode 100644 index 000000000000..f6c35f7da4e2 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -0,0 +1,48 @@ +contract c { + struct Nested { + uint256 x; + uint256 y; + } + struct Struct { + uint256 a; + mapping(uint256 => Struct) b; + Nested nested; + uint256 c; + } + mapping(uint256 => Struct) data; + + function set(uint256 k) public returns (bool) { + data[k].a = 1; + data[k].nested.x = 3; + data[k].nested.y = 4; + data[k].c = 2; + return true; + } + + function copy(uint256 from, uint256 to) public returns (bool) { + data[to] = data[from]; + return true; + } + + function retrieve(uint256 k) + public + returns (uint256 a, uint256 x, uint256 y, uint256 c) + { + a = data[k].a; + x = data[k].nested.x; + y = data[k].nested.y; + c = data[k].c; + } +} + +// ---- +// set(uint256): 7 -> true +// retrieve(uint256): 7 -> 1, 3, 4, 2 +// copy(uint256,uint256): 7, 8 -> true +// retrieve(uint256): 7 -> 1, 3, 4, 2 +// retrieve(uint256): 8 -> 1, 3, 4, 2 +// copy(uint256,uint256): 0, 7 -> true +// retrieve(uint256): 7 -> 0, 0, 0, 0 +// retrieve(uint256): 8 -> 1, 3, 4, 2 +// copy(uint256,uint256): 7, 8 -> true +// retrieve(uint256): 8 -> 0, 0, 0, 0 diff --git a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol new file mode 100644 index 000000000000..a1cfcac05df5 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol @@ -0,0 +1,19 @@ +contract c { + struct Struct { + uint256 a; + uint256 b; + } + Struct data1; + Struct data2; + + function test() public returns (bool) { + data1.a = 1; + data1.b = 2; + Struct memory x = data1; + data2 = x; + return data2.a == data1.a && data2.b == data1.b; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/structs/struct_delete_member.sol b/test/libsolidity/semanticTests/structs/struct_delete_member.sol new file mode 100644 index 000000000000..fbaf7b6d9e8f --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_member.sol @@ -0,0 +1,20 @@ +contract test { + struct testStruct { + uint256 m_value; + } + testStruct data1; + + constructor() public { + data1.m_value = 2; + } + + function deleteMember() public returns (uint256 ret_value) { + testStruct storage x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 + x.m_value = 4; + delete x.m_value; + ret_value = data1.m_value; + } +} + +// ---- +// deleteMember() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol new file mode 100644 index 000000000000..59d79da9fa3a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol @@ -0,0 +1,18 @@ +contract test { + struct testStruct { + uint256 m_value; + } + mapping(uint256 => testStruct) campaigns; + + constructor() public { + campaigns[0].m_value = 2; + } + + function deleteIt() public returns (uint256) { + delete campaigns[0]; + return campaigns[0].m_value; + } +} + +// ---- +// deleteIt() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol new file mode 100644 index 000000000000..5368b090ea86 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol @@ -0,0 +1,14 @@ +contract C { + struct S { + uint256 a; + bool x; + } + S public s; + + constructor() public { + s = S({a: 1, x: true}); + } +} + +// ---- +// s() -> 1, true diff --git a/test/libsolidity/semanticTests/types/tuple_in_tuple.sol b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol new file mode 100644 index 000000000000..d9737a807832 --- /dev/null +++ b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol @@ -0,0 +1,24 @@ +contract test { + function f0() public returns(int, bool) { + int a; + bool b; + ((a, b)) = (2, true); + return (a, b); + } + function f1() public returns(int) { + int a; + (((a, ), )) = ((1, 2) ,3); + return a; + } + function f2() public returns(int) { + int a; + (((, a),)) = ((1, 2), 3); + return a; + } +} +// ==== +// compileViaYul: also +// ---- +// f0() -> 2, true +// f1() -> 1 +// f2() -> 2 diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol new file mode 100644 index 000000000000..f6fa4d45e77a --- /dev/null +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + +struct S { uint256 v; string s; } + +contract A +{ + function test() external virtual returns (uint256 v, string memory s) + { + v = 42; + s = "test"; + } +} +contract X is A +{ + S public override test; + + function set() public { test.v = 2; test.s = "statevar"; } +} + + +// ---- +// test() -> 0, 64, 0 +// set() -> +// test() -> 2, 0x40, 8, "statevar" diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol new file mode 100644 index 000000000000..404013ee6c6a --- /dev/null +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; + +struct S { uint256 v; string s; } + +contract A +{ + function test(uint256 x) external virtual returns (uint256 v, string memory s) + { + v = x; + s = "test"; + } +} +contract X is A +{ + mapping(uint256 => S) public override test; + + function set() public { test[42].v = 2; test[42].s = "statevar"; } +} + + +// ---- +// test(uint256): 0 -> 0, 64, 0 +// test(uint256): 42 -> 0, 64, 0 +// set() -> +// test(uint256): 0 -> 0, 64, 0 +// test(uint256): 42 -> 2, 0x40, 8, "statevar" diff --git a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol new file mode 100644 index 000000000000..b83b1c598f84 --- /dev/null +++ b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol @@ -0,0 +1,10 @@ +contract C { + uint256 constant x = 0x123 + 0x456; + + function f() public returns (uint256) { + return x + 1; + } +} + +// ---- +// f() -> 0x57a diff --git a/test/libsolidity/semanticTests/various/balance.sol b/test/libsolidity/semanticTests/various/balance.sol new file mode 100644 index 000000000000..d6a80dbf9bff --- /dev/null +++ b/test/libsolidity/semanticTests/various/balance.sol @@ -0,0 +1,13 @@ +contract test { + constructor() public payable {} + + function getBalance() public returns (uint256 balance) { + return address(this).balance; + } +} + +// ==== +// compileViaYul: also +// ---- +// constructor(), 23 wei -> +// getBalance() -> 23 diff --git a/test/libsolidity/semanticTests/various/byte_optimization_bug.sol b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol new file mode 100644 index 000000000000..3d94e4333fe8 --- /dev/null +++ b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint256 x) public returns (uint256 a) { + assembly { + a := byte(x, 31) + } + } + + function g(uint256 x) public returns (uint256 a) { + assembly { + a := byte(31, x) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 2 -> 0 +// g(uint256): 2 -> 2 diff --git a/test/libsolidity/semanticTests/various/code_access_content.sol b/test/libsolidity/semanticTests/various/code_access_content.sol new file mode 100644 index 000000000000..2c115d6c8dae --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_content.sol @@ -0,0 +1,42 @@ +contract D { + bytes32 public x; + + constructor() public { + bytes32 codeHash; + assembly { + let size := codesize() + codecopy(mload(0x40), 0, size) + codeHash := keccak256(mload(0x40), size) + } + x = codeHash; + } +} + + +contract C { + function testRuntime() public returns (bool) { + D d = new D(); + bytes32 runtimeHash = keccak256(type(D).runtimeCode); + bytes32 otherHash; + uint256 size; + assembly { + size := extcodesize(d) + extcodecopy(d, mload(0x40), 0, size) + otherHash := keccak256(mload(0x40), size) + } + require(size == type(D).runtimeCode.length); + require(runtimeHash == otherHash); + return true; + } + + function testCreation() public returns (bool) { + D d = new D(); + bytes32 creationHash = keccak256(type(D).creationCode); + require(creationHash == d.x()); + return true; + } +} + +// ---- +// testRuntime() -> true +// testCreation() -> true diff --git a/test/libsolidity/semanticTests/various/code_access_create.sol b/test/libsolidity/semanticTests/various/code_access_create.sol new file mode 100644 index 000000000000..3fbcf6132e6d --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_create.sol @@ -0,0 +1,26 @@ +contract D { + uint256 x; + + constructor() public { + x = 7; + } + + function f() public view returns (uint256) { + return x; + } +} + + +contract C { + function test() public returns (uint256) { + bytes memory c = type(D).creationCode; + D d; + assembly { + d := create(0, add(c, 0x20), mload(c)) + } + return d.f(); + } +} + +// ---- +// test() -> 7 diff --git a/test/libsolidity/semanticTests/various/code_access_padding.sol b/test/libsolidity/semanticTests/various/code_access_padding.sol new file mode 100644 index 000000000000..ecad28a16580 --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_padding.sol @@ -0,0 +1,19 @@ +contract D { + function f() public pure returns (uint256) { + return 7; + } +} + + +contract C { + function diff() public pure returns (uint256 remainder) { + bytes memory a = type(D).creationCode; + bytes memory b = type(D).runtimeCode; + assembly { + remainder := mod(sub(b, a), 0x20) + } + } +} + +// ---- +// diff() -> 0 # This checks that the allocation function pads to multiples of 32 bytes # diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol new file mode 100644 index 000000000000..f1220d351e4c --- /dev/null +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -0,0 +1,20 @@ +contract A { + function f() public { + new B(); + } +} + + +contract B { + function f() public {} +} + + +contract C { + function f() public { + new B(); + } +} + +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol new file mode 100644 index 000000000000..fdd7aa32ebbf --- /dev/null +++ b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol @@ -0,0 +1,15 @@ +contract C { + function f() public returns (uint256 r) { + uint256; + uint256; + uint256; + uint256; + int256 x = -7; + return uint256(x); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> -7 diff --git a/test/libsolidity/semanticTests/various/cross_contract_types.sol b/test/libsolidity/semanticTests/various/cross_contract_types.sol new file mode 100644 index 000000000000..47842cc030bb --- /dev/null +++ b/test/libsolidity/semanticTests/various/cross_contract_types.sol @@ -0,0 +1,17 @@ +contract Lib { + struct S { + uint256 a; + uint256 b; + } +} + + +contract Test { + function f() public returns (uint256 r) { + Lib.S memory x = Lib.S({a: 2, b: 3}); + r = x.b; + } +} + +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/various/decayed_tuple.sol b/test/libsolidity/semanticTests/various/decayed_tuple.sol new file mode 100644 index 000000000000..b00942cb3225 --- /dev/null +++ b/test/libsolidity/semanticTests/various/decayed_tuple.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint256) { + uint256 x = 1; + (x) = 2; + return x; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol new file mode 100644 index 000000000000..0c8dc3835ff1 --- /dev/null +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -0,0 +1,36 @@ +contract C { + uint256 x = 7; + bytes data; + uint256[] y; + uint256[] arrayData; + + function returnsArray() public returns (uint256[] memory) { + arrayData = new uint256[](9); + arrayData[2] = 5; + arrayData[7] = 4; + return arrayData; + } + + function f(bytes memory s) public returns (uint256) { + uint256 loc; + uint256[] memory memArray; + (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2); + if (loc != 8) return 1; + if (x != 4) return 2; + if (y.length != 9) return 3; + if (y[2] != 5) return 4; + if (y[7] != 4) return 5; + if (data.length != s.length) return 6; + if (data[3] != s[3]) return 7; + if (arrayData[3] != 2) return 8; + (memArray, loc) = (arrayData, 3); + if (loc != 3) return 9; + if (memArray.length != arrayData.length) return 10; + bytes memory memBytes; + (x, memBytes, y[2], , ) = (456, s, 789, 101112, 131415); + if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11; + } +} + +// ---- +// f(bytes): 0x20, 0x5, "abcde" -> 0 diff --git a/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol new file mode 100644 index 000000000000..d1ea9ab34f0b --- /dev/null +++ b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol @@ -0,0 +1,10 @@ +contract test { + function f(uint256 k) public returns (uint256) { + return k; + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 9 -> 9 diff --git a/test/libsolidity/semanticTests/various/external_types_in_calls.sol b/test/libsolidity/semanticTests/various/external_types_in_calls.sol new file mode 100644 index 000000000000..9906bd52bc33 --- /dev/null +++ b/test/libsolidity/semanticTests/various/external_types_in_calls.sol @@ -0,0 +1,28 @@ +contract C1 { + C1 public bla; + + constructor(C1 x) public { + bla = x; + } +} + + +contract C { + function test() public returns (C1 x, C1 y) { + C1 c = new C1(C1(9)); + x = c.bla(); + y = this.t1(C1(7)); + } + + function t1(C1 a) public returns (C1) { + return a; + } + + function t2() public returns (C1) { + return C1(9); + } +} + +// ---- +// test() -> 9, 7 +// t2() -> 9 diff --git a/test/libsolidity/semanticTests/various/flipping_sign_tests.sol b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol new file mode 100644 index 000000000000..8309615a5c22 --- /dev/null +++ b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol @@ -0,0 +1,10 @@ +contract test { + function f() public returns (bool) { + int256 x = -2**255; + assert(-x == x); + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/gasleft_decrease.sol b/test/libsolidity/semanticTests/various/gasleft_decrease.sol new file mode 100644 index 000000000000..8de56296e0b7 --- /dev/null +++ b/test/libsolidity/semanticTests/various/gasleft_decrease.sol @@ -0,0 +1,20 @@ +contract C { + uint256 v; + + function f() public returns (bool) { + uint256 startGas = gasleft(); + v++; + assert(startGas > gasleft()); + return true; + } + + function g() public returns (bool) { + uint256 startGas = gasleft(); + assert(startGas > gasleft()); + return true; + } +} + +// ---- +// f() -> true +// g() -> true diff --git a/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol new file mode 100644 index 000000000000..00c0eabed3ba --- /dev/null +++ b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol @@ -0,0 +1,14 @@ +contract C { + function gasleft() public returns (uint256) { + return 0; + } + + function f() public returns (uint256) { + return gasleft(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/various/inline_member_init.sol b/test/libsolidity/semanticTests/various/inline_member_init.sol new file mode 100644 index 000000000000..5dca66d80c07 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_member_init.sol @@ -0,0 +1,19 @@ +contract test { + constructor() public { + m_b = 6; + m_c = 8; + } + + uint256 m_a = 5; + uint256 m_b; + uint256 m_c = 7; + + function get() public returns (uint256 a, uint256 b, uint256 c) { + a = m_a; + b = m_b; + c = m_c; + } +} + +// ---- +// get() -> 5, 6, 8 diff --git a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol new file mode 100644 index 000000000000..53f5b3718be7 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol @@ -0,0 +1,24 @@ +contract Base { + constructor() public {} + + uint256 m_base = 5; + + function getBMember() public returns (uint256 i) { + return m_base; + } +} + + +contract Derived is Base { + constructor() public {} + + uint256 m_derived = 6; + + function getDMember() public returns (uint256 i) { + return m_derived; + } +} + +// ---- +// getBMember() -> 5 +// getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol new file mode 100644 index 000000000000..e350a11d3f51 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol @@ -0,0 +1,9 @@ +contract c { + function f() public returns (int8) { + int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; + return foo3[0]; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol new file mode 100644 index 000000000000..743825d607f4 --- /dev/null +++ b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol @@ -0,0 +1,19 @@ +// A long time ago, some opcodes were renamed, which involved the opcodes +// "iszero" and "not". +contract C { + function f() public returns (bool) { + bytes32 x = bytes32(uint256(1)); + assembly { + x := not(x) + } + if (x != ~bytes32(uint256(1))) return false; + assembly { + x := iszero(x) + } + if (x != bytes32(0)) return false; + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/literal_empty_string.sol b/test/libsolidity/semanticTests/various/literal_empty_string.sol new file mode 100644 index 000000000000..bf4da54099af --- /dev/null +++ b/test/libsolidity/semanticTests/various/literal_empty_string.sol @@ -0,0 +1,20 @@ +contract C { + bytes32 public x; + uint256 public a; + + function f(bytes32 _x, uint256 _a) public { + x = _x; + a = _a; + } + + function g() public { + this.f("", 2); + } +} + +// ---- +// x() -> 0 +// a() -> 0 +// g() -> +// x() -> 0 +// a() -> 2 diff --git a/test/libsolidity/semanticTests/various/memory_overwrite.sol b/test/libsolidity/semanticTests/various/memory_overwrite.sol new file mode 100644 index 000000000000..7312509e2ff0 --- /dev/null +++ b/test/libsolidity/semanticTests/various/memory_overwrite.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (bytes memory x) { + x = "12345"; + x[3] = 0x61; + x[0] = 0x62; + } +} + +// ---- +// f() -> 0x20, 5, "b23a5" diff --git a/test/libsolidity/semanticTests/various/multi_modifiers.sol b/test/libsolidity/semanticTests/various/multi_modifiers.sol new file mode 100644 index 000000000000..07f6c038d3db --- /dev/null +++ b/test/libsolidity/semanticTests/various/multi_modifiers.sol @@ -0,0 +1,23 @@ +// This triggered a bug in some version because the variable in the modifier was not +// unregistered correctly. +contract C { + uint256 public x; + modifier m1 { + address a1 = msg.sender; + x++; + _; + } + + function f1() public m1() { + x += 7; + } + + function f2() public m1() { + x += 3; + } +} +// ---- +// f1() -> +// x() -> 0x08 +// f2() -> +// x() -> 0x0c diff --git a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol new file mode 100644 index 000000000000..6f79442e7aff --- /dev/null +++ b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol @@ -0,0 +1,47 @@ +contract C { + function g() public returns (uint256 a, uint256 b, uint256 c) { + a = 1; + b = 2; + c = 3; + } + + function h() public returns (uint256 a, uint256 b, uint256 c, uint256 d) { + a = 1; + b = 2; + c = 3; + d = 4; + } + + function f1() public returns (bool) { + (uint256 x, uint256 y, uint256 z) = g(); + if (x != 1 || y != 2 || z != 3) return false; + (, uint256 a, ) = g(); + if (a != 2) return false; + (uint256 b, , ) = g(); + if (b != 1) return false; + (, , uint256 c) = g(); + if (c != 3) return false; + return true; + } + + function f2() public returns (bool) { + (uint256 a1, , uint256 a3, ) = h(); + if (a1 != 1 || a3 != 3) return false; + (uint256 b1, uint256 b2, , ) = h(); + if (b1 != 1 || b2 != 2) return false; + (, uint256 c2, uint256 c3, ) = h(); + if (c2 != 2 || c3 != 3) return false; + (, , uint256 d3, uint256 d4) = h(); + if (d3 != 3 || d4 != 4) return false; + (uint256 e1, , uint256 e3, uint256 e4) = h(); + if (e1 != 1 || e3 != 3 || e4 != 4) return false; + return true; + } + + function f() public returns (bool) { + return f1() && f2(); + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/negative_stack_height.sol b/test/libsolidity/semanticTests/various/negative_stack_height.sol new file mode 100644 index 000000000000..d5074dc911fc --- /dev/null +++ b/test/libsolidity/semanticTests/various/negative_stack_height.sol @@ -0,0 +1,63 @@ +contract C { + mapping(uint256 => Invoice) public invoices; + struct Invoice { + uint256 AID; + bool Aboola; + bool Aboolc; + bool exists; + } + + function nredit(uint256 startindex) + public + pure + returns ( + uint256[500] memory CIDs, + uint256[500] memory dates, + uint256[500] memory RIDs, + bool[500] memory Cboolas, + uint256[500] memory amounts + ) + {} + + function return500InvoicesByDates( + uint256 begindate, + uint256 enddate, + uint256 startindex + ) + public + view + returns ( + uint256[500] memory AIDs, + bool[500] memory Aboolas, + uint256[500] memory dates, + bytes32[3][500] memory Abytesas, + bytes32[3][500] memory bytesbs, + bytes32[2][500] memory bytescs, + uint256[500] memory amounts, + bool[500] memory Aboolbs, + bool[500] memory Aboolcs + ) + {} + + function return500PaymentsByDates( + uint256 begindate, + uint256 enddate, + uint256 startindex + ) + public + view + returns ( + uint256[500] memory BIDs, + uint256[500] memory dates, + uint256[500] memory RIDs, + bool[500] memory Bboolas, + bytes32[3][500] memory bytesbs, + bytes32[2][500] memory bytescs, + uint256[500] memory amounts, + bool[500] memory Bboolbs + ) + {} +} + +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol new file mode 100644 index 000000000000..f02fda353d50 --- /dev/null +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + uint256 b; + S1 s; + uint256 c; + } + + function f(S2 calldata s) + external + pure + returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) + { + return (s.a, s.b, s.s.a, s.s.b, s.c); + } +} + +// ---- +// f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol new file mode 100644 index 000000000000..e0a25a857a8c --- /dev/null +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol @@ -0,0 +1,27 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + uint256 b; + S1 s; + uint256 c; + } + + function f(S2 calldata s) + external + pure + returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) + { + S2 memory m = s; + return (m.a, m.b, m.s.a, m.s.b, m.c); + } +} + +// ---- +// f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol new file mode 100644 index 000000000000..9ba67b198b15 --- /dev/null +++ b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol @@ -0,0 +1,10 @@ +contract test { + int8 public x = 2; + int8 public y = 127; + int16 public q = 250; +} + +// ---- +// x() -> 2 +// y() -> 127 +// q() -> 250 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol new file mode 100644 index 000000000000..0c84352b6237 --- /dev/null +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -0,0 +1,20 @@ +contract C { + function f() public view returns (uint256) { + return msg.sender.balance; + } +} + + +contract D { + C c = new C(); + + constructor() public payable {} + + function f() public view returns (uint256) { + return c.f(); + } +} + +// ---- +// constructor(), 27 wei -> +// f() -> 27 diff --git a/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol new file mode 100644 index 000000000000..0864121de77b --- /dev/null +++ b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol @@ -0,0 +1,35 @@ +contract Base { + uint256 data; + + function setData(uint256 i) public { + data = i; + } + + function getViaBase() public returns (uint256 i) { + return data; + } +} + + +contract A is Base { + function setViaA(uint256 i) public { + setData(i); + } +} + + +contract B is Base { + function getViaB() public returns (uint256 i) { + return getViaBase(); + } +} + + +contract Derived is Base, B, A {} + +// ==== +// compileViaYul: also +// ---- +// getViaB() -> 0 +// setViaA(uint256): 23 -> +// getViaB() -> 23 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol new file mode 100644 index 000000000000..350ea2622142 --- /dev/null +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol @@ -0,0 +1,15 @@ +// The EVM cannot provide access to dynamically-sized return values, so we have to skip them. +contract C { + function f() public returns (uint256, uint256[] memory, uint256) { + return (7, new uint256[](2), 8); + } + + function g() public returns (uint256, uint256) { + // Previous implementation "moved" b to the second place and did not skip. + (uint256 a, , uint256 b) = this.f(); + return (a, b); + } +} + +// ---- +// g() -> 7, 8 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol new file mode 100644 index 000000000000..3b80cf973030 --- /dev/null +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -0,0 +1,22 @@ +// For accessors, the dynamic types are already removed in the external signature itself. +contract C { + struct S { + uint256 x; + string a; // this is present in the accessor + uint256[] b; // this is not present + uint256 y; + } + S public s; + + function g() public returns (uint256, uint256) { + s.x = 2; + s.a = "abc"; + s.b = [7, 8, 9]; + s.y = 6; + (uint256 x, , uint256 y) = this.s(); + return (x, y); + } +} + +// ---- +// g() -> 2, 6 diff --git a/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol new file mode 100644 index 000000000000..585914c804b9 --- /dev/null +++ b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol @@ -0,0 +1,11 @@ +contract A { + uint256 x = 1; + uint256 y = 2; + + function a() public returns (uint256 x) { + x = A.y; + } +} + +// ---- +// a() -> 2 diff --git a/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol new file mode 100644 index 000000000000..6bef6f85a650 --- /dev/null +++ b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol @@ -0,0 +1,10 @@ +contract Scope { + uint256 stateVar = 42; + + function getStateVar() public view returns (uint256 stateVar) { + stateVar = Scope.stateVar; + } +} + +// ---- +// getStateVar() -> 42 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol new file mode 100644 index 000000000000..96c5419bea6b --- /dev/null +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// f() -> 0x1 # This should work, next should throw # +// fview() -> FAILURE +// fpure() -> FAILURE diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol new file mode 100644 index 000000000000..a193e875204d --- /dev/null +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: 0x1 +// fview() -> 1 +// fpure() -> 1 diff --git a/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol new file mode 100644 index 000000000000..7a8a2c276218 --- /dev/null +++ b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol @@ -0,0 +1,11 @@ +contract Test { + mapping(string => uint256) data; + + function f() public returns (uint256) { + data["abc"] = 2; + return data["abc"]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/various/store_bytes.sol b/test/libsolidity/semanticTests/various/store_bytes.sol new file mode 100644 index 000000000000..99eb1acff958 --- /dev/null +++ b/test/libsolidity/semanticTests/various/store_bytes.sol @@ -0,0 +1,14 @@ +// this test just checks that the copy loop does not mess up the stack +contract C { + function save() public returns (uint256 r) { + r = 23; + savedData = msg.data; + r = 24; + } + + bytes savedData; +} + +// ---- +// save() -> 24 # empty copy loop # +// save(): "abcdefg" -> 24 diff --git a/test/libsolidity/semanticTests/various/string_tuples.sol b/test/libsolidity/semanticTests/various/string_tuples.sol new file mode 100644 index 000000000000..3269d97c0b61 --- /dev/null +++ b/test/libsolidity/semanticTests/various/string_tuples.sol @@ -0,0 +1,17 @@ +contract C { + function f() public returns (string memory, uint256) { + return ("abc", 8); + } + + function g() public returns (string memory, string memory) { + return (h(), "def"); + } + + function h() public returns (string memory) { + return ("abc"); + } +} + +// ---- +// f() -> 0x40, 0x8, 0x3, "abc" +// g() -> 0x40, 0x80, 0x3, "abc", 0x3, "def" diff --git a/test/libsolidity/semanticTests/various/super.sol b/test/libsolidity/semanticTests/various/super.sol new file mode 100644 index 000000000000..16e4257e157c --- /dev/null +++ b/test/libsolidity/semanticTests/various/super.sol @@ -0,0 +1,29 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 4; + } +} + + +contract D is B, C { + function f() public override(B, C) returns (uint256 r) { + return super.f() | 8; + } +} + +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/various/super_alone.sol b/test/libsolidity/semanticTests/various/super_alone.sol new file mode 100644 index 000000000000..623f25330882 --- /dev/null +++ b/test/libsolidity/semanticTests/various/super_alone.sol @@ -0,0 +1,10 @@ +contract A { + function f() public { + super; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol new file mode 100644 index 000000000000..59907b476b7a --- /dev/null +++ b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol @@ -0,0 +1,34 @@ +// This tests a swap in storage which does not work as one +// might expect because we do not have temporary storage. +// (x, y) = (y, x) is the same as +// y = x; +// x = y; +contract c { + struct S { + uint256 a; + uint256 b; + } + S public x; + S public y; + + function set() public { + x.a = 1; + x.b = 2; + y.a = 3; + y.b = 4; + } + + function swap() public { + (x, y) = (y, x); + } +} + +// ---- +// x() -> 0, 0 +// y() -> 0, 0 +// set() -> +// x() -> 1, 2 +// y() -> 3, 4 +// swap() -> +// x() -> 1, 2 +// y() -> 1, 2 diff --git a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol new file mode 100644 index 000000000000..1f12ba390dd7 --- /dev/null +++ b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol @@ -0,0 +1,11 @@ +contract test { + function f(bool cond) public pure returns (uint256) { + uint32 x = 0x1234_ab; + uint256 y = 0x1234_abcd_1234; + return cond ? x : y; + } +} +// ---- +// f(bool): true -> 0x1234ab +// f(bool): false -> 0x1234abcd1234 + diff --git a/test/libsolidity/semanticTests/various/tuples.sol b/test/libsolidity/semanticTests/various/tuples.sol new file mode 100644 index 000000000000..00fcd6f9a08b --- /dev/null +++ b/test/libsolidity/semanticTests/various/tuples.sol @@ -0,0 +1,30 @@ +contract C { + uint256[] data; + uint256[] m_c; + + function g() internal returns (uint256 a, uint256 b, uint256[] storage c) { + return (1, 2, data); + } + + function h() external returns (uint256 a, uint256 b) { + return (5, 6); + } + + function f() public returns (uint256) { + data.push(3); + uint256 a; + uint256 b; + (a, b) = this.h(); + if (a != 5 || b != 6) return 1; + uint256[] storage c = m_c; + (a, b, c) = g(); + if (a != 1 || b != 2 || c[0] != 3) return 2; + (a, b) = (b, a); + if (a != 2 || b != 1) return 3; + (a, , b, , ) = (8, 9, 10, 11, 12); + if (a != 8 || b != 10) return 4; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol new file mode 100644 index 000000000000..1ab8586fcc25 --- /dev/null +++ b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol @@ -0,0 +1,26 @@ +contract C { + struct S { + uint256 x; + } + S s; + + function g() internal returns (uint256, S storage, uint256) { + s.x = 7; + return (1, s, 2); + } + + function f() public returns (bool) { + (uint256 x1, S storage y1, uint256 z1) = g(); + if (x1 != 1 || y1.x != 7 || z1 != 2) return false; + (, S storage y2, ) = g(); + if (y2.x != 7) return false; + (uint256 x2, , ) = g(); + if (x2 != 1) return false; + (, , uint256 z2) = g(); + if (z2 != 2) return false; + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol new file mode 100644 index 000000000000..19d11342f3cc --- /dev/null +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -0,0 +1,23 @@ +contract helper { + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public payable returns (uint256 bal) { + uint256 someStackElement = 20; + return h.getBalance.value(amount).gas(1000).value(amount + 3)(); + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol new file mode 100644 index 000000000000..d74a0f7f455d --- /dev/null +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -0,0 +1,22 @@ +contract helper { + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public returns (uint256 bal) { + return h.getBalance.value(amount).gas(1000).value(amount + 3)(); // overwrite value + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/write_storage_external.sol b/test/libsolidity/semanticTests/various/write_storage_external.sol new file mode 100644 index 000000000000..0bbe522484da --- /dev/null +++ b/test/libsolidity/semanticTests/various/write_storage_external.sol @@ -0,0 +1,40 @@ +contract C { + uint256 public x; + + function f(uint256 y) public payable { + x = y; + } + + function g(uint256 y) external { + x = y; + } + + function h() public { + this.g(12); + } +} + + +contract D { + C c = new C(); + + function f() public payable returns (uint256) { + c.g(3); + return c.x(); + } + + function g() public returns (uint256) { + c.g(8); + return c.x(); + } + + function h() public returns (uint256) { + c.h(); + return c.x(); + } +} + +// ---- +// f() -> 3 +// g() -> 8 +// h() -> 12 diff --git a/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol b/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol new file mode 100644 index 000000000000..ff9c03cbe857 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][] memory a = new uint[][](2); + for (uint i = 0; i < 2; ++i) + a[i] = new uint[](3); + a[1][1] = n; + uint[] memory b = a[1]; + return b[1]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_2d_new.sol b/test/libsolidity/semanticTests/viaYul/array_2d_new.sol new file mode 100644 index 000000000000..bfc8b201363c --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_2d_new.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][] memory a = new uint[][](2); + for (uint i = 0; i < 2; ++i) + a[i] = new uint[](3); + return a[0][0] = n; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol b/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol new file mode 100644 index 000000000000..a2b3de54f66c --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][][] memory a = new uint[][][](2); + for (uint i = 0; i < 2; ++i) + { + a[i] = new uint[][](3); + for (uint j = 0; j < 3; ++j) + a[i][j] = new uint[](4); + } + a[1][1][1] = n; + uint[][] memory b = a[1]; + uint[] memory c = b[1]; + return c[1]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_3d_new.sol b/test/libsolidity/semanticTests/viaYul/array_3d_new.sol new file mode 100644 index 000000000000..c3ff22ca351f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_3d_new.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][][] memory a = new uint[][][](2); + for (uint i = 0; i < 2; ++i) + { + a[i] = new uint[][](3); + for (uint j = 0; j < 3; ++j) + a[i][j] = new uint[](4); + } + return a[1][1][1] = n; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol new file mode 100644 index 000000000000..5344ae67fe47 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol @@ -0,0 +1,29 @@ +contract C { + function f(uint n, uint m) public { + function() internal returns (uint)[] memory arr = new function() internal returns (uint)[](n); + arr[m](); + } + function f2(uint n, uint m, uint a, uint b) public { + function() internal returns (uint)[][] memory arr = new function() internal returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() internal returns (uint)[](m); + arr[a][b](); + } + function g(uint n, uint m) public { + function() external returns (uint)[] memory arr = new function() external returns (uint)[](n); + arr[m](); + } + function g2(uint n, uint m, uint a, uint b) public { + function() external returns (uint)[][] memory arr = new function() external returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() external returns (uint)[](m); + arr[a][b](); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256): 1823621, 12323 -> FAILURE +// f2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE +// g(uint256,uint256): 1823621, 12323 -> FAILURE +// g2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol new file mode 100644 index 000000000000..aa1349cb82a0 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol @@ -0,0 +1,24 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m, uint a, uint b) public returns (uint) { + map[s] = 0; + uint[][] memory x = new uint[][](n); + for (uint i = 0; i < n; ++i) + x[i] = new uint[](m); + return x[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 0, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 3, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 3, 3, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol new file mode 100644 index 000000000000..1f490ba59aa1 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m) public returns (uint) { + map[s] = 0; + uint[4][] memory x = new uint[4][](n); + return x[m][0]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 2, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 2, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256): 0x60, 200, 199, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol new file mode 100644 index 000000000000..31dd0a66a60c --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol @@ -0,0 +1,17 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint[3] memory x, uint[2] memory y, uint[] memory z, uint t) { + map[s] = 0; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0, 0, 0, 0, 0, 0xe0, 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol new file mode 100644 index 000000000000..6cf73bd8e870 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol @@ -0,0 +1,19 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint) { + map[s] = 0; + uint[3] memory x; + return x[2]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol new file mode 100644 index 000000000000..1f9e70db7bd4 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint a) public returns (uint) { + map[s] = 0; + uint[] memory x = new uint[](n); + return x[a]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 5, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 4, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol new file mode 100644 index 000000000000..d0e4ee59507e --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol @@ -0,0 +1,45 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[] calldata x, uint256 s, uint256 e) external returns (uint256) { + return uint256[](x[s:e]).length; + } + function f(uint256[] calldata x, uint256 s, uint256 e, uint256 ss, uint256 ee) external returns (uint256) { + return uint256[](x[s:e][ss:ee]).length; + } + function f_s_only(uint256[] calldata x, uint256 s) external returns (uint256) { + return uint256[](x[s:]).length; + } + function f_e_only(uint256[] calldata x, uint256 e) external returns (uint256) { + return uint256[](x[:e]).length; + } + function g(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) { + return uint256[](x[s:e])[idx]; + } + function gg(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) { + return x[s:e][idx]; + } + function gg_s_only(uint256[] calldata x, uint256 s, uint256 idx) external returns (uint256) { + return x[s:][idx]; + } + function gg_e_only(uint256[] calldata x, uint256 e, uint256 idx) external returns (uint256) { + return x[:e][idx]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[],uint256,uint256): 0x60, 2, 4, 5, 1, 2, 3, 4, 5 -> 2 +// f(uint256[],uint256,uint256): 0x60, 2, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// f(uint256[],uint256,uint256): 0x60, 3, 3, 5, 1, 2, 3, 4, 5 -> 0 +// f(uint256[],uint256,uint256): 0x60, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// f(uint256[],uint256,uint256): 0x60, 0, 3, 5, 1, 2, 3, 4, 5 -> 3 +// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 2, 5, 1, 2, 3, 4, 5 -> 1 +// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 4, 5, 1, 2, 3, 4, 5 -> FAILURE +// f_s_only(uint256[],uint256): 0x40, 2, 5, 1, 2, 3, 4, 5 -> 3 +// f_s_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// f_e_only(uint256[],uint256): 0x40, 3, 5, 1, 2, 3, 4, 5 -> 3 +// f_e_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 +// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 +// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol index d8defa17aedb..6340c71c385d 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f(uint256,uint256): 10, 3 -> 1 // f(uint256,uint256): 10, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol index 9164bf06cf88..d93e18d063c5 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f(int256,int256): 10, 3 -> 1 // f(int256,int256): 10, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/function_pointers.sol b/test/libsolidity/semanticTests/viaYul/function_pointers.sol new file mode 100644 index 000000000000..5bd433a3d2a4 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/function_pointers.sol @@ -0,0 +1,25 @@ +contract C { + function f() public { + function() internal returns (uint) _f; + _f(); + } + function g() public { + function() external returns (uint) _g; + _g(); + } + function h1() internal returns (function() internal returns (uint) _h) {} + function h2() public { + h1()(); + } + function k1() internal returns (function() external returns (uint) _k) {} + function k2() public { + k1()(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE +// g() -> FAILURE +// h2() -> FAILURE +// k2() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/keccak.sol b/test/libsolidity/semanticTests/viaYul/keccak.sol index 887faf9e43fe..58c37ebd6256 100644 --- a/test/libsolidity/semanticTests/viaYul/keccak.sol +++ b/test/libsolidity/semanticTests/viaYul/keccak.sol @@ -8,7 +8,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // keccak1() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 // keccak2() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 diff --git a/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol new file mode 100644 index 000000000000..880522d60178 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol @@ -0,0 +1,10 @@ +contract C { + mapping (string => uint) map; + function set(string memory s) public { + map[s]; + } +} +// ==== +// compileViaYul: true +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> diff --git a/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol new file mode 100644 index 000000000000..d0085dec19f6 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol @@ -0,0 +1,16 @@ +contract C { + uint[] arr1; + uint[][] arr2; + function f() internal returns (uint[] storage ptr1, uint[][] storage ptr2) { + ptr1 = arr1; + ptr2 = arr2; + } + function g() public returns (uint, uint) { + return (arr1.length, arr2.length); + } + +} +// ==== +// compileViaYul: also +// ---- +// g() -> 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/string_format.sol b/test/libsolidity/semanticTests/viaYul/string_format.sol index 2d9d71d7c3b0..98e4d1081949 100644 --- a/test/libsolidity/semanticTests/viaYul/string_format.sol +++ b/test/libsolidity/semanticTests/viaYul/string_format.sol @@ -5,7 +5,7 @@ contract C { function h() external pure returns (bytes4) { return 0xcafecafe; } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f1() -> 0x20, 6, left(0x616263616263) // f2() -> 32, 47, 44048183223289766195424279195050628400112610419087780792899004030957505095210, 18165586057823232067963737336409268114628061002662705707816940456850361417728 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol new file mode 100644 index 000000000000..33d151f82acc --- /dev/null +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol @@ -0,0 +1,22 @@ +contract Base { + function f() public returns (uint256 i) { + return g(); + } + + function g() public virtual returns (uint256 i) { + return 1; + } +} + + +contract Derived is Base { + function g() public override returns (uint256 i) { + return 2; + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 2 +// f() -> 2 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol new file mode 100644 index 000000000000..590294f15e33 --- /dev/null +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol @@ -0,0 +1,32 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } + + function overridden() public virtual returns (uint256 r) { + return 1; + } + + function g() public returns (uint256 r) { + return overridden(); + } +} + + +contract Base is BaseBase(BaseBase.g()) {} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } + + function overridden() public override returns (uint256 r) { + return 2; + } +} + +// ---- +// getA() -> 2 diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol index f49a572cfa47..290d738726c3 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol @@ -6,23 +6,17 @@ contract c { x = x + 1; return x; } - function g(bool a) public returns (bool) { + function g() public returns (bool) { bool b; - if (a) { - x = 0; - b = (f() == 0) && (f() == 0); - assert(x == 1); - assert(!b); - } else { x = 100; - b = (f() > 0) && (f() > 0); + b = f() > 0; assert(x == 102); // Should fail. assert(!b); - } return b; } } // ---- // Warning: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning: (362-372): Assertion violation happens here +// Warning: (202-218): Assertion violation happens here +// Warning: (242-252): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index 79acf49d7628..f6e32d10a8a1 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -19,4 +19,6 @@ contract A is B { } } // ---- +// Warning: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (265-270): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (253-271): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 5b339e0049f1..79f0c03dc02c 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol index c0bf299f8e69..d33247a0e346 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index 4ad9083fa84f..b5428d15b44e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -31,4 +31,7 @@ contract A is B2, B1 { // Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (362-378): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c8861d..b6363f8efaf0 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol index a607a9556d18..c4dbe02191bc 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol @@ -14,3 +14,4 @@ contract A is C { } // ---- // Warning: (148-162): Assertion violation happens here +// Warning: (166-182): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol index ee4504287264..1f002796f38f 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol @@ -9,5 +9,3 @@ contract C { } } // ---- -// Warning: (99-107): Assertion checker does not support recursive function calls. -// Warning: (141-144): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol index 2647e0771d71..dd3924e2971d 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol @@ -2,40 +2,22 @@ pragma experimental SMTChecker; contract C { - uint x; uint y; - uint z; function f() public { - if (x == 1) - x = 2; - else - x = 1; - g(); + if (y != 1) + g(); assert(y == 1); } - function g() public { + function g() internal { y = 1; h(); - assert(z == 1); } - function h() public { - z = 1; - x = 1; + function h() internal { f(); - // This fails for the following calls to the contract: - // h() - // g() h() - // It does not fail for f() g() h() because in that case - // h() will not inline f() since it already is in the callstack. - assert(x == 1); + assert(y == 1); } } // ---- -// Warning: (271-274): Assertion checker does not support recursive function calls. -// Warning: (140-143): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here -// Warning: (201-204): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol index d2f8ab1db514..ee24f0ae5687 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol @@ -14,4 +14,3 @@ contract C } // ---- -// Warning: (111-114): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol index d5b83f007aa6..5d3292992ad3 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol @@ -22,5 +22,4 @@ contract C } } // ---- -// Warning: (206-209): Assertion checker does not support recursive function calls. -// Warning: (111-114): Assertion checker does not support recursive function calls. +// Warning: (130-144): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol new file mode 100644 index 000000000000..e17e384ddc7e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + g(); + assert(x == 1); + } + + function g() internal { + assert(x == 2); + --x; + assert(x == 1); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol new file mode 100644 index 000000000000..3f765ecd29f0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + g(); + assert(x == 2); + } + + function g() internal { + assert(x == 3); + --x; + assert(x == 2); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (177-191): Assertion violation happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol new file mode 100644 index 000000000000..0d2a3c79476f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 1); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 0); + ++x; + f(); + assert(x == 0); + } +} +// ---- +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol new file mode 100644 index 000000000000..da8dec6a204b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 2); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 1); + ++x; + f(); + assert(x == 1); + } +} +// ---- +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (155-169): Assertion violation happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol new file mode 100644 index 000000000000..b052f5070870 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + ++x; + g(); + g(); + assert(x == 1); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol new file mode 100644 index 000000000000..cb50a931e699 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + ++x; + g(); + g(); + assert(x == 3); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (191-205): Assertion violation happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol new file mode 100644 index 000000000000..5e69f7830b2d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function g(uint y) public { + uint z = L.f(y); + assert(z == y); + } +} + +library L { + function f(uint x) internal returns (uint) { + return x; + } +} + +// ---- +// Warning: (131-190): Function state mutability can be restricted to pure +// Warning: (86-87): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol index 17cf6ea64e9b..0c2a781fca9d 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol @@ -9,4 +9,3 @@ contract C { } // // ---- -// Warning: (126-129): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol index 22b956584aa4..00f78df3cc6c 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol @@ -25,4 +25,3 @@ a; // ---- // Warning: (72-90): Statement has no effect. // Warning: (96-107): Statement has no effect. -// Warning: (304-307): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c8861d..b6363f8efaf0 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index 82d411e47b8d..7be0676d2c72 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -24,4 +24,10 @@ contract A is B { // ---- // Warning: (171-176): Underflow (resulting value less than 0) happens here // Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (260-265): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-287): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-291): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (308-313): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (296-314): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index fc6643a4292a..ff316ce477e9 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -23,4 +23,9 @@ contract A is B { // ---- // Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (231-236): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (283-289): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (306-311): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (294-312): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol index 91798ec0cbc0..36c8ce55186a 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol @@ -13,5 +13,3 @@ contract C { } } // ---- -// Warning: (162-175): Assertion violation happens here -// Warning: (179-193): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol index 1fb380649f38..6303158f7ef5 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol @@ -1,5 +1,8 @@ pragma experimental SMTChecker; +// This test gets different results on Linux and OSX. +// Re-enable when fixed (SMTSolvers: z3) + contract Simple { function f() public pure { uint x = 10; @@ -16,9 +19,7 @@ contract Simple { } } // ==== -// SMTSolvers: z3 +// SMTSolvers: none // ---- -// Warning: (172-187): Error trying to invoke SMT solver. // Warning: (195-209): Error trying to invoke SMT solver. -// Warning: (172-187): Assertion violation happens here // Warning: (195-209): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol index e7fde4d5aa28..ea1c54851e1a 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol @@ -12,10 +12,4 @@ contract Simple { assert(y == x); } } -// ==== -// SMTSolvers: z3 // ---- -// Warning: (164-179): Error trying to invoke SMT solver. -// Warning: (187-201): Error trying to invoke SMT solver. -// Warning: (164-179): Assertion violation happens here -// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol index 7f73f86c10a5..4b97a7ea0b4e 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol @@ -5,10 +5,6 @@ contract C function f(uint x, bool b) public pure { require(x < 10); for (; x < 10; ) { - if (b) { - x = 20; - continue; - } ++x; } assert(x > 15); @@ -17,4 +13,5 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning: (185-199): Assertion violation happens here +// Warning: (66-72): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (142-156): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol index e57c62313b36..11cd22d11840 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol @@ -13,8 +13,6 @@ contract C assert(x > 0); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (296-309): Error trying to invoke SMT solver. // Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index 1a9216de04a9..0694493f52c7 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -14,8 +14,6 @@ contract LoopFor2 { assert(b[0] == 900); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (281-301): Assertion violation happens here // Warning: (305-324): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol index c85e7e3fb1b6..e022e0f299ba 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol @@ -4,6 +4,7 @@ contract C { function f(uint x, uint y) public pure { x = 7; while ((x = y) > 0) { + --y; } assert(x == 7); } @@ -11,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning: (216-230): Assertion violation happens here +// Warning: (224-238): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index e9752b3a8d33..986e7205b764 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -28,8 +28,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (329-344): Assertion violation happens here // Warning: (380-395): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol index 4447cf6d74fd..71238c3be6e5 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol @@ -26,8 +26,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (323-338): Assertion violation happens here // Warning: (362-377): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol index 7a420c28f82c..702c24f4b612 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,4 +21,5 @@ contract C } } // ---- +// Warning: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (136-149): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol index b01fe812e509..16d071fdb4f7 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol @@ -17,6 +17,3 @@ contract C } } // ---- -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (253-266): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol index 9cd7de9e67ba..8a1ba5e6ef90 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol @@ -4,7 +4,7 @@ contract C { uint[][] a; function f(bool b) public { - require(a[2][3] == 4); + a[2][3] = 4; if (b) delete a; else @@ -13,5 +13,7 @@ contract C assert(a[1][1] == 0); } } +// ==== +// SMTSolvers: z3 // ---- -// Warning: (184-204): Assertion violation happens here +// Warning: (174-194): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index e9ef98b26868..a8ae53997a8e 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -20,6 +20,7 @@ contract C // Warning: (165-204): Assertion violation happens here // Warning: (208-240): Assertion violation happens here // Warning: (244-275): Assertion violation happens here +// Warning: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (304-332): Assertion violation happens here // Warning: (336-352): Assertion violation happens here // Warning: (356-379): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 1657a95c6dc9..abee678c0503 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -1,13 +1,23 @@ pragma experimental SMTChecker; contract C { + function(uint) returns (uint) a; + function(uint) returns (uint) b; function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { assert(g(2) == h(2)); assert(g == h); } + function g() public { + f(a, b); + } } // ---- -// Warning: (146-150): Assertion checker does not yet implement this type of function call. -// Warning: (154-158): Assertion checker does not yet implement this type of function call. -// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons -// Warning: (139-159): Assertion violation happens here -// Warning: (163-177): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index b2701fdf9cfc..d94f3204b9ef 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -10,5 +10,4 @@ contract B { } // ---- // Warning: (162-184): Assertion violation happens here -// Warning: (136-158): Assertion violation happens here // Warning: (162-184): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol index 8cd4686529d3..00d2965bc9f2 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -1,4 +1,5 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { @@ -8,24 +9,21 @@ contract C uint[][] memory cc, uint8[][] memory dd, uint[][][] memory eee - ) internal pure { - require(a[0] == 2); - require(cc[0][0] == 50); - require(dd[0][0] == 10); - require(eee[0][0][0] == 50); + ) public pure { + a[0] = 2; + cc[0][0] = 50; + dd[0][0] = 10; + eee[0][0][0] = 50; b[0] = 1; - // Fails because b == a is possible. - assert(a[0] == 2); - // Fails because b == cc[0] is possible. - assert(cc[0][0] == 50); + // Fails because + // b == a is possible + // b == cc[0] is possible + // b == ee[0][0] is possible + assert(a[0] == 2 || cc[0][0] == 50 || eee[0][0][0] == 50); // Should not fail since knowledge is erased only for uint[]. assert(dd[0][0] == 10); - // Fails because b == ee[0][0] is possible. - assert(eee[0][0][0] == 50); assert(b[0] == 1); } } // ---- -// Warning: (345-362): Assertion violation happens here -// Warning: (409-431): Assertion violation happens here -// Warning: (571-597): Assertion violation happens here +// Warning: (400-457): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol index d015d147173b..94e7ccfcf633 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol @@ -1,11 +1,12 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); - require(a[0] == 2); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (314-331): Assertion violation happens here +// Warning: (321-338): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol index f6d6b1977d66..1ae2b935613a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol @@ -1,12 +1,13 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; uint[] storage c = array; - require(a[0] == 2); + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -19,4 +20,4 @@ contract C } } // ---- -// Warning: (469-486): Assertion violation happens here +// Warning: (476-493): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol index 6461634e1161..2aacc5c0d9fa 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol @@ -4,7 +4,9 @@ contract C { uint[] array; uint[][] array2d; + uint[][][][] array4d; uint8[] tinyArray; + uint8[][][] tinyArray3d; function f( uint[] storage a, uint[] storage b, @@ -12,13 +14,13 @@ contract C uint8[][] storage dd, uint[][][] storage eee ) internal { - require(a[0] == 2); - require(array[0] == 42); - require(array2d[0][0] == 42); - require(tinyArray[0] == 42); - require(cc[0][0] == 42); - require(dd[0][0] == 42); - require(eee[0][0][0] == 42); + a[0] = 2; + array[0] = 42; + array2d[0][0] = 42; + tinyArray[0] = 42; + cc[0][0] = 42; + dd[0][0] = 42; + eee[0][0][0] = 42; b[0] = 1; // Fails because b == a is possible. assert(a[0] == 2); @@ -36,10 +38,19 @@ contract C assert(eee[0][0][0] == 42); assert(b[0] == 1); } + + function g(uint a, uint b, uint c, uint d, uint e) public { + f(array2d[a], array2d[b], array4d[c][c], tinyArray3d[d], array4d[e]); + } } // ---- -// Warning: (489-506): Assertion violation happens here -// Warning: (553-575): Assertion violation happens here -// Warning: (627-654): Assertion violation happens here -// Warning: (795-817): Assertion violation happens here -// Warning: (957-983): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol index 4b134e58e333..3d60c836a896 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol @@ -2,9 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } + function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -15,4 +20,5 @@ contract C } } // ---- -// Warning: (347-364): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol index d70cc18bbf64..68438eb6dcbe 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol @@ -2,10 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { uint[] memory d = c; - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -19,4 +23,7 @@ contract C } } // ---- -// Warning: (497-514): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol index 13dc6fcc2ba3..384a89b4fe6e 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol @@ -3,9 +3,10 @@ pragma experimental SMTChecker; contract C { uint[] array; + uint[][] array2d; function f(uint[] storage a, uint[] storage b) internal { - require(a[0] == 2); - require(b[0] == 42); + a[0] = 2; + b[0] = 42; array[0] = 1; // Fails because array == a is possible. assert(a[0] == 2); @@ -13,7 +14,12 @@ contract C assert(b[0] == 42); assert(array[0] == 1); } + function g(uint x, uint y) public { + f(array2d[x], array2d[y]); + } } // ---- -// Warning: (226-243): Assertion violation happens here -// Warning: (290-308): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index ff47cc0e469d..e94ad4580419 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -4,20 +4,29 @@ contract C { uint[] b; uint[] d; + uint[][] array2d; + function g(uint x, uint[] memory c) public { + f(array2d[x], c); + } function f(uint[] storage a, uint[] memory c) internal { - require(d[0] == 42); - require(c[0] == 42); - require(a[0] == 2); + d[0] = 42; + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Should not fail since b == d is not possible. + // Fails because d == a is possible. assert(d[0] == 42); - // Fails because b == a is possible. + // Fails because b == a and d == a are possible. assert(a[0] == 2); + // b == a is possible, but does not fail because b + // was the last assignment. assert(b[0] == 1); } } // ---- -// Warning: (446-463): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol index 062e9776d335..dc69dd55aa29 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[] severalMaps8; mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (451-482): Assertion violation happens here -// Warning: (665-701): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol index 1d5ab2687e9b..2e16e7b5da3c 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (807-827): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol index 99e993032efc..dd846b483a3a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol @@ -2,7 +2,7 @@ pragma experimental SMTChecker; contract C { - function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) internal pure { + function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) public pure { require(c[0] == 42); require(a[0] == 2); b[0] = 1; @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning: (230-248): Assertion violation happens here -// Warning: (295-312): Assertion violation happens here +// Warning: (228-246): Assertion violation happens here +// Warning: (293-310): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol index fa0c5eae1384..45c12263be79 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol @@ -2,18 +2,25 @@ pragma experimental SMTChecker; contract C { - uint[2] b; + uint[2] b1; + uint[2] b2; function f(uint[2] storage a, uint[2] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); - b[0] = 1; + c[0] = 42; + a[0] = 2; + b1[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Fails because b == a is possible. + // Fails because b1 == a is possible. assert(a[0] == 2); - assert(b[0] == 1); + assert(b1[0] == 1); + } + function g(bool x, uint[2] memory c) public { + if (x) f(b1, c); + else f(b2, c); } } // ---- -// Warning: (342-359): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol index a675c29e231c..7e31b10ae6a6 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[2] severalMaps8; mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (455-486): Assertion violation happens here -// Warning: (669-705): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol index c9c96a0a7230..e2f597943df3 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps3d[x][0]); + } } // ---- -// Warning: (811-831): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol index a1648c66e462..9c1c3caaa969 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol @@ -8,7 +8,7 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Left; assert(_d == E.Left); } diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol index 2bd2afbda622..be1d40be4036 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol @@ -8,10 +8,10 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Right; assert(_d == E.Left); } } // ---- -// Warning: (161-181): Assertion violation happens here +// Warning: (159-179): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol index 47bdd0c66a0d..4821518fdc2b 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol @@ -1,19 +1,20 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { enum D { Left, Right } struct S { uint x; D d; } - function f(S memory s) internal pure { + function f(S memory s) public pure { s.d = D.Left; assert(s.d == D.Left); } } // ---- -// Warning: (109-119): Assertion checker does not yet support the type of this variable. -// Warning: (139-142): Assertion checker does not yet support this expression. -// Warning: (139-140): Assertion checker does not yet implement type struct C.S memory -// Warning: (139-151): Assertion checker does not yet implement such assignments. -// Warning: (162-165): Assertion checker does not yet support this expression. -// Warning: (162-163): Assertion checker does not yet implement type struct C.S memory -// Warning: (155-176): Assertion violation happens here +// Warning: (143-153): Assertion checker does not yet support the type of this variable. +// Warning: (171-174): Assertion checker does not yet support this expression. +// Warning: (171-172): Assertion checker does not yet implement type struct C.S memory +// Warning: (171-183): Assertion checker does not yet implement such assignments. +// Warning: (194-197): Assertion checker does not yet support this expression. +// Warning: (194-195): Assertion checker does not yet implement type struct C.S memory +// Warning: (187-208): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol new file mode 100644 index 000000000000..bb2513790959 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C +{ + bytes32 x; + function f(bytes8 y) public view { + assert(x == g()); + assert(x != y); + } + function g() public view returns (bytes32) { + return x; + } +} +// ---- +// Warning: (116-130): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol index 39d096f5904c..8170d67e6200 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol @@ -5,8 +5,9 @@ contract C mapping (uint => uint) a; mapping (uint => uint) b; - function f() public { - require(a[1] == b[1]); + function f(uint x) public { + a[1] = x; + b[1] = x; a[1] = 2; mapping (uint => uint) storage c = a; assert(c[1] == 2); @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (261-281): Assertion violation happens here +// Warning: (266-286): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol index 86af187ad75e..f31192b3a71e 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol @@ -6,10 +6,10 @@ contract C mapping (uint => mapping (uint => uint)) maps; mapping (uint => mapping (uint => uint8)) maps8; function f(mapping (uint => uint) storage map1, mapping (uint => uint) storage map2) internal { - require(map1[0] == 2); - require(a[0] == 42); - require(maps[0][0] == 42); - require(maps8[0][0] == 42); + map1[0] = 2; + a[0] = 42; + maps[0][0] = 42; + maps8[0][0] = 42; map2[0] = 1; // Fails because map2 == map1 is possible. assert(map1[0] == 2); @@ -21,8 +21,21 @@ contract C assert(maps8[0][0] == 42); assert(map2[0] == 1); } + + function g(bool b, uint x, uint y) public { + if (b) + f(a, maps[y]); + else + f(maps[x], maps[y]); + } } // ---- -// Warning: (437-457): Assertion violation happens here -// Warning: (503-521): Assertion violation happens here -// Warning: (573-597): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 4a9291f89308..afb9a9ea0ac1 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -11,3 +11,4 @@ contract C { } // ---- // Warning: (147-166): Assertion violation happens here +// Warning: (170-190): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function.sol b/test/libsolidity/smtCheckerTests/types/tuple_function.sol index 53b197395447..8103a02038c7 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function.sol @@ -15,3 +15,4 @@ contract C } // ---- // Warning: (182-196): Assertion violation happens here +// Warning: (200-214): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol index 1e548403d0e9..e76db56d1245 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol @@ -17,3 +17,4 @@ contract C } // ---- // Warning: (205-219): Assertion violation happens here +// Warning: (223-237): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.json b/test/libsolidity/smtCheckerTestsJSON/multi.json index b125712fd310..b1be750736c5 100644 --- a/test/libsolidity/smtCheckerTestsJSON/multi.json +++ b/test/libsolidity/smtCheckerTestsJSON/multi.json @@ -3,7 +3,7 @@ { "smtlib2responses": { - "0x82fb8ee094f0f56b7a63a74177b54a1710d6fc531d426f288c18f36b76cf6a8b": "sat\n((|EVALEXPR_0| 1))\n", + "0x9c50514d749eabf3c13d97ad7d787e682dd99a114bad652b10a01b8c6ad6c1fb": "sat\n((|EVALEXPR_0| 1))\n", "0xb524e7c577188e2e36f0e67fead51269fa0f8b8fb41bff2d973dcf584d38cd1e": "sat\n((|EVALEXPR_0| 0))\n" } } diff --git a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol index f3be9071501e..4966b4e858fa 100644 --- a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol +++ b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol @@ -2,8 +2,10 @@ contract C { function f() public { uint[] storage x; uint[10] storage y; + x; + y; } } // ---- -// DeclarationError: (38-54): Uninitialized storage pointer. -// DeclarationError: (58-76): Uninitialized storage pointer. +// TypeError: (80-81): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (85-86): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol new file mode 100644 index 000000000000..ddb5faa9fa2b --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol @@ -0,0 +1,38 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for {} eq(0,0) { c_slot := s_slot } {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for {} eq(0,1) { c_slot := s_slot } {} + } + c; + } + function h() internal pure { + S storage c; + assembly { + for {} eq(0,0) {} { c_slot := s_slot } + } + c; + } + function i() internal pure { + S storage c; + assembly { + for {} eq(0,1) {} { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (189-190): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (340-341): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (491-492): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (642-643): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. + diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol new file mode 100644 index 000000000000..19a58b2349bf --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol @@ -0,0 +1,19 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(0) {} {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(1) {} {} + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol new file mode 100644 index 000000000000..1d4302755f79 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal pure { + S storage c; + assembly { + if flag { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (188-189): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol new file mode 100644 index 000000000000..180feb770be2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this should warn about unreachable code, but currently function flow is ignored + assembly { + function f() { return(0, 0) } + f() + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol new file mode 100644 index 000000000000..bba9daf34627 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this could be allowed, but currently control flow for functions is not analysed + assembly { + function f() { revert(0, 0) } + f() + } + c; + } +} +// ---- +// TypeError: (287-288): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol new file mode 100644 index 000000000000..c3f2409158ea --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol @@ -0,0 +1,12 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol new file mode 100644 index 000000000000..72884e74159e --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol @@ -0,0 +1,33 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + case 1 { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + default { return(0,0) } + } + c; + } +} +// ---- +// TypeError: (208-209): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (421-422): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol new file mode 100644 index 000000000000..d723109244a6 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol @@ -0,0 +1,31 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + default { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + default { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { revert(0, 0) } + default { c_slot := s_slot } + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol new file mode 100644 index 000000000000..25654b1dda41 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol @@ -0,0 +1,66 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do { + break; + c = s; + } while(false); + c; + } + function g() internal view { + S storage c; + do { + if (s.f) { + continue; + c = s; + } + else { + } + } while(false); + c; + } + function h() internal view { + S storage c; + do { + if (s.f) { + break; + } + else { + c = s; + } + } while(false); + c; + } + function i() internal view { + S storage c; + do { + if (s.f) { + continue; + } + else { + c = s; + } + } while(false); + c; + } + function j() internal view { + S storage c; + do { + continue; + c = s; + } while(false); + c; + } +} +// ---- +// TypeError: (184-185): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (145-150): Unreachable code. +// Warning: (168-173): Unreachable code. +// TypeError: (411-412): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (325-330): Unreachable code. +// TypeError: (635-636): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (862-863): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (1011-1012): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (972-977): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol new file mode 100644 index 000000000000..40ddc377773a --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol @@ -0,0 +1,44 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do {} while((c = s).f); + c; + } + function g() internal view { + S storage c; + do { c = s; } while(false); + c; + } + function h() internal view { + S storage c; + c = s; + do {} while(false); + c; + } + function i() internal view { + S storage c; + do {} while(false); + c = s; + c; + } + function j() internal view { + S storage c; + do { + c = s; + break; + } while(false); + c; + } + function k() internal view { + S storage c; + do { + c = s; + continue; + } while(false); + c; + } +} +// ---- +// Warning: (606-611): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol new file mode 100644 index 000000000000..e93d9798f215 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(;; c = s) { + } + c; + } + function g() internal view { + S storage c; + for(;;) { + c = s; + } + c; + } +} +// ---- +//TypeError: (143-144): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (261-262): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol new file mode 100644 index 000000000000..b3e9f46d1468 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(c = s;;) { + } + c; + } + function g() internal view { + S storage c; + for(; (c = s).f;) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol new file mode 100644 index 000000000000..7da6bef92e69 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol @@ -0,0 +1,22 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal { + S storage c; + if (flag) c = s; + c; + } + function g(bool flag) internal { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else s.f = true; + } + c; + } +} +// ---- +// TypeError: (138-139): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (330-331): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol new file mode 100644 index 000000000000..5d3a6d5bd52a --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol @@ -0,0 +1,39 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + if (flag) c = s; + else c = s; + c; + } + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else { c = s; } + c; + } + function h(bool flag) internal view { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else c = s; + } + c; + } + function i() internal view { + S storage c; + if ((c = s).f) { + } + c; + } + function j() internal view { + S storage c; + if ((c = s).f && !(c = s).f) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol new file mode 100644 index 000000000000..6df4939cf4dc --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + modifier revertIfNoReturn() { + _; + revert(); + } + modifier ifFlag(bool flag) { + if (flag) + _; + } + struct S { uint a; } + S s; + function f(bool flag) revertIfNoReturn() internal view { + if (flag) s; + } + function g(bool flag) revertIfNoReturn() ifFlag(flag) internal view { + s; + } + +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol new file mode 100644 index 000000000000..5cb580528409 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol @@ -0,0 +1,11 @@ +contract C { + struct S { bool f; } + S s; + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else revert(); + s; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol new file mode 100644 index 000000000000..a6dd300cedad --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol @@ -0,0 +1,24 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + false && (c = s).f; + c; + } + function g() internal view { + S storage c; + true || (c = s).f; + c; + } + function h() internal view { + S storage c; + // expect error, although this is always fine + true && (false || (c = s).f); + c; + } +} +// ---- +// TypeError: (137-138): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (235-236): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (398-399): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol new file mode 100644 index 000000000000..e8ad5e4853f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + (c = s).f && false; + c; + } + function g() internal view { + S storage c; + (c = s).f || true; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol new file mode 100644 index 000000000000..f820456e18ee --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure {} + function g() internal view { s; } + function h() internal view { + S storage c; + c = s; + c; + } + function i() internal view { + S storage c; + (c) = s; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol new file mode 100644 index 000000000000..2c47731aaceb --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol @@ -0,0 +1,11 @@ +contract C { + uint256[] s; + function f() public { + bool d; + uint256[] storage x; + uint256[] storage y = d ? (x = s) : x; + y; + } +} +// ---- +// TypeError: (145-146): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol new file mode 100644 index 000000000000..a510bd40dada --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol @@ -0,0 +1,9 @@ +contract C { + uint256[] s; + function f() public view { + uint256[] storage x; + uint256[] storage y = (x = s)[0] > 0 ? x : x; + y; + } +} +// --- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol new file mode 100644 index 000000000000..3b4552f86fef --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? (c = s).f : false; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? false : (c = s).f; + c; + } +} +// ---- +// TypeError: (152-153): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (266-267): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol new file mode 100644 index 000000000000..1dd8d2f5745e --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? c = s : c = s; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? c = s : (c = s); + c; + } + function h(bool flag) internal view { + S storage c; + flag ? (c = s).f : (c = s).f; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol new file mode 100644 index 000000000000..ca43dc346fc6 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol @@ -0,0 +1,42 @@ +contract C { + struct S { bool f; } + S s; + function ext() external {} + function f() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { } + r; + } + function h() internal + { + S storage r; + try this.ext() {} + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function i() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r; } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError: (206-207): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (343-344): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (526-527): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (653-654): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol new file mode 100644 index 000000000000..6834525d5c01 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol @@ -0,0 +1,30 @@ +contract C { + struct S { bool f; } + S s; + function ext() external { } + function f() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function h() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol new file mode 100644 index 000000000000..e2e27419638a --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage, uint) { + return (s,2); + } + function g() internal view { + uint a; + S storage c; + (c, a) = f(); + c; + } + function h() internal view { + (s, s); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol new file mode 100644 index 000000000000..cd6fee8dbfa8 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while(false) { + c = s; + } + c; + } +} +// ---- +// TypeError: (161-162): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol new file mode 100644 index 000000000000..20e72e5dbcb9 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol @@ -0,0 +1,25 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while((c = s).f) { + } + c; + } + function g() internal view { + S storage c; + c = s; + while(false) { + } + c; + } + function h() internal view { + S storage c; + while(false) { + } + c = s; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/as_function_param.sol b/test/libsolidity/syntaxTests/immutable/as_function_param.sol new file mode 100644 index 000000000000..3636b0aef679 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/as_function_param.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint immutable) public pure {} +} +// ---- +// DeclarationError: (28-42): The "immutable" keyword can only be used for state variables. diff --git a/test/libsolidity/syntaxTests/immutable/assembly.sol b/test/libsolidity/syntaxTests/immutable/assembly.sol new file mode 100644 index 000000000000..7b98b67c66df --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/assembly.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + function f() public view { + uint t; + assembly { + t := x + } + } +} +// ---- +// TypeError: (118-119): Assembly access to immutable variables is not supported. diff --git a/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol new file mode 100644 index 000000000000..702f0307981d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + return; + + x = 1; + } +} +// ---- +// TypeError: (93-100): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol new file mode 100644 index 000000000000..b8557321b9ef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + x = 1; + } +} +// ---- +// TypeError: (93-94): Immutable variables must be initialized unconditionally, not in an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol new file mode 100644 index 000000000000..1423659c8912 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + initX(); + } + + function initX() internal { + x = 3; + } +} +// ---- +// TypeError: (126-127): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol new file mode 100644 index 000000000000..ad17635c4311 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + x = f(); + } + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (143-144): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol new file mode 100644 index 000000000000..0b2207808434 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x; + constructor() public { + x = 3 + x; + } +} +// ---- +// TypeError: (78-79): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol new file mode 100644 index 000000000000..f3b724f0324a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() public { + (x, y) = f(); + } + + function f() internal pure returns(uint _x, uint _y) { + _x = 3; + _y = 4; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol new file mode 100644 index 000000000000..2c1c37c79b22 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() readX(x = 3) public { } + + modifier readX(uint _x) { + _; f(_x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol new file mode 100644 index 000000000000..b266665308bc --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() initX public { + } + + modifier initX() { + _; x = 23; + } +} +// ---- +// TypeError: (109-110): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol new file mode 100644 index 000000000000..a9a1b3a225e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol @@ -0,0 +1,14 @@ +contract C { + uint immutable x; + constructor() readX public { + x = 3; + } + + modifier readX() { + _; f(x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (126-127): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol new file mode 100644 index 000000000000..54ac9f6158da --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x--; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol new file mode 100644 index 000000000000..def28fad2814 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + delete x; + } +} +// ---- +// TypeError: (81-82): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/double_specifier.sol b/test/libsolidity/syntaxTests/immutable/double_specifier.sol new file mode 100644 index 000000000000..39f0f1e76e64 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/double_specifier.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable immutable x; + uint immutable constant x; +} +// ---- +// ParserError: (32-41): Constantness already set to "immutable" +// ParserError: (64-72): Constantness already set to "immutable" diff --git a/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol new file mode 100644 index 000000000000..7ab8393cf42f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol @@ -0,0 +1,5 @@ +contract C { + function() external immutable f; +} +// ---- +// TypeError: (17-48): Immutable variables of external function type are not yet supported. diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization.sol b/test/libsolidity/syntaxTests/immutable/function_initialization.sol new file mode 100644 index 000000000000..c836beb27b08 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3; } +} diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol new file mode 100644 index 000000000000..2a4365e69ffe --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (99-100): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol new file mode 100644 index 000000000000..04eaa621ddec --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x = 2; } +} +// ---- +// TypeError: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (200-201): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol new file mode 100644 index 000000000000..2ec62e93102b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x + 2; } +} +// ---- +// TypeError: (200-201): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol new file mode 100644 index 000000000000..3d8e04636279 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol @@ -0,0 +1,3 @@ +contract C { + uint immutable x = 0; +} diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol new file mode 100644 index 000000000000..0068bf87d77d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x++; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol new file mode 100644 index 000000000000..5f659bff6a08 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = f(); + + function f() internal returns(uint) { return x; } +} +// ---- +// TypeError: (107-108): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol new file mode 100644 index 000000000000..ee794fd37156 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol @@ -0,0 +1,15 @@ +contract B { + uint immutable x; + + constructor() public { + x = 3; + } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol new file mode 100644 index 000000000000..0d98f8b40122 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B { + uint immutable y; + constructor() B(y = 3) public { } +} +// ---- +// TypeError: (155-156): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol new file mode 100644 index 000000000000..16ed52f36bef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y = 3) { + uint immutable y; +} +// ---- +// TypeError: (111-114): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol new file mode 100644 index 000000000000..ed352746f7f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol @@ -0,0 +1,16 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y) { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- +// TypeError: (111-114): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol new file mode 100644 index 000000000000..d56fb487ad44 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return 3; + } +} + +contract C is B { + function xInit() internal override returns(uint) { + return x; + } +} +// ---- +// TypeError: (260-261): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol new file mode 100644 index 000000000000..5dda6bfb00dd --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + B.readX; + } + + function readX() internal override returns(uint) { + return 3; + } +} +// ---- +// TypeError: (109-110): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol new file mode 100644 index 000000000000..278efe55ff45 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal view virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + super.readX(); + } + + function readX() internal view override returns(uint) { + return 1; + } +} +// ---- +// TypeError: (114-115): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol new file mode 100644 index 000000000000..f1e60dbd822d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable x; + + constructor() readX public { + x = 3; + } + + modifier readX() virtual { + _; f(3); + } + + function f(uint a) internal pure {} +} + +contract C is B { + modifier readX() override { + _; f(x); + } +} +// ---- +// TypeError: (252-253): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol new file mode 100644 index 000000000000..24a5239ed849 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -0,0 +1,12 @@ +contract B { + uint immutable x = 4; +} + +contract C is B { + constructor() public { + x = 3; + } +} +// ---- +// TypeError: (95-96): Immutable variables must be initialized in the constructor of the contract they are defined in. +// TypeError: (95-96): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol new file mode 100644 index 000000000000..a6db6a6650da --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol @@ -0,0 +1,8 @@ +contract C { + constructor() public { + return; + } + + uint immutable x = 3; +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol new file mode 100644 index 000000000000..2827ab38972d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + while (true) + x = 1; + } +} +// ---- +// TypeError: (95-96): Immutable variables can only be initialized once, not in a while statement. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol new file mode 100644 index 000000000000..c71335b839ad --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return B.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (496-497): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol new file mode 100644 index 000000000000..eaabbb71a261 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return super.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (500-501): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol new file mode 100644 index 000000000000..8ff940fefe00 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + x = 1; + x = 4; + } +} +// ---- +// TypeError: (85-86): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/non-value_type.sol b/test/libsolidity/syntaxTests/immutable/non-value_type.sol new file mode 100644 index 000000000000..67398ce20455 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/non-value_type.sol @@ -0,0 +1,5 @@ +contract C { + uint[] immutable x; +} +// ---- +// TypeError: (17-35): Immutable variables cannot have a non-value type. diff --git a/test/libsolidity/syntaxTests/immutable/private_state_var.sol b/test/libsolidity/syntaxTests/immutable/private_state_var.sol new file mode 100644 index 000000000000..2cfc22a85317 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/private_state_var.sol @@ -0,0 +1,20 @@ +contract B { + uint immutable private x = f(); + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (209-210): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol new file mode 100644 index 000000000000..b64f236a972b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() internal { + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol new file mode 100644 index 000000000000..4de778ba3e74 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() readX internal { + } + + modifier readX() { + _; + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol new file mode 100644 index 000000000000..d35c68d8a863 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol @@ -0,0 +1,6 @@ +contract C { + uint immutable x = 0; + uint y = x; +} +// ---- +// TypeError: (52-53): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol new file mode 100644 index 000000000000..0fd5461d4b3d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + return; + + x = 1; + } +} +// ---- +// TypeError: (70-77): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/selector.sol b/test/libsolidity/syntaxTests/immutable/selector.sol new file mode 100644 index 000000000000..4b880c8e8bcb --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + this.readX.selector; + } + + function readX() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (85-89): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_name.sol b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol new file mode 100644 index 000000000000..c84f724089e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + C.selector.selector; + C.selector; + } + + function selector() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (114-124): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol new file mode 100644 index 000000000000..a8b91a639349 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol @@ -0,0 +1,16 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + readX().selector; + } + + function f() external view returns(uint) { + return x; + } + + function readX() public view returns(function() external view returns(uint) _f) { + _f = this.f; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized.sol b/test/libsolidity/syntaxTests/immutable/uninitialized.sol new file mode 100644 index 000000000000..18c60ea9df3d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x; +} +// ---- +// TypeError: (0-36): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol new file mode 100644 index 000000000000..69aa5448f542 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable private x; + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (0-209): Construction control flow ends without initializing all immutable state variables. +// TypeError: (211-375): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol new file mode 100644 index 000000000000..2b5614650666 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 1; + + function readX() internal view returns(uint) { + return x + 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol new file mode 100644 index 000000000000..e844de0800fc --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x = 0; + + function f() internal { + x = 1; + } +} +// ---- +// TypeError: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (76-77): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol new file mode 100644 index 000000000000..592633379c84 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + + function f() readX internal { } + + modifier readX() { + _; x = 1; + } +} +// ---- +// TypeError: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (111-112): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol new file mode 100644 index 000000000000..d02284aeff95 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; + +abstract contract C { + struct S { + uint256 x; + string y; + } + function f(address x) external virtual returns (uint256, string memory); +} +contract D is C { + mapping(address => S) public override f; +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol new file mode 100644 index 000000000000..4e6e02598729 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; + +abstract contract C { + struct S { + uint256 x; + string y; + } + + function f() external virtual returns (uint256, string memory); +} +contract D is C { + S public override f; +} diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol index ecda3e994eaa..625502f25899 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol @@ -5,3 +5,5 @@ contract C { } } } +// ---- +// DeclarationError: (72-73): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol new file mode 100644 index 000000000000..2fc49b1dbab1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure {} + constructor() public { + assembly { + let x := f + } + } +} +// ---- +// DeclarationError: (112-113): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol b/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol new file mode 100644 index 000000000000..fbd5ab6ee0a4 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol @@ -0,0 +1,12 @@ +contract C { + bool nc = false; + bool constant c = nc; + function f() public { + assembly { + let t := c + } + } +} +// ---- +// TypeError: (52-54): Initial value for constant variable has to be compile-time constant. +// TypeError: (112-113): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol b/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol new file mode 100644 index 000000000000..563d47851a9d --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol @@ -0,0 +1,12 @@ +contract C { + bool constant c = this; + function f() public { + assembly { + let t := c + } + } +} +// ---- +// TypeError: (33-37): Type contract C is not implicitly convertible to expected type bool. +// TypeError: (33-37): Initial value for constant variable has to be compile-time constant. +// TypeError: (95-96): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol index edae7549dd82..c43dbad6d0b8 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// DeclarationError: (52-85): Uninitialized storage pointer. +// TypeError: (95-96): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol index a0b6f71e7d0c..68e3b2c44861 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// DeclarationError: (84-95): Uninitialized storage pointer. +// TypeError: (105-106): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol new file mode 100644 index 000000000000..b94334d38770 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol @@ -0,0 +1,6 @@ +interface I { + function f() external m pure returns (uint); + modifier m() { _; } +} +// ---- +// SyntaxError: (16-60): Functions in interfaces cannot have modifiers. diff --git a/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol new file mode 100644 index 000000000000..7197a4edb719 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol @@ -0,0 +1,8 @@ +abstract contract C { + /// @param id Some identifier + /// @return value Some value + /// @return value2 Some value 2 + function vote(uint id) public virtual returns (uint value); +} +// ---- +// DocstringParsingError: (26-121): Documentation tag "@return value2 Some value 2" exceeds the number of return parameters. diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol index 4c1f96e6e516..9035caa25703 100644 --- a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol @@ -3,4 +3,3 @@ contract c { } // ---- // TypeError: (39-58): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer. -// DeclarationError: (60-83): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol new file mode 100644 index 000000000000..50f0eb365dc0 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol @@ -0,0 +1,7 @@ +contract C { + struct S {t t;} + function f(function(S memory) external) public {} +} +// ---- +// TypeError: (25-26): Name has to refer to a struct, enum or contract. +// TypeError: (53-61): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/structs/member_type_func.sol b/test/libsolidity/syntaxTests/structs/member_type_func.sol new file mode 100644 index 000000000000..709a40e267d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_func.sol @@ -0,0 +1,8 @@ +contract C { + function f() public {} + struct S {f x;} + function g(function(S memory) external) public {} +} +// ---- +// TypeError: (50-51): Name has to refer to a struct, enum or contract. +// TypeError: (78-86): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol new file mode 100644 index 000000000000..2fa48cfb1b5a --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol @@ -0,0 +1,12 @@ +contract C { + function f() { + (((((((((((,2),)),)),),))=4))); + } +} +// ---- +// SyntaxError: (15-69): No visibility specified. Did you intend to add "public"? +// TypeError: (46-47): Expression has to be an lvalue. +// TypeError: (60-61): Type int_const 4 is not implicitly convertible to expected type tuple(tuple(tuple(tuple(tuple(,int_const 2),),),),). +// TypeError: (37-61): Tuple component cannot be empty. +// TypeError: (36-62): Tuple component cannot be empty. +// TypeError: (35-63): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol new file mode 100644 index 000000000000..2a5d62752ce3 --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + int a; + (((a,),)) = ((1,2),3); + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol new file mode 100644 index 000000000000..9028c7ccc8bc --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol @@ -0,0 +1,8 @@ +contract B { + uint immutable x = 1; + function f() public pure returns (uint) { + return x; + } +} +// ---- +// TypeError: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolutil/IpfsHash.cpp b/test/libsolutil/IpfsHash.cpp index 4514b2f4c0f3..9c4fb56332fd 100644 --- a/test/libsolutil/IpfsHash.cpp +++ b/test/libsolutil/IpfsHash.cpp @@ -60,15 +60,50 @@ BOOST_AUTO_TEST_CASE(test_largest_unchunked) BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbNDspMkzkMFKyS3eCJGedG7GWRQHSCzJCZLjxP7wyVAx"); } -// TODO This needs chunking implemented -//BOOST_AUTO_TEST_CASE(test_large) -//{ -// size_t length = 1310710; -// string data; -// data.resize(length, 0); -// BOOST_REQUIRE_EQUAL(data.size(), length); -// BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); -//} +BOOST_AUTO_TEST_CASE(test_smallest_chunked) +{ + size_t length = 1024 * 256 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbVuw4C4vcmVKqxoWtgDVobvcHrSn51qsmQmyxjk4sB2Q"); +} + +BOOST_AUTO_TEST_CASE(test_large) +{ + size_t length = 1310710; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); +} + +BOOST_AUTO_TEST_CASE(test_largest_one_level) +{ + size_t length = 45613056; // 1024 * 256 * 174; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmY4HSz1oVGdUzb8poVYPLsoqBZjH6LZrtgnme9wWn2Qko"); +} + +BOOST_AUTO_TEST_CASE(test_smallest_multi_level) +{ + size_t length = 45613057; // 1024 * 256 * 174 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmehMASWcBsX7VcEQqs6rpR5AHoBfKyBVEgmkJHjpPg8jq"); +} + +BOOST_AUTO_TEST_CASE(test_multi_level_tree) +{ + size_t length = 46661632; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmaTb1sT9hrSXJLmf8bxJ9NuwndiHuMLsgNLgkS2eXu3Xj"); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index ff1517eecd5f..ee6d77dab750 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -106,3 +107,51 @@ string yul::test::format(string const& _source, bool _yul) { return yul::AsmPrinter()(*parse(_source, _yul).first); } + +namespace +{ +std::map const validDialects = { + { + "evm", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialect::strictAssemblyForEVMObjects(_evmVersion); } + }, + { + "evmTyped", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialectTyped::instance(_evmVersion); } + }, + { + "yul", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::Dialect::yulDeprecated(); } + }, + { + "ewasm", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::WasmDialect::instance(); } + } +}; + +vector validDialectNames() +{ + vector names{size(validDialects), ""}; + transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); + + return names; +} +} + +yul::Dialect const& yul::test::dialect(std::string const& _name, langutil::EVMVersion _evmVersion) +{ + if (!validDialects.count(_name)) + BOOST_THROW_EXCEPTION(runtime_error{ + "Invalid Dialect \"" + + _name + + "\". Valid dialects are " + + util::joinHumanReadable(validDialectNames(), ", ", " and ") + + "." + }); + + return validDialects.at(_name)(_evmVersion); +} diff --git a/test/libyul/Common.h b/test/libyul/Common.h index 0b154e69cd61..b5f175ac283d 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -23,6 +23,8 @@ #include +#include + #include #include #include @@ -53,4 +55,6 @@ parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& Block disambiguate(std::string const& _source, bool _yul = true); std::string format(std::string const& _source, bool _yul = true); +solidity::yul::Dialect const& dialect(std::string const& _name, langutil::EVMVersion _evmVersion); + } diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index fdee56bda223..a545b6124f5b 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -48,17 +48,11 @@ using namespace solidity::frontend::test; using namespace std; -EwasmTranslationTest::EwasmTranslationTest(string const& _filename) +EwasmTranslationTest::EwasmTranslationTest(string const& _filename): + EVMVersionRestrictedTestCase(_filename) { - boost::filesystem::path path(_filename); - - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 2d74d5501a97..db3a39df00f6 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -60,15 +60,11 @@ string toString(SideEffects const& _sideEffects) } } -FunctionSideEffects::FunctionSideEffects(string const& _filename) +FunctionSideEffects::FunctionSideEffects(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test input: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _linePrefix, bool _formatted) diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f1b284042e9f..9dd589748f80 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -38,23 +38,12 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -ObjectCompilerTest::ObjectCompilerTest(string const& _filename) +ObjectCompilerTest::ObjectCompilerTest(string const& _filename): + TestCase(_filename) { - boost::filesystem::path path(_filename); - - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - if (m_settings.count("optimize")) - { - m_optimize = true; - m_validatedSettings["optimize"] = "true"; - m_settings.erase("optimize"); - } - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_optimize = m_reader.boolSetting("optimize", false); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 75e38c844d57..05accb695057 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -19,61 +19,22 @@ #include #include -#include -#include -#include - #include #include +#include #include +#include + using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul::test; -namespace -{ -std::map const validDialects = { - { - "evm", - [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& - { return yul::EVMDialect::strictAssemblyForEVM(_evmVersion); } - }, - { - "evmTyped", - [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& - { return yul::EVMDialectTyped::instance(_evmVersion); } - }, - { - "yul", - [](langutil::EVMVersion) -> yul::Dialect const& - { return yul::Dialect::yulDeprecated(); } - }, - { - "ewasm", - [](langutil::EVMVersion) -> yul::Dialect const& - { return yul::WasmDialect::instance(); } - } -}; - -vector validDialectNames() -{ - vector names{size(validDialects), ""}; - transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); - - return names; -} -} - void SyntaxTest::parseAndAnalyze() { - string dialectName = m_validatedSettings.count("dialect") ? m_validatedSettings["dialect"] : "evmTyped"; - - yul::Dialect const& dialect = validDialects.at(dialectName)(m_evmVersion); - if (m_sources.size() != 1) BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."}); @@ -84,12 +45,12 @@ void SyntaxTest::parseAndAnalyze() ErrorReporter errorReporter{errorList}; auto scanner = make_shared(CharStream(source, name)); - auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); + auto parserResult = yul::Parser(errorReporter, *m_dialect).parse(scanner, false); if (parserResult) { yul::AsmAnalysisInfo analysisInfo; - yul::AsmAnalyzer(analysisInfo, errorReporter, dialect).analyze(*parserResult); + yul::AsmAnalyzer(analysisInfo, errorReporter, *m_dialect).analyze(*parserResult); } for (auto const& error: errorList) @@ -114,23 +75,9 @@ void SyntaxTest::parseAndAnalyze() } -void SyntaxTest::validateSettings() +SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): + CommonSyntaxTest(_filename, _evmVersion) { - CommonSyntaxTest::validateSettings(); - - if (!m_settings.count("dialect")) - return; - - string const dialect = m_settings["dialect"]; - m_validatedSettings["dialect"] = dialect; - m_settings.erase("dialect"); - - if (!validDialects.count(dialect)) - BOOST_THROW_EXCEPTION(runtime_error{ - "Invalid Dialect \"" + - dialect + - "\". Valid dialects are " + - joinHumanReadable(validDialectNames(), ", ", " and ") + - "." - }); + string dialectName = m_reader.stringSetting("dialect", "evmTyped"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); } diff --git a/test/libyul/SyntaxTest.h b/test/libyul/SyntaxTest.h index 087a6326cab7..b4c838b54a47 100644 --- a/test/libyul/SyntaxTest.h +++ b/test/libyul/SyntaxTest.h @@ -36,15 +36,13 @@ class SyntaxTest: public solidity::test::CommonSyntaxTest { return std::make_unique(_config.filename, _config.evmVersion); } - SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion): - CommonSyntaxTest(_filename, _evmVersion) {} + SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); virtual ~SyntaxTest() {} - - /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. - /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). - void validateSettings() override; protected: void parseAndAnalyze() override; + +private: + Dialect const* m_dialect = nullptr; }; } diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index f37d18383b22..937ffb57e7d3 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -45,17 +45,11 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -YulInterpreterTest::YulInterpreterTest(string const& _filename) +YulInterpreterTest::YulInterpreterTest(string const& _filename): + EVMVersionRestrictedTestCase(_filename) { - boost::filesystem::path path(_filename); - - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index e488460fccda..9458174edb34 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -89,7 +89,8 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -YulOptimizerTest::YulOptimizerTest(string const& _filename) +YulOptimizerTest::YulOptimizerTest(string const& _filename): + EVMVersionRestrictedTestCase(_filename) { boost::filesystem::path path(_filename); @@ -97,38 +98,12 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); m_optimizerStep = std::prev(std::prev(path.end()))->string(); - ifstream file(_filename); - soltestAssert(file, "Cannot open test contract: \"" + _filename + "\"."); - file.exceptions(ios::badbit); + m_source = m_reader.source(); - m_source = parseSourceAndSettings(file); - if (m_settings.count("dialect")) - { - auto dialectName = m_settings["dialect"]; - if (dialectName == "yul") - m_dialect = &Dialect::yulDeprecated(); - else if (dialectName == "ewasm") - m_dialect = &WasmDialect::instance(); - else if (dialectName == "evm") - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); - else if (dialectName == "evmTyped") - m_dialect = &EVMDialectTyped::instance(solidity::test::CommonOptions::get().evmVersion()); - else - BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName)); - - m_validatedSettings["dialect"] = dialectName; - m_settings.erase("dialect"); - } - else - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); - - if (m_settings.count("step")) - { - m_validatedSettings["step"] = m_settings["step"]; - m_settings.erase("step"); - } + auto dialectName = m_reader.stringSetting("dialect", "evm"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); - m_expectation = parseSimpleExpectations(file); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) @@ -375,21 +350,8 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = AsmPrinter{*m_dialect}(*m_ast) + "\n"; + m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + AsmPrinter{ *m_dialect }(*m_ast) + "\n"; - if (m_optimizerStep != m_validatedSettings["step"]) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << - _linePrefix << - "Invalid optimizer step. Given: \"" << - m_validatedSettings["step"] << - "\", should be: \"" << - m_optimizerStep << - "\"." << - endl; - return TestResult::FatalError; - } if (m_expectation != m_obtainedResult) { string nextIndentLevel = _linePrefix + " "; @@ -408,12 +370,6 @@ void YulOptimizerTest::printSource(ostream& _stream, string const& _linePrefix, printIndented(_stream, m_source, _linePrefix); } -void YulOptimizerTest::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool _formatted) -{ - m_validatedSettings["step"] = m_optimizerStep; - EVMVersionRestrictedTestCase::printUpdatedSettings(_stream, _linePrefix, _formatted); -} - void YulOptimizerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const { printIndented(_stream, m_obtainedResult, _linePrefix); diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 41e38fca050b..99e239e3b90f 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -57,7 +57,6 @@ class YulOptimizerTest: public solidity::frontend::test::EVMVersionRestrictedTes TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; - void printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: diff --git a/test/libyul/yulOptimizerTests/blockFlattener/basic.yul b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul index 8558737cfc52..e5761743679a 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/basic.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul @@ -8,9 +8,9 @@ } let z := mload(2) } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let _1 := mload(0) // let f_a := mload(1) diff --git a/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul index dfb88a1785ef..a5be68a7cea4 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul @@ -3,9 +3,9 @@ a := add(a, 1) } } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } // { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul index ca5055ac9cb8..f31c5f979cc6 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul @@ -8,9 +8,9 @@ } let t := add(3, 9) } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // if add(mload(7), sload(mload(3))) // { diff --git a/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul index a397000e416a..d7fd4b45a423 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul @@ -14,9 +14,9 @@ a := add(a, c) } } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let a := 3 // let b := 4 diff --git a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul index f3243296b67d..0b2a8245de38 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul @@ -5,9 +5,9 @@ default { a := 3 { a := 4 } } a := 5 } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let a := 1 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul index 07eca2709077..739ba78bffa8 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul @@ -5,9 +5,9 @@ function h() -> z { z := g() } a := h() } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { // let a // a := h() diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul index 0630fe35f27b..ac04f96a81c1 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul @@ -8,7 +8,7 @@ function d() -> w { w := c() } } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul index 9873c717fcff..02ac2317de94 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul @@ -8,7 +8,7 @@ function y() -> x { x := z() } } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul index c0fe6834ad8e..4398bc29f4a8 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul @@ -2,7 +2,7 @@ function f() -> x { x := g() } function g() -> x { x := f() } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul index a077efcf1c1d..ac4ed1b0545e 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul @@ -6,9 +6,9 @@ } mstore(1, codesize()) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := 1 // let b := codesize() diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul index e664aa7812b6..8de646ae3547 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul @@ -3,9 +3,9 @@ if b { b := 1 } let c := 1 } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let b := 1 // if b { b := b } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul index ab08c0dee173..3a51b6626dec 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul @@ -23,9 +23,9 @@ p_1 := add(array, _22) } } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let _13 := 0x20 // let _14 := allocate(_13) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul index 89075e3bbb8a..d0e124f6b52e 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul @@ -7,9 +7,9 @@ a := 9 sstore(x, 3) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := calldataload(0) // let x := calldataload(0x20) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul index 957232a8f19f..1fd8f84f1fcf 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul @@ -23,9 +23,9 @@ let _11 := array_index_access(x, _10) mstore(_11, _9) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function allocate(size) -> p // { diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul index 4725a444eee0..2848ef3f10eb 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul @@ -35,9 +35,9 @@ } sstore(_1, sum_50) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let _1 := 0 // let _33 := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul index 3b3e65dbd374..ac0fa0f52d05 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul @@ -7,9 +7,9 @@ let c := double_with_se(i) let d := double_with_se(i) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function double(x) -> y // { y := add(x, x) } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul index 42b4274ce65d..a9f5664eccd3 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul @@ -2,9 +2,9 @@ let a := mload(1) let b := mload(1) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mload(1) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul index 6fee35665d3a..ed8916e63723 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul @@ -2,9 +2,9 @@ let a := gas() let b := gas() } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := gas() // let b := gas() diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul index 4c1e0feebe57..0f723c6c18de 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul @@ -14,9 +14,9 @@ object "main" { } data "abc" "Hello, World!" } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let r := "abc" // let a := datasize("abc") diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul index 44d08679fa13..5e5b79696c8b 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul @@ -10,9 +10,9 @@ mstore(0, calldataload(0)) mstore(0, x) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := 10 // let x := 20 diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul index 2457b3b56ceb..ab0d0a1ea236 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul index 1fdc65d43c97..c64fc93f9833 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul @@ -2,9 +2,9 @@ let a := mul(1, codesize()) let b := mul(1, codesize()) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mul(1, codesize()) // let b := a diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul index 178b8c52366b..46bf80eb7bba 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul @@ -9,9 +9,9 @@ let b := 0 sstore(a, b) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function f() -> x // { diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul index 0012af03717a..ab14e04250a0 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul @@ -5,9 +5,9 @@ let b mstore(sub(a, b), 7) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul index 9f1bda6651fd..52198a1732f3 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul @@ -12,9 +12,9 @@ a := b mstore(2, a) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mload(0) // let b := add(a, 7) diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul index f0b46072f14c..c7cc17fc8fd4 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul @@ -6,8 +6,9 @@ } // ==== // dialect: yul -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y:bool := false // for { } true { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul index ab523246e33d..de92aad7aefa 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul @@ -6,8 +6,9 @@ } // ==== // dialect: ewasm -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y:i32 := 0:i32 // for { } true { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul index 00c46fcdff93..d49ec0709e79 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul @@ -4,9 +4,9 @@ if y { break } } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul index 1e6b0828a971..9246c162b1f4 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul @@ -4,9 +4,9 @@ if y { continue } } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul index e936287db59b..35ba33c6ec44 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul @@ -7,9 +7,9 @@ x := 2 } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // let y := mload(0) diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul index c541590bd8bf..4207525c7e8d 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul @@ -8,9 +8,9 @@ } sstore(0, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x // for { } x { sstore(1, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul index 101407783d7a..6aa3ba57d71b 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul @@ -7,9 +7,9 @@ sstore(10, x) } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // for { } 1 { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul index 86983b3162e0..ef262b2e6ead 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul @@ -3,9 +3,9 @@ if x { sstore(0, x) } sstore(1, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // if x { sstore(0, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul index ca8bbba871a2..0326a5c3ad97 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul @@ -3,9 +3,9 @@ if x { sstore(0, x) revert(0, 0) } sstore(1, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // if x diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul index 1f4da9eea840..58a43c0d2481 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul @@ -7,9 +7,9 @@ pop(x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := calldataload(0) // switch x diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul index f37bf3d016b2..17e4d03a2466 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul index 01b3016107f5..a12f171c650a 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul @@ -5,9 +5,9 @@ y := 0 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul index d4014c2b058e..86f99e29873e 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul @@ -5,9 +5,9 @@ y := 0 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul index de41539f0ca2..2ea623e7a9d3 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul @@ -9,9 +9,9 @@ x := 2 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // let y := mload(0) diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul index d84611352669..a7fadc4345a1 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul @@ -7,9 +7,9 @@ } sstore(0, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x // for { } x { sstore(1, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul index a697c6d25be7..e5342b2a63ff 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul @@ -9,9 +9,9 @@ sstore(10, x) } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // for { } 1 { } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul index 830ae064c1af..8c94644ce1ac 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul @@ -4,9 +4,9 @@ x := 0 sstore(1, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // if x { sstore(0, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul index 13ffc0bac5e0..d90e1fc4bd5b 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul @@ -4,9 +4,9 @@ x := 0 sstore(1, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // if x diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul index 107b2923b9f0..361b0f77b26a 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul @@ -8,9 +8,9 @@ pop(x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := calldataload(0) // switch x diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul index cb88da72bbcd..176d7fd57b72 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul b/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul index 47b76fa7cf7a..a7c4126ac17c 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul @@ -4,9 +4,9 @@ let z := 0xffff0000ffff0000ffff0000ffff0000ff00ff00ffff0000ffff0000ffff0000 let w := 0xffffffff000000ffffef000001feff000067ffefff0000ff230002ffee00fff7 } -// ==== -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let x := 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00 // let y := 0x1100ff00ff00ff001100ff00ff001100ff00ff001100ff00ff001100ff001100 diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul b/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul index 6371638eba03..82dccdab41de 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul @@ -7,8 +7,9 @@ } // ==== // EVMVersion: >=constantinople -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let a := shl(172, 1) // let x := add(shl(248, 17), 0xffffffffffffffffffffffff23) diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul b/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul index 0315fdab8143..af8a08886dd3 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul @@ -4,9 +4,9 @@ for { let i := 0xff00 } lt(i, 2) { i := add(i, 3) } { } } -// ==== -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let x := 8 // let y := 0xffff diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul index 87450d939d13..bf74a266ce4a 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul @@ -1,7 +1,7 @@ { let a := mload(0) if a {} } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let a := mload(0) // pop(a) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul index 737ff7bf1ae8..a204e6cec189 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul @@ -1,5 +1,5 @@ { if mload(0) {} } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { pop(mload(0)) } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul index af7726df93f2..f0bf4b7c2a9e 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul @@ -3,9 +3,9 @@ function g() -> x { leave x := 7 } function h() -> x { if x { leave } } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // function f() -> x // { x := 7 } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul index 16e718d2e270..f52551ab454d 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul @@ -1,9 +1,9 @@ { switch mload(0) default { mstore(1, 2) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // pop(mload(0)) // { mstore(1, 2) } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul index fb2434ec7648..a04b87ae6b46 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul @@ -10,9 +10,9 @@ case 1 { } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // pop(add(y, 4)) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul index 4b4f2cc0c4e4..03879b263bd2 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } case 2 { y := 10 } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul index 57beacaa1964..223760723382 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // if eq(1, calldataload(0)) { y := 9 } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul index c5c4e44a764c..7c6283a8fca0 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul @@ -5,9 +5,9 @@ case 2 { y := 10 } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul index 4a3558b6526a..b314cd96de59 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul @@ -1,9 +1,9 @@ { switch calldataload(0) case 2 { mstore(0, 0) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if eq(2, calldataload(0)) { mstore(0, 0) } // } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul index 6f7390d53367..8e48e2b6586f 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul @@ -4,9 +4,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() { mstore(4, 5) } // } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul index 2b6397269fee..7bf16956eab3 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul @@ -8,9 +8,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(8, 9) } // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul index 5412a7952b29..806a8ebcbdad 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul @@ -7,9 +7,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul index d6f7e5d18378..b8bf66163026 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul @@ -5,9 +5,9 @@ revert(0, x) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul index 7ef55bc3c645..e8d034733ccd 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul @@ -6,9 +6,9 @@ revert(0, x) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(1, 2) } // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul index 9d5ce5b84794..b92efd885229 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul @@ -5,9 +5,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(1, 2) } // { diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul index 3d350c43b39e..ef17a9708a7d 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul @@ -13,9 +13,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul index a0d751938e97..c7a3d98e1942 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul @@ -14,9 +14,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul index 61eae9ff2ed6..d6b9f9d23995 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul @@ -14,9 +14,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul index 90dae56f0961..ce3c006727b3 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul @@ -18,9 +18,9 @@ pop(f()) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // function f() -> x // { diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul index be13641c20cb..7e37d6d4b256 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul @@ -15,9 +15,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // revert(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul index ee4227b6adee..ad157573685f 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul @@ -15,9 +15,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // stop() diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul index 295a49504c53..f5e67f77f530 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul @@ -4,7 +4,7 @@ let i_1 := i_0 } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { stop() } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul index e1d0f30fe349..d5df28fb0973 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul @@ -12,9 +12,9 @@ pop(add(1, 1)) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // fun() // revert(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul index b89c1e0b7fbd..088394ee0a3f 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul @@ -12,9 +12,9 @@ y := 10 } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let y := mload(0) // switch y diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul index c1003538d916..0e0a2b9c4f4d 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul @@ -4,9 +4,9 @@ } mstore(0, 0) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // { revert(0, 0) } // mstore(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul index 77e4303b6366..28425b6e18c0 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul index e3a0b7a2faea..e01ae0aed568 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul index 561ac2066f24..6999c39c0070 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul @@ -15,9 +15,9 @@ stop() } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // let a := 20 diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul index c6b246ad0f4f..30f2e54794a0 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul index 7a4cfe739a0e..f99c4e4476de 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c, d, f } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul index def0a2d3159b..6b6da1f189fa 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul index b403dc095c44..d55afc4828b8 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul @@ -1,8 +1,9 @@ { { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let aanteuhdaoneudbrgkjiuaothduiathudaoeuh diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul index bfe22582a4fb..4ea58a6ef3cb 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: disambiguator // ---- +// step: disambiguator +// // { } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul index 2ed2b9ad10f6..676416437795 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul @@ -1,6 +1,7 @@ { } // ==== -// step: disambiguator // dialect: yul // ---- +// step: disambiguator +// // { } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul index 0948b51b9470..16ee5353f79b 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables.yul b/test/libyul/yulOptimizerTests/disambiguator/variables.yul index 7b197f4205b4..65c93bbb9e88 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables.yul @@ -1,8 +1,9 @@ { { let a:u256 } { let a:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a } // { let a_1 } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul index 9c72b82ff885..e2cf23756d56 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul @@ -1,8 +1,9 @@ { { let a:u256 let a_1:u256 } { let a:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul index fecb67a653df..a78af7dabcab 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let c diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul index e400e69ae4a7..5f561185d53e 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul @@ -54,9 +54,9 @@ } } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // pop(f(1, 2, 3)) // pop(f(4, 5, 6)) diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul index 43629175bc52..69045f364141 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul @@ -4,9 +4,9 @@ function f() { mstore(1, mload(0)) } function g() { mstore(1, mload(0)) } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // f() // f() diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul index 25ffa4017707..5967adc0e13c 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul @@ -4,9 +4,9 @@ function f() -> b { let a := mload(0) b := a } function g() -> a { let b := mload(0) a := b } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // pop(f()) // pop(f()) diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul index 9de10889bcde..490bfd07229b 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul @@ -4,9 +4,9 @@ function f(x) { switch x case 0 { mstore(0, 42) } case 1 { mstore(1, 42) } } function g(x) { switch x case 1 { mstore(1, 42) } case 0 { mstore(0, 42) } } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // f(0) // f(1) diff --git a/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul b/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul index 37649a5724e7..12228009ab83 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul @@ -16,9 +16,9 @@ let y11:= ref1(y1) let y12:= ref3(y1) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function ref1(a) -> x // { x := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul index e95c4144c9ab..49237c2f640a 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul @@ -2,9 +2,9 @@ function f(a) -> x { x := add(a, a) } let y := f(calldatasize()) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := add(a, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul index d19082f2e7d3..42dc4f015f19 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul @@ -3,9 +3,9 @@ function g(b, c) -> y { y := mul(mload(c), f(b)) } let y := g(calldatasize(), 7) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := add(a, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul index 923032af04c6..337751a7241f 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul @@ -3,9 +3,9 @@ function g(b, s) -> y { y := f(b, f(s, s)) } let y := g(calldatasize(), 7) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a, r) -> x // { x := g(a, f(r, f(r, r))) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul index 1b2b702fe5e6..1698ac5ace27 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul @@ -3,9 +3,9 @@ function f(a) -> x { x := a } let y := f(mload(2)) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := a } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul index 5a116bcc34c8..48acca95c9da 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul @@ -6,9 +6,9 @@ function h() -> z { mstore(0, 4) z := mload(0) } let r := f(g(), h()) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a, b) -> x // { x := add(b, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul index 8c3cd28e3ee0..2b6dba24069f 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f() -> x // { x := 2 } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul index 5b73ed3fe025..0456654e3a82 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := a } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul index b2a204482b0f..045168cf1303 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul @@ -10,9 +10,9 @@ let z := 3 let t := add(z, 9) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // if add(mload(7), sload(mload(3))) { let y := add(mload(3), 3) } // let t := add(3, 9) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul index 07982ca47949..461285ae246c 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul @@ -4,9 +4,9 @@ let x := mul(add(b, a), mload(2)) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(3) // let b := mload(6) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul index 9d9a139a2d1f..5fcd2c5bf252 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul @@ -4,9 +4,9 @@ let x := mul(add(b, a), 2) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(mul(add(mload(6), mload(2)), 2), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul index 95a0eccb3d13..ed209e597dee 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul @@ -3,9 +3,9 @@ let a := mload(2) let b := add(a, a) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // let b := add(a, a) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul index d8624dc14238..2837f70330a8 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul @@ -7,9 +7,9 @@ let x := mul(a, add(2, b)) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // sstore(mul(a, add(2, mload(6))), 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul index 6296c3778f98..d69f9d8a5db0 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul @@ -4,9 +4,9 @@ let x := mul(add(a, b), 2) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // sstore(mul(add(a, mload(6)), 2), 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul index 02c4466a2e59..0e4f1916a258 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul @@ -11,9 +11,9 @@ } sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let x := calldataload(mload(2)) // sstore(x, 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul index 0cc4e8f6dffb..b453446ad2ce 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul @@ -1,9 +1,9 @@ { for { let b := mload(1) } b {} {} } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // for { let b := mload(1) } b { } // { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul index 1444769048f4..ab41999d50f3 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul @@ -2,9 +2,9 @@ let a := mload(0) for { } a {} {} } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(0) // for { } a { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul index 12fa6ec51752..30bf46634868 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul @@ -5,9 +5,9 @@ x := add(a, 3) } } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // function f(a) -> x // { diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul index 97661414fc42..e327d0b33f67 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul @@ -4,9 +4,9 @@ let b := mload(a) a := 4 } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // let b := mload(a) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul index 03d2a7aee463..5ec8a900ad5d 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul @@ -3,9 +3,9 @@ let x := calldataload(a) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(calldataload(mload(2)), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul index 21bf67c890c7..812cb7e7c6ae 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul @@ -5,9 +5,9 @@ let d := add(b, c) sstore(d, 0) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let b := sload(mload(3)) // sstore(add(b, mload(7)), 0) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul index 7eb5f5285943..77f73f11266c 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul index e8e7877623f4..08f7bb9fb00b 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul @@ -14,9 +14,9 @@ let z := 3 let t := add(z, 9) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // switch add(mload(7), sload(mload(3))) // case 3 { let y := add(mload(3), 3) } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul index cf2b4a6155ab..d6c652e2c06a 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul @@ -5,9 +5,9 @@ let x := mul(add(c, b), a) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(mul(add(mload(7), mload(6)), mload(2)), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul index 809f6ff23e4a..7833193954af 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul @@ -3,9 +3,9 @@ let c, d := f() let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> x, z // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul index 94caf0782e79..d465600e3c6c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul @@ -5,8 +5,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := shr(248, x) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul index 36c56834b79a..6649a86b57cb 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul @@ -10,8 +10,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul index f506c41bde21..4211b6afec10 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul @@ -8,8 +8,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := and(shl(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul index 983a3b25117b..2f655efbae59 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -1,5 +1,5 @@ { let a := add(7, sub(mload(0), 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := mload(0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul index efbcf8712246..8bf04b987ff0 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -1,5 +1,5 @@ { let a := add(1, mul(3, 4)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := 13 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul index cd74d73ad961..bbda84653601 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul @@ -3,9 +3,10 @@ let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) } // ==== -// step: expressionSimplifier // EVMVersion: >=constantinople // ---- +// step: expressionSimplifier +// // { // let a := create2(0, 0, 0x20, 0) // let b := create2(0, 0, 0x20, 0) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul index c7cc887dc6a6..44a8cabb0b83 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul @@ -2,9 +2,9 @@ let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := create(0, 0, 0x20) // let b := create(0, 0, 0x20) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul index e7b60d2df4db..38502d22cedb 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -1,5 +1,5 @@ { let a := sub(calldataload(0), calldataload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := 0 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul index 2838eb1936b1..5f3d268ca561 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -1,7 +1,7 @@ { let a := sub(calldataload(1), calldataload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := sub(calldataload(1), calldataload(0)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul index 554d080b2f56..d75ceb83148d 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -2,9 +2,9 @@ let a := mload(0) let b := sub(a, a) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := mload(0) // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul index 0c92afe38926..73b6bfa545fd 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -2,9 +2,9 @@ function f() -> a {} let b := add(7, sub(f(), 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul index 83906d67524d..b8b3ee35d84e 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul @@ -2,9 +2,9 @@ let a := 10 for { } iszero(eq(a, 0)) { a := add(a, 1) } {} } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := 10 // for { } iszero(iszero(a)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul index 9ad8892431f1..fc75f131f942 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -2,9 +2,9 @@ let a := mload(sub(7, 7)) let b := sub(a, 0) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := mload(0) // let b := a diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul index d5e7c0ad483d..073db8d4e33c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul @@ -5,9 +5,9 @@ // create cannot be removed. let d := byte(33, create(0, 0, 0x20)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := calldataload(0) // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul index 6d7b03035ca8..65a325d6c20b 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 8))) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // mstore(0, and(calldataload(0), 255)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul index d949b2f17f2e..d4e35af33925 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 255))) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul index 1430fd718da5..b7bc3e4f99c6 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -2,9 +2,9 @@ function f(a) -> b { } let c := sub(f(0), f(1)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f(a) -> b // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul index cc819fff8667..b43440128a01 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -3,9 +3,9 @@ function f2() -> b { } let c := sub(f1(), f2()) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f1() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul index fc43fa78807e..c0fa43e2c7e5 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -3,9 +3,9 @@ function f() -> a { } let b := sub(f(), f()) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul index cee563173d27..d153ae724337 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -3,9 +3,9 @@ { let a := div(keccak256(0, 0), 0) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := div(keccak256(0, 0), 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul index 8178c6d03705..fbc5befd0431 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul @@ -3,9 +3,9 @@ x := 0 mstore(0, add(7, x)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := mload(0) // x := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul index cb5303eaff7d..b81ae96197a5 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul @@ -6,8 +6,9 @@ } // ==== // EVMVersion: >=constantinople -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := shr(248, calldataload(0)) // let b := shr(248, calldataload(0)) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul index 8d69bcc8d7bd..550c7becb16c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul @@ -6,8 +6,9 @@ } // ==== // EVMVersion: >=constantinople -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := 0 // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul index 8586396c452c..c1c0093ef4db 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul @@ -4,9 +4,9 @@ let y := add(d, add(c, 7)) } } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> c, d // { let y := 7 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul index 1d398f833ac5..9ca956c103d2 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -1,7 +1,7 @@ { let a := add(0, mload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := mload(0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul index 7e181a097786..14d65c4e9267 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul @@ -4,9 +4,10 @@ } } // ==== -// step: expressionSimplifier // EVMVersion: >byzantium // ---- +// step: expressionSimplifier +// // { // for { } div(create(0, 1, 0), shl(msize(), 1)) { } // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul index 7562dfb47e51..30a2f5e901db 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul index e5db5ff76b9a..e88877365a9e 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul @@ -3,9 +3,9 @@ let c, d let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let c, d // let y := 7 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul index f8480a32734c..d66d4efb2ce0 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul @@ -4,9 +4,9 @@ let d let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul index 2eb72c1125c2..d56a5285e519 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul @@ -7,9 +7,9 @@ } } } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 0 // let x := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul index bb73118ec98a..294cac14ab19 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul @@ -5,9 +5,9 @@ } sstore(x, f(mload(2), mload(2))) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 3 // let _2 := 7 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul index 9699918718de..60492e027233 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul @@ -9,9 +9,9 @@ object "main" { } data "abc" "Hello, World!" } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let x := dataoffset("abc") // let y := datasize("abc") diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul index c47eebe7b490..c5f979bf1a54 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul index 3608cab4fc4c..3664a7e26474 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul @@ -5,9 +5,9 @@ default { mstore(0, mload(3)) } x := add(mload(3), 4) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let x := 8 // let _1 := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul index 3d48eed79a16..a70827b93048 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul @@ -1,9 +1,9 @@ { mstore(add(calldataload(2), mload(3)), 8) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 8 // let _2 := 3 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul index 37f40f5e68e4..4bae497d6392 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul @@ -11,8 +11,9 @@ } // ==== // dialect: ewasm -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // function fun(x:i32, y) -> t:i32, z:i32 // { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul index 95e171475f2e..031507a38026 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul @@ -6,9 +6,9 @@ for { } a { } { } for { } add(a, a) { } { } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let a := 1 // for { } 42 { } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul index 6f1ad5458a5b..41c9e4234b77 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul @@ -1,9 +1,9 @@ { for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // for { let a := 1 } true { a := add(a, 1) } // { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul index 292284a65c91..acbe3e456918 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul @@ -13,9 +13,9 @@ mstore(b,b) } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let random := 42 // for { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul index f470264627b1..bbe7823072dc 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let random := 42 // for { let a := 1 } true { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul index 951a5fbe6f3e..6304c4de57a3 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul @@ -5,9 +5,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul index 9fa5fd37e764..38a00f752860 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let a := 1 // for { } iszero(eq(a, 10)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul index c751f4483208..331d9cdde435 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul @@ -13,9 +13,9 @@ mstore(b,b) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul index c8db1e5aa1ad..0dad899616d5 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul index b3ec1bbe8b16..259e4aeda4c6 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul @@ -4,9 +4,9 @@ let b3, c3 := f(a1) let b4, c4 := f(c3) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_2 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul index fd9d893ae31e..39c3bb2950c7 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul @@ -8,9 +8,9 @@ r := add(a, calldatasize()) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul index a088e1681905..151b7596ca43 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul @@ -15,9 +15,9 @@ // This should be inlined because it is a constant as well (zero) let s := f(a3) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_1 := mload(2) diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul index b8c05b72c416..4bcfe6f19465 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul @@ -10,9 +10,9 @@ // Single-use functions are always inlined. let r := f(mload(1)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_6 := mload(1) diff --git a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul index 8e9c2019d325..b2162c520ec9 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul @@ -7,9 +7,9 @@ mstore(0, verylongfunctionname(verylongvariablename2)) mstore(1, verylongvariablename2) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let verylongvariablename2_1 := 3 diff --git a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul index f40563602cf7..7bc959ec8f58 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul @@ -5,9 +5,9 @@ } let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5))) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(5) diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul index 2f8a85842505..56cac2704e64 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -3,9 +3,9 @@ function g(b, c) -> y { y := mul(mload(c), f(b)) } let y := g(f(3), 7) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _1 := 7 diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 46980522df33..98c8e301065f 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -22,9 +22,9 @@ f(100) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let x_8 := 100 diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul index 7d90e011830f..21369f10b683 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul @@ -6,9 +6,9 @@ let r, s := f(mload(0)) mstore(r, s) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul index f9f01a193c0b..143ae35b27b8 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul @@ -6,8 +6,9 @@ } // ==== // dialect: evmTyped -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(3) diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul index 4a7837ca2e0c..c1ce89bcfe16 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul @@ -9,9 +9,9 @@ x := f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(2))))))))))))))))))) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // function f(a) -> b // { b := sload(mload(a)) } diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul index 337612d37735..10ccc2a3794f 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul @@ -7,9 +7,9 @@ // the global context gets too big. let x := f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(2))))))))))))))))))) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_20 := 2 diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul index 552ce9a821a4..a22025c4b385 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul @@ -4,9 +4,9 @@ let a1 := calldataload(0) f(a1) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_2 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul index c45e7a3c7129..2897b5151d08 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul @@ -4,9 +4,9 @@ } f(mload(0)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul index 94282bf5cc74..5678a13fd9a1 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul @@ -9,9 +9,9 @@ r := a } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := 0 diff --git a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul index 373416925bca..c46505c79a17 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul @@ -8,9 +8,9 @@ } pop(add(f(7), 2)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _1 := 2 diff --git a/test/libyul/yulOptimizerTests/fullInliner/recursion.yul b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul index 82632a9e7173..375327e652f9 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/recursion.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul @@ -4,9 +4,9 @@ } f(mload(0)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { f(mload(0)) } // function f(a) diff --git a/test/libyul/yulOptimizerTests/fullInliner/simple.yul b/test/libyul/yulOptimizerTests/fullInliner/simple.yul index af6fa8283954..2c9d979b5832 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/simple.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/simple.yul @@ -5,9 +5,9 @@ } let y := add(f(sload(mload(2))), mload(7)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(7) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul index 4c7fa2692135..968ec47673f7 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul @@ -2,9 +2,9 @@ let a := add(7, sub(mload(0), 7)) mstore(a, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _2 := 0 // mstore(mload(_2), _2) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constants.yul b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul index 2ea5ea442c94..0b31d361f922 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/constants.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul @@ -2,7 +2,7 @@ let a := add(1, mul(3, 4)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 13) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul index df7712b7a0bc..452ceaae6ff8 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul @@ -2,7 +2,7 @@ let a := sub(calldataload(0), calldataload(0)) mstore(a, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul index bc7bf5b5a594..8c5e4e5dfca5 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul @@ -2,9 +2,9 @@ let a := sub(calldataload(1), calldataload(0)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // mstore(_1, sub(calldataload(1), calldataload(_1))) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul index 1e9de7755c72..e8efb735c08a 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul @@ -2,7 +2,7 @@ let a := mload(0) mstore(0, sub(a, a)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul index d75e681a4af7..73bd6c3639f9 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul @@ -3,9 +3,9 @@ let b := add(7, sub(f(), 7)) mstore(b, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul index 339e744d748d..9d4e52dc1b3a 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul @@ -3,9 +3,9 @@ let a := 10 for { } iszero(eq(a, sub(x, calldataload(3)))) { a := add(a, 1) } {} } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let a := 10 // for { } iszero(iszero(a)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul index 453f74e33c85..687491818679 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul @@ -7,9 +7,9 @@ // run of CSE afterwards. mstore(b, eq(calldataload(0), a)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let a := calldataload(0) // let _4 := 0 diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul index 59dbf172b0f5..1e448c9a4f28 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 8))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _4 := 0 // mstore(_4, and(calldataload(_4), 255)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul index 41254b818e55..0756c1841d57 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 255))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _4 := 0 // mstore(_4, and(calldataload(_4), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul index 5038a6fa9920..8dd271d9fe04 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul @@ -2,9 +2,9 @@ function f(a) -> b { } mstore(0, sub(f(0), f(1))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f(a) -> b // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul index 72ebadd20e78..0b17b1e4aff8 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul @@ -4,9 +4,9 @@ let c := sub(f1(), f2()) mstore(0, c) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f1() -> a // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul index 11de59aba47e..3133c69cab2c 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul @@ -4,9 +4,9 @@ let b := sub(f(), f()) mstore(0, b) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f() -> a // { mstore(1, 2) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul index 9f916198c6ce..05fc9447d683 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul @@ -3,9 +3,9 @@ let a := div(create(0, 0, 0), 0) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // pop(create(_1, _1, _1)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul index 811f867805f6..93b424233079 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul @@ -19,9 +19,9 @@ mstore(17, or(x, not(x))) mstore(18, or(not(x), x)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // mstore(1, 0) // mstore(2, 0) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul index fceec7ecf89b..5fe6324959bd 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul @@ -2,9 +2,9 @@ let a := add(0, mload(0)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // mstore(_1, mload(_1)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul index 13d4997dde64..9ebdf012894d 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul @@ -4,9 +4,9 @@ let y := 255 mstore(1, signextend(0, y)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // mstore(0, 7) // mstore(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul index 7ff4c60c3e6d..15ff14a845e9 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 384a47d893a3..2f400186aae6 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1071,9 +1071,10 @@ } } // ==== -// step: fullSuite // EVMVersion: >=constantinople // ---- +// step: fullSuite +// // { // { // let _1 := mload(1) diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 4557c1889487..5e6e0305dfa2 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,8 +458,9 @@ } // ==== // EVMVersion: >=constantinople -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := 0 diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 635da72d6053..1076f349a44e 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -228,9 +228,9 @@ mstore(0x00, keccak256(0x300, mul(n, 0x80))) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := 0x80 diff --git a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul index a9cdb84e0c14..71ed36d9794d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul @@ -6,9 +6,9 @@ } if y { revert(0, 0) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let y := mload(0x20) diff --git a/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul index 314f2a363d8d..c5cdf81041b1 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul @@ -14,9 +14,9 @@ v := calldataload(add(data, mul(i, 0x20))) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul index f8fb81aa8f8e..3b30ac4f7f05 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul @@ -17,9 +17,9 @@ v := add(v, calldataload(7)) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index c5ed25e2f010..4f1a6f35d06a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -16,9 +16,9 @@ for { switch mul(1,2) case 2 { mstore(0x40, 0x20) } } sub(1,1) {} { mstore(0x80, 0x40) } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let p := mload(0x40) diff --git a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul index 4efe81d899e3..f0ee054b9a01 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul @@ -7,9 +7,9 @@ } {} } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := iszero(caller()) diff --git a/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul index fa1364297034..a0ac5c36d292 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul @@ -6,9 +6,9 @@ default { invalid() } mstore(1, 1) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // switch mload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul b/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul index bb350bf8faef..6e217ae4fbb4 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul @@ -9,7 +9,7 @@ mstore(sub(1,div(sub(x_9,1),sub(1,sub(x_9,1)))), 1) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(1, 1) } } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul index c4a70b03ec1b..78af81f383f2 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -31,9 +31,9 @@ a,b := abi_decode_t_bytes_calldata_ptr(a,b) mstore(a,b) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul index 564ed9664cbb..4421dd3cdf20 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul @@ -9,9 +9,9 @@ } mstore(a, b) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let a := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul index 6a4e52652656..961abd72c48d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul @@ -20,9 +20,9 @@ sstore(0,0) sstore(3,1) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := gt(not(pc()), 1) diff --git a/test/libyul/yulOptimizerTests/fullSuite/storage.yul b/test/libyul/yulOptimizerTests/fullSuite/storage.yul index 4daff379f296..7b3360847e18 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/storage.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/storage.yul @@ -3,9 +3,9 @@ sstore(4, 3) sstore(8, sload(4)) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // sstore(4, 5) diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul index 2e878e437909..65c7512d350d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul @@ -6,7 +6,7 @@ case 1 { y := 9 } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(9, 0) } } diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul index 6710f1fee279..cff8bd755c1d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul @@ -7,7 +7,7 @@ default { y := 10 } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(10, 0) } } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul index 95117c38e728..b75efc54c408 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul @@ -4,9 +4,9 @@ } function f() -> y { y := 8 } } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let x := 2 } // function f() -> y diff --git a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul index f08de996b807..7bfbb340124f 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul @@ -1,8 +1,9 @@ { let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul index 4ad0c872dfcc..2e5380d2c220 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul @@ -4,9 +4,9 @@ let x := 2 } } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { { let x := 2 } } // function f() -> y diff --git a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul index 5fb398ad7516..1aedecf6a348 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul @@ -6,8 +6,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul index 455da2ef5bbe..9193da739c2b 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul @@ -10,8 +10,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let a } // function f() diff --git a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul index f7836b6c64fc..b007f1855a30 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul @@ -3,8 +3,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let a } // function f() diff --git a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul index 00895bf38c67..e292340f2985 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { { } } diff --git a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul index 276fd4b0229e..bd66b3a2b1cd 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function f() -> x:bool diff --git a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul index 4e9e9604f3e7..060fab314c96 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // let c diff --git a/test/libyul/yulOptimizerTests/functionHoister/nested.yul b/test/libyul/yulOptimizerTests/functionHoister/nested.yul index 89ea5c0ffc23..05eb4803b0a6 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/nested.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/nested.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function g() diff --git a/test/libyul/yulOptimizerTests/functionHoister/single.yul b/test/libyul/yulOptimizerTests/functionHoister/single.yul index 6cc82e68ad09..205626b57f58 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/single.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/single.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function f() diff --git a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul index 27c9e84193e8..e0d43adbb394 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul @@ -1,6 +1,6 @@ { } -// ==== -// step: functionHoister // ---- +// step: functionHoister +// // { } diff --git a/test/libyul/yulOptimizerTests/loadResolver/loop.yul b/test/libyul/yulOptimizerTests/loadResolver/loop.yul index 4f7b27734176..3606891ee19e 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/loop.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/loop.yul @@ -4,9 +4,9 @@ x := add(x, 1)} {y := add(x, y) } } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 123213 // let _2 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul b/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul index a9487afa7fe2..77fc5aef0184 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul @@ -14,9 +14,9 @@ function g() {} } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 9 // let _2 := 2 diff --git a/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul b/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul index 17a8761d9866..199fb1f6c47a 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul @@ -5,9 +5,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := msize() // let _3 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul index 7f5dfb2f09f9..e9ced5f4c31f 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _3 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul index f7cfdd7c3a92..8ce9fba00cc4 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _4 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul index 4fc21bbf54ad..225e0f6272d5 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _3 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul index ebe65992a8c3..b4b113e812e7 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul @@ -7,9 +7,9 @@ } sstore(0, mload(2)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 3 // let _2 := 2 diff --git a/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul b/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul index 9bb8261dd836..5e5c065535f4 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul @@ -6,9 +6,9 @@ foo(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function foo(x) // { diff --git a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul index 2de873942867..da910baceec1 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul @@ -11,9 +11,9 @@ funcWithLoop(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function userNot(x) -> y // { y := iszero(x) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul index 1e06cf8df3e3..efef507dc8d4 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul @@ -11,9 +11,9 @@ funcWithLoop(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function userNot(x) -> y // { y := iszero(x) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul b/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul index 7fb27c996c80..01e29b705e0a 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul @@ -7,9 +7,9 @@ mstore(a, c) sstore(10, mload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul b/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul index aed91e002f9c..8c33e4f5dcbc 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul @@ -7,9 +7,9 @@ sstore(a, c) mstore(32, sload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loadResolver/reassign.yul b/test/libyul/yulOptimizerTests/loadResolver/reassign.yul index fe7fccfe618f..4687422e92ac 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/reassign.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/reassign.yul @@ -4,9 +4,9 @@ a := calldataload(2) mstore(0, sload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 0 // let a := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul b/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul index d2d0999fac6c..1b73cecb93d3 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul @@ -14,9 +14,9 @@ a := 39 mstore(sload(a), 11) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul b/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul index ae23c1b8b08e..1aa05106c363 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul @@ -9,9 +9,9 @@ mstore(b, 8) sstore(mload(a), mload(b)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store.yul index 67aa196f431f..f6f8250ce07e 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store.yul @@ -6,9 +6,9 @@ // if the two slots are different. mstore(0, sload(x)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // sstore(x, 7) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul index b90b177b3ac6..a5cb459ad71a 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul @@ -6,9 +6,9 @@ // written are 7. mstore(0, sload(x)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let _2 := 7 diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul index a4154ce63ea4..c2e669b7f603 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul @@ -9,9 +9,9 @@ sstore(b, 8) mstore(sload(a), sload(b)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul b/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul index b07883c3abad..ec9eec6c9e6e 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul @@ -8,9 +8,9 @@ stores() sstore(0, mload(2)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function stores() // { mstore(0, 1) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/simple.yul b/test/libyul/yulOptimizerTests/loadResolver/simple.yul index a32a542418ab..a645f1692f1b 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/simple.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/simple.yul @@ -4,9 +4,9 @@ let q := sload(calldataload(0)) mstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // sstore(calldataload(0), _2) diff --git a/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul b/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul index a4c06ebdcea7..667abe053c2a 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul @@ -4,9 +4,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // mstore(calldataload(0), _2) diff --git a/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul b/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul index 75307ad5df86..b24ab0170ce5 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul @@ -10,9 +10,10 @@ mstore(0, sload(a)) } // ==== -// step: loadResolver // EVMVersion: >=byzantium // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul index 82ea2bf78751..a1b58ceb2b7b 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul @@ -7,9 +7,9 @@ mstore(a, not_inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul index c7e39cc70e99..19a85c813841 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul @@ -9,9 +9,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul index 7f842bbdda34..19e681d6b6b4 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul @@ -8,9 +8,9 @@ let q := g() } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // function f() -> x // { x := g() } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul index 5f3eeb7ac7b5..2bb86008ef51 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul @@ -8,9 +8,9 @@ let q := g() } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // function f() -> x // { x := g() } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul index 86cf1274e3d0..18feab0c1178 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul @@ -7,9 +7,9 @@ mstore(a, not_inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul index 787c1756baac..15fe39ccd08e 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul @@ -6,9 +6,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 0 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul index a489b134babf..5fdabc7cb6fb 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul @@ -8,9 +8,9 @@ a := add(a, 1) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul index 970fec59f55e..b0527e2b646b 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul @@ -6,9 +6,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul index 741efd42b4e4..635e618d5f4c 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { diff --git a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul index 69fb0bef0cae..1d14937e7bde 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { diff --git a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul index b97bb2f4d861..a888970e729e 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { let a } diff --git a/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul index 1ab108f6b2e8..6b20d712cab1 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { let a } diff --git a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul index 54ad2d843e7f..5bad02dfba15 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul @@ -1,8 +1,9 @@ {} // ==== -// step: mainFunction // dialect: yul // ---- +// step: mainFunction +// // { // function main() // { } diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul index 9c547d17ace5..54045ce379f1 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul @@ -6,9 +6,9 @@ function illegal5(illegal1, illegal2) -> illegal3 { illegal3 := add(illegal1, illegal2) } } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // let x := illegal4_1(1, 2) // function illegal4_1(illegal1_2, illegal2_3) -> illegal3_4 diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul index dae343217157..08b7bc43121d 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul @@ -1,7 +1,7 @@ { { let illegal1 := 1 } { let illegal2 := 2 let illegal3, illegal4 } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // { let illegal1_1 := 1 } // { diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul index 73228739aff4..472aa6a33e8f 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul @@ -4,9 +4,9 @@ illegal3 := add(illegal1, illegal2) } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // function f(illegal1_1, illegal2_2) -> illegal3_3 // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul index a7280364fbd3..314963368082 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul @@ -7,9 +7,9 @@ a := 7 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a := 2 // a := 3 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul index 6b15425c9393..120b43ce81a5 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul @@ -13,9 +13,9 @@ y := 8 mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // let y diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul index 647de0d24c34..1b795ad8c73a 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul @@ -12,9 +12,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul index 3ac614b67ed6..0ee9c551da7a 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul @@ -15,9 +15,9 @@ } x := 3 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // for { } calldataload(0) { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul index ab7ba0df16c7..ee747ae5d040 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul @@ -12,9 +12,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul index 14faa2a752f5..b70f35d2bc27 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul @@ -11,9 +11,9 @@ x := 3 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // for { } calldataload(0) { mstore(x, 0x42) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul index 87104647d8c5..f54b21af8b0b 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul @@ -18,9 +18,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // for { } calldataload(0) { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul index 96571f435fb1..5edd71d58b38 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul @@ -25,9 +25,9 @@ } x := 13 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 2 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul index 6df6e33ef188..5b086bab8a4d 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul @@ -18,9 +18,9 @@ } } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // for { } 1 { } // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul index 5b0e70f8c150..ce084b1ce168 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul @@ -34,9 +34,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul index 7e9518f89d19..a83f536668dc 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul @@ -31,9 +31,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul index 3cf202582295..5d1e60738568 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul @@ -10,9 +10,9 @@ } x := 3 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul index eceba8c23bc0..5e88df9689ca 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul @@ -22,9 +22,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul index 444f73265966..2a20ca8b69bb 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul @@ -11,9 +11,9 @@ } r := 2 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let r // function f(x, y) -> a, b diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul index 35b6c968f49f..2fab04a36fba 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul @@ -9,9 +9,9 @@ // This enforces that none of the assignments above can be removed. mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul index 120c3e07b0d8..7585f848a2b2 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul @@ -10,9 +10,9 @@ d := 3 mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul index 9fadcbbe990e..637a32c02a2e 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul @@ -10,9 +10,9 @@ d := 3 mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul index ef68155ad1d4..1caee198a10d 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul @@ -20,9 +20,9 @@ t := 8 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // function f(a, b) -> x // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul index b4546aa558c6..f0845680f40c 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul @@ -8,9 +8,9 @@ x := 3 y := 4 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // function f() -> a, b // { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul index b094f25b7621..2b13ba3c36b8 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul @@ -5,9 +5,9 @@ b := a a := b } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a := 2 // a := 7 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul index a5e697555cdd..343c672dea0c 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul @@ -3,9 +3,9 @@ a := 0 a := mload(0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a // a := mload(0) diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul index 8e75d82edb7c..035f66d3851e 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul @@ -11,9 +11,9 @@ mstore(0, x) } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let i := 0 // for { } lt(i, 2) { i := add(i, 1) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul index f5a29382b393..c4f3b3e43ff3 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let i := 0 // for { } lt(i, 2) { i := add(i, 1) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul index 85c83bde859b..d3c6cb4a6da0 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul @@ -6,9 +6,9 @@ a := 2 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a // { let b } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul index 1fd827ed1fdb..12b9ae6b7140 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul @@ -3,7 +3,7 @@ a := 1 a := 2 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { let a } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul index 547ae025b513..a39cdedd7029 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul @@ -7,9 +7,9 @@ default { x := 3 } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul index 7ab2036e1422..271bb4864c7a 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul @@ -6,9 +6,9 @@ case 0 { x := 2 } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul index 8dc43d7219f8..8d38dcc161d4 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul @@ -7,9 +7,9 @@ default { mstore(x, 1) } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul index f0b29f30114f..94de51b8893d 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul @@ -5,9 +5,9 @@ switch calldataload(0) case 0 { mstore(0, 1) } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul index 520c87b92628..39a7f91b3317 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul @@ -5,9 +5,9 @@ pop(a) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // pop(caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul index 4dd86f958347..59731d61782b 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul @@ -7,9 +7,9 @@ } let x := a } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // pop(caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul index e664291f57ce..0a1e38fa6851 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul @@ -4,9 +4,9 @@ if b { pop(b) b := a } let c := b } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // let b := address() diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul index e0aa0a385b4b..442d64654994 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul @@ -6,9 +6,9 @@ default { let x := a let y := b b := a } pop(add(a, b)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := 2 diff --git a/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul index 95733af88d41..c3f6ea6a2ac3 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul @@ -6,9 +6,9 @@ mstore(add(a, a), mload(a)) sstore(a, sload(a)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // mstore(caller(), caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul index 074100d899cc..7fc927a4a267 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul @@ -7,9 +7,9 @@ } let b := x } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x // { diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul index cc009509f225..6221f11606f4 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul @@ -2,9 +2,9 @@ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) // let b := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul index e39b66b44abb..2721600038e2 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul @@ -13,9 +13,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul index 5a98c4c4d046..ed80715e9de5 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul @@ -15,9 +15,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul index f01514cc5861..a5d8198b0628 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul @@ -20,9 +20,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul index ae04949d9b1b..1b9b3a84ae8d 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul @@ -23,9 +23,9 @@ let x := b // does not rematerialize as b may be either origin() or callvalue() (btw: not caller()) let y := c // does not rematerialize as c may be either origin() or caller() } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul index 9f57a473961c..b00d50f264cc 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul @@ -4,9 +4,9 @@ let a := 0xffffffffffffffffffffff mstore(a, a) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 0xffffffffffffffffffffff // mstore(a, a) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul index 6d388c19ead3..cc8d233981b6 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul @@ -5,9 +5,9 @@ let a := 0xffffffffffffffffffffff mstore(0, a) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 0xffffffffffffffffffffff // mstore(0, 0xffffffffffffffffffffff) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul index 0d42afa97578..92aacb22973d 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul @@ -12,9 +12,9 @@ let c := sdiv(x, 4) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul index ec79843b1e49..fb73227b71bd 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul @@ -12,9 +12,9 @@ mstore(add(a, a), a) mstore(a, mload(a)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let b := 2 // mstore(2, 2) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul index 8fdd1f002f21..4255445b31b4 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul @@ -18,9 +18,9 @@ } } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := origin() // let b := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul index 1b59fd598af8..4cffa6a027a9 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul @@ -5,9 +5,9 @@ let c := a mstore(add(a, b), c) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // function f(x) -> y // { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul index 0fb8856049be..dc1d78c74a61 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul @@ -4,9 +4,9 @@ let c := a mstore(add(a, b), c) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul index fe3cf5812684..ef6e12ab6ec2 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul @@ -6,9 +6,9 @@ let d := add(b, c) pop(a) pop(b) pop(c) pop(d) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := extcodesize(0) // let b := a diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul index c10f9ddf2644..7af26af635c3 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul @@ -5,9 +5,9 @@ let b := mload(a) pop(b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // pop(1) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul index e0452c4b80f6..e7c28202c5bf 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul @@ -1,5 +1,5 @@ {} -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul index 0eeb248a62cc..071c3c463912 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul @@ -7,9 +7,9 @@ let y := add(x, 1) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul index c95cf5454cef..3d4d65f0aebe 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul @@ -12,9 +12,9 @@ } } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul index 35d4f0332a4a..04a797e20fb7 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul @@ -3,9 +3,9 @@ let b := a mstore(0, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul index 076131cde2ec..bb5f029803fa 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul @@ -4,9 +4,9 @@ a := mul(a, 2) let b := a } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := extcodesize(0) // a := mul(a, 2) diff --git a/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul index 7e60a54be77e..c8b22424ccd9 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul @@ -6,9 +6,9 @@ } } } -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { // if mul(add(calldataload(0), 2), 3) // { diff --git a/test/libyul/yulOptimizerTests/splitJoin/functions.yul b/test/libyul/yulOptimizerTests/splitJoin/functions.yul index b4ad9ed8e778..e2f554dd0eb5 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/functions.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/functions.yul @@ -8,9 +8,9 @@ sstore(b, mul(b, 2)) } } -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { // let x := f(0) // function f(y) -> r diff --git a/test/libyul/yulOptimizerTests/splitJoin/smoke.yul b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul index d59bd5644de9..4aac31ddb680 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/smoke.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul @@ -1,5 +1,5 @@ {} -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul index e4787540dc93..43b9ade6a055 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul @@ -15,9 +15,9 @@ b := mload(a) } } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul index 0617c47e469b..2e818561c656 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul @@ -6,9 +6,9 @@ a := mload(4) mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_5 := mload(4) // mstore(a_5, 0) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul index 73aee8516968..4815349d7d37 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul @@ -8,9 +8,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // if mload(1) { a := mload(3) } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul index b6eb0b6cf9cf..a0821ab3738c 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul @@ -9,9 +9,9 @@ } mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul index e747e85032b7..92a0ba8259ea 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul @@ -16,9 +16,9 @@ } mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul index 8265f500edae..63c4f8561eba 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul @@ -13,9 +13,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // switch mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul index 15e56f4d6fe2..e2f130507a11 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -3,9 +3,9 @@ a := mload(1) mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_2 := mload(1) // mstore(a_2, 0) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul index 0e777a5a5e80..e2516b45b971 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul @@ -6,9 +6,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // if mload(1) { a := mload(1) } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul index aacad246a3f7..e6e0553c1811 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul @@ -9,9 +9,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // switch mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul index fbb149c68c8b..da8d797b828a 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul @@ -20,9 +20,9 @@ let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) mstore(a,b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 // { diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul index 1c246a754d16..6053bd9ac9a8 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul @@ -7,9 +7,9 @@ b := mload(a) mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_1 := mload(0) // let b_2 := mload(a_1) diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul index 28520d3c524e..104dc2822ea0 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul @@ -10,9 +10,9 @@ } } } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // function copy(from, to) -> length // { diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul index 105970c3c24d..85e86e005efa 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul @@ -5,9 +5,9 @@ a := 4 mstore(0, a) } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul index 6770fee2b5a2..75a045d05c93 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul @@ -5,9 +5,9 @@ a := mload(add(a, 4)) mstore(0, a) } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul index 18d404df0017..1c79777d259d 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul @@ -19,9 +19,9 @@ } } } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { // function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 // { diff --git a/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul index 48b9d39cfd15..1985f6c0888e 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul @@ -2,7 +2,7 @@ let a := calldataload(0) a := a } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { let a := calldataload(0) } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul index 03dab25f91cd..be2cfe9d75d3 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul @@ -4,9 +4,9 @@ a := a_1 mstore(a_1, 0) } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { // let a := mload(1) // a := mload(0) diff --git a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul index 76a45916046a..81f82451f33f 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul @@ -7,9 +7,9 @@ a := add(a, 1) mstore(a, 1) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul index 6901f9f17889..e1e1c13ba82f 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul index fcf1a4478a66..284bdbf3d8e4 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul index 217a043ad3b9..ac7e426abef5 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul index cec4196330b3..77ac89a60d89 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul @@ -13,9 +13,9 @@ } a := add(a, 8) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/function.yul b/test/libyul/yulOptimizerTests/ssaTransform/function.yul index b6e120f4049b..d41c18b04c60 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/function.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/function.yul @@ -6,9 +6,9 @@ a := add(a, d) } } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // function f(a, b) -> c, d // { diff --git a/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul index 4f1c44123a8a..ff4a0eb1de8a 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul @@ -7,9 +7,9 @@ b := mload(a) function f() -> x, y {} } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul b/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul index ea849ead97ce..06847711e465 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul @@ -6,9 +6,9 @@ sstore(a, b) function f(t, v) -> w, z {} } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let x_1, y_2 := f(1, 2) // let x := x_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul index a48de00022b0..e075f06a2462 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul @@ -10,9 +10,9 @@ } a := add(b, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul index 7d2d7bc33e57..32d74f180be1 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul @@ -11,9 +11,9 @@ // but not above because end of block mstore(0, x) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul index f42de9976228..b41998c59235 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul @@ -6,9 +6,9 @@ mstore(c, 0) c := add(a, b) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a := 1 // let b := add(a, 2) diff --git a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul index d23d07d82e06..b52ec0c0504e 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul @@ -4,9 +4,9 @@ a := 3 a := 4 } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul index 32d6b339bb22..8604be0e32ea 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul @@ -8,9 +8,9 @@ default { a := add(a, 8) } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul index 775df55f46ac..3cf38ef38869 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul @@ -6,9 +6,9 @@ // should still create an SSA variable for a mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul index f9d48a22730b..4b1aa164371c 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul @@ -14,8 +14,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b_1:bool := true // let b:bool := b_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul index 685c42a24ba9..596f56cb0369 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b:bool := true // let c_1:bool := false diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul index 7d61b1031da8..b87b5dbbc3e5 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b:bool := true // let c_1:bool := false diff --git a/test/libyul/yulOptimizerTests/ssaTransform/used.yul b/test/libyul/yulOptimizerTests/ssaTransform/used.yul index c23bc6ee85dc..cbd0cdb25b0b 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/used.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/used.yul @@ -14,9 +14,9 @@ a := 4 mstore(a, 0) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul index 6c0ceef63a96..4ff05615717d 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul @@ -3,9 +3,9 @@ let y := calldataload(calldataload(9)) mstore(y, add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(y, 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // mstore(calldataload(calldataload(9)), add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(calldataload(calldataload(9)), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) // } diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul index e74e788aaa57..f237ea8bbcbb 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul @@ -5,9 +5,9 @@ mstore(y, add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(y, 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let x := 8 // function f() diff --git a/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul b/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul index 39c46fb962e5..b5f3dd8725be 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul @@ -2,9 +2,9 @@ let x := 8 function f() { let y := 9 } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let x := 8 // function f() diff --git a/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul b/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul index f285d6b5e414..279c0d90145b 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul @@ -18,9 +18,9 @@ extcodecopy(1, msize(), 1, 1) } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let _17_72 := pc() // let _22_75 := pc() diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul index f180d50ad88b..94da17faa09f 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul @@ -6,7 +6,7 @@ x := 1 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x := 0 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul similarity index 93% rename from test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol rename to test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul index 0d352a3f9770..edca6b642ded 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul @@ -3,7 +3,7 @@ let b := a } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let a := 42 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul index ab08b18e86ff..3b7a391815c1 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul @@ -1,5 +1,5 @@ { if 0 { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul index 836b4371c47e..bbed85bb011c 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul @@ -3,7 +3,7 @@ if x { mstore(0, 0) } if y { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x, y } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul index 7d493efbccd8..bf1046fb269f 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul @@ -1,5 +1,5 @@ { if 1 { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul index 4595fe89991a..bee5381c8627 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul @@ -2,7 +2,7 @@ let x if x { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul index 6783b6eee38a..44b2ecc61ea2 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul @@ -1,5 +1,5 @@ { if 1 { if 1 { for { mstore(0, 0) } 0 {} { mstore(2, 3) } if 0 { mstore(1, 2) } } } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul index b80d4ea99991..faa0304df945 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul @@ -4,9 +4,9 @@ case 0 { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // { y := 9 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul index 5e985d78e1a5..9238cb05763a 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { y := 10 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // { y := 10 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul index 584bbb73d460..9036db7092e1 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul @@ -4,7 +4,7 @@ case 0 { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let y := 200 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul index 0dea1229264e..5eeae71ba265 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul @@ -5,7 +5,7 @@ case "" { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let y := 200 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul index 8629be3f6db4..f8cc0558aa2e 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { y := 100 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul index 80758f0822a1..cceace3bbb41 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul @@ -2,7 +2,7 @@ function f() { let a := 1 } function g() { f() } } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul index 1c1397061b42..32e96b9f3a20 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul @@ -3,9 +3,9 @@ a := 4 let b := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a := 1 // a := 4 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul index 2dd207fbfae2..bc05f09f8a6f 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul @@ -4,9 +4,9 @@ a := f() b := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a, b // function f() -> x diff --git a/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul b/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul index c6b93a648ad0..6ad93184a699 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul @@ -3,9 +3,9 @@ let b := keccak256(1, 1) sstore(0, msize()) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // pop(keccak256(1, 1)) // sstore(0, msize()) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul b/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul index 0d49d713bae1..09d366fb2931 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul @@ -7,7 +7,7 @@ } let x := f(g(2)) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/msize.yul b/test/libyul/yulOptimizerTests/unusedPruner/msize.yul index ae6beaa7508f..05ad7b743eee 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/msize.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/msize.yul @@ -3,9 +3,9 @@ let b := mload(10) sstore(0, msize()) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // pop(mload(10)) // sstore(0, msize()) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul index 77e9306c92a4..47e114373be7 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul @@ -4,9 +4,9 @@ function f() -> x, y { } a, b := f() } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul index 01e573199e1e..86f175d69523 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul @@ -3,9 +3,9 @@ x := 1 y := 2 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let x, y // x := 1 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul index 76bd6a510fda..f727b0e93cb7 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul @@ -1,7 +1,7 @@ { let x, y } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul index b4f6a5a7e1c1..aa8ae0ad4bfc 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul @@ -2,7 +2,7 @@ function f() -> x, y { } let a, b := f() } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul index 67d692f708f6..f54087a7d8c6 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul @@ -2,9 +2,9 @@ let x, y x := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let x, y // x := 1 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul b/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul index 8d40256eb496..7bfc4d788410 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul @@ -3,7 +3,7 @@ let b := mload(10) sstore(0, 5) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { sstore(0, 5) } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul index 0890982151e2..66d16db1ed41 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul @@ -2,7 +2,7 @@ let a := 1 pop(a) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul index 9adf68be53dd..a2ac495a0405 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul index b3a05d38f5ed..9de594351b6c 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul @@ -3,7 +3,7 @@ let b := 1 mstore(0, 1) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { mstore(0, 1) } diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul index 054d0b53cfa5..8d306ca16875 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul @@ -11,9 +11,9 @@ let b := 2 let x, y := f() } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul index f9510e32f63a..9a169b4354a1 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul @@ -8,9 +8,9 @@ let r r := 4 } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul index 4911a6bd21c8..4968e2b0400f 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul @@ -3,9 +3,9 @@ let a let b } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // let x := 0 // let y := 0 diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul index fac0eaa06bf9..f8c37f2c5016 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul @@ -7,9 +7,9 @@ let s := 3 let t } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul index 62d21dd0fff4..2b7d1048f968 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul @@ -1,7 +1,7 @@ { let a } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { let a := 0 } diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul index 912246bea0db..069c68cab89e 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // let a1 := 0 // let a2:bool := false diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul index 694d21847d8c..b6ee0377f807 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul @@ -1,7 +1,7 @@ { let datasize_256 := 1 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { let datasize_1 := 1 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul index 629b5451eec2..ea4ca9ea221d 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul @@ -3,9 +3,9 @@ function f() { let f_1 } let f_10 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let f_1 // function f() diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul index 6f1d1817c6e2..1608dd6fb714 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul @@ -8,9 +8,9 @@ } let f_10 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let f_1 // function f(x) -> x_1, y diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul index 7dabb4d47e4d..c7d86ef98776 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul @@ -2,9 +2,9 @@ function f() { let x_1 := 0 } function g() { let x_2 := 0 } } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // function f() // { let x := 0 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul index acf71b1d9035..2d7dbeb4a0ac 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul @@ -1,7 +1,7 @@ { let mul_256 := 1 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { let mul_1 := 1 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul index 4104c9567799..cd32d8530f20 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul @@ -4,9 +4,9 @@ let a_4312 := 0xdeadbeef let _42 := 21718 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let a := 1 // let a_1 := 2 diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul index d96a7b0bec54..100e5ccc6b09 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul @@ -4,9 +4,9 @@ let x_2 := 3 let x_1 := 4 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let x := 1 // let x_1 := 2 diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul index e1132ad8b3fc..d197d4ff352a 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul @@ -3,9 +3,9 @@ let x_2 := 2 let x_3 := 3 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let x := 1 // let x_1 := 2 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul index fadfbd61f011..10f8814a66ed 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul @@ -3,9 +3,9 @@ val := 9876543219876543219876543219876543219876543219876543219876543219876543210 } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let val_0 := 196678011949 // let val_1 := 17592899865401375162 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul index d633e9500c45..3aef8c99ceea 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // function swap(x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3) -> a_0, a_1, a_2, a_3, b_0, b_1, b_2, b_3 // { diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul index c1a99ef34016..5ac0bfc00a8d 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul @@ -1,9 +1,9 @@ { let x := add(999999999999999999999999999999999999999999999999999999999999999, 77777777777777777777777777777777777777777777777777777777777777) } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 12390 // let _1_1 := 13186919961226471680 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul index 5bfef91b1254..b799760f839f 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul @@ -2,9 +2,9 @@ if calldataload(0) { sstore(0, 1) } if add(calldataload(0), calldataload(1)) { sstore(0, 2) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul index 016dd9bb80b1..803b289708f7 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul @@ -2,9 +2,9 @@ let or_bool := 2 if or_bool { sstore(0, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let or_bool_3_0 := 0 // let or_bool_3_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul index 22cb7631e222..d40e2283fc12 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul @@ -5,9 +5,9 @@ case 2 { sstore(2, 1) } case 3 { sstore(3, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul index 12759e230039..a9612a392776 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul @@ -5,9 +5,9 @@ case 0x01000000000000000000000000000000000000020 { sstore(2, 1) } case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul index 617eebb6416a..403c579623ac 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul @@ -6,9 +6,9 @@ case 3 { sstore(3, 1) } default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul index 20f81d614a4d..d1f45a6231fd 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul @@ -6,9 +6,9 @@ case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul index 2a42adefe333..a02243a25a10 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul @@ -2,9 +2,9 @@ switch calldataload(0) default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 5ea8486ec5f5..c8add4ce7ab1 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(isoltest ../CommonSyntaxTest.cpp ../EVMHost.cpp ../TestCase.cpp + ../TestCaseReader.cpp ../libsolidity/util/BytesUtils.cpp ../libsolidity/util/ContractABIUtils.cpp ../libsolidity/util/TestFileParser.cpp diff --git a/test/tools/afl_fuzzer.cpp b/test/tools/afl_fuzzer.cpp index 1ca5428e1c30..eff4bc55afbb 100644 --- a/test/tools/afl_fuzzer.cpp +++ b/test/tools/afl_fuzzer.cpp @@ -124,7 +124,7 @@ Allowed options)", else if (arguments.count("standard-json")) FuzzerUtil::testStandardCompiler(input, quiet); else - FuzzerUtil::testCompiler(input, optimize, quiet); + FuzzerUtil::testCompilerJsonInterface(input, optimize, quiet); } catch (...) { diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index f66c2e841653..e4de0f185f38 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -17,28 +17,87 @@ #include +#include + #include + #include #include + #include +#include + #include using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::evmasm; - -static vector s_evmVersions = { - "homestead", - "tangerineWhistle", - "spuriousDragon", - "byzantium", - "constantinople", - "petersburg", - "istanbul" +using namespace solidity::langutil; + +static vector s_evmVersions = { + EVMVersion::homestead(), + EVMVersion::tangerineWhistle(), + EVMVersion::spuriousDragon(), + EVMVersion::byzantium(), + EVMVersion::constantinople(), + EVMVersion::petersburg(), + EVMVersion::istanbul(), + EVMVersion::berlin() }; +void FuzzerUtil::testCompilerJsonInterface(string const& _input, bool _optimize, bool _quiet) +{ + if (!_quiet) + cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; + + Json::Value config = Json::objectValue; + config["language"] = "Solidity"; + config["sources"] = Json::objectValue; + config["sources"][""] = Json::objectValue; + config["sources"][""]["content"] = _input; + config["settings"] = Json::objectValue; + config["settings"]["optimizer"] = Json::objectValue; + config["settings"]["optimizer"]["enabled"] = _optimize; + config["settings"]["optimizer"]["runs"] = 200; + config["settings"]["evmVersion"] = "berlin"; + + // Enable all SourceUnit-level outputs. + config["settings"]["outputSelection"]["*"][""][0] = "*"; + // Enable all Contract-level outputs. + config["settings"]["outputSelection"]["*"]["*"][0] = "*"; + + runCompiler(jsonCompactPrint(config), _quiet); +} + +void FuzzerUtil::testCompiler(string const& _input, bool _optimize) +{ + frontend::CompilerStack compiler; + EVMVersion evmVersion = s_evmVersions[_input.size() % s_evmVersions.size()]; + frontend::OptimiserSettings optimiserSettings; + if (_optimize) + optimiserSettings = frontend::OptimiserSettings::standard(); + else + optimiserSettings = frontend::OptimiserSettings::minimal(); + compiler.setSources({{"", _input}}); + compiler.setEVMVersion(evmVersion); + compiler.setOptimiserSettings(optimiserSettings); + try + { + compiler.compile(); + } + catch (Error const&) + { + } + catch (FatalError const&) + { + } + catch (UnimplementedFeatureError const&) + { + } +} + void FuzzerUtil::runCompiler(string const& _input, bool _quiet) { if (!_quiet) @@ -73,30 +132,6 @@ void FuzzerUtil::runCompiler(string const& _input, bool _quiet) } } -void FuzzerUtil::testCompiler(string const& _input, bool _optimize, bool _quiet) -{ - if (!_quiet) - cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; - - Json::Value config = Json::objectValue; - config["language"] = "Solidity"; - config["sources"] = Json::objectValue; - config["sources"][""] = Json::objectValue; - config["sources"][""]["content"] = _input; - config["settings"] = Json::objectValue; - config["settings"]["optimizer"] = Json::objectValue; - config["settings"]["optimizer"]["enabled"] = _optimize; - config["settings"]["optimizer"]["runs"] = 200; - config["settings"]["evmVersion"] = s_evmVersions[_input.size() % s_evmVersions.size()]; - - // Enable all SourceUnit-level outputs. - config["settings"]["outputSelection"]["*"][""][0] = "*"; - // Enable all Contract-level outputs. - config["settings"]["outputSelection"]["*"]["*"][0] = "*"; - - runCompiler(jsonCompactPrint(config), _quiet); -} - void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet) { if (!_quiet) diff --git a/test/tools/fuzzer_common.h b/test/tools/fuzzer_common.h index edf196c1c81c..801c8c1c18bd 100644 --- a/test/tools/fuzzer_common.h +++ b/test/tools/fuzzer_common.h @@ -24,7 +24,8 @@ struct FuzzerUtil { static void runCompiler(std::string const& _input, bool _quiet); - static void testCompiler(std::string const& _input, bool _optimize, bool _quiet); + static void testCompilerJsonInterface(std::string const& _input, bool _optimize, bool _quiet); static void testConstantOptimizer(std::string const& _input, bool _quiet); static void testStandardCompiler(std::string const& _input, bool _quiet); + static void testCompiler(std::string const& _input, bool _optimize); }; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index e439aba64dd7..ad689ffcf6c4 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,7 +161,6 @@ TestTool::Result TestTool::process() (AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush(); m_test = m_testCaseCreator(TestCase::Config{m_path.string(), m_options.evmVersion()}); - m_test->validateSettings(); if (m_test->shouldRun()) switch (TestCase::TestResult result = m_test->run(outputMessages, " ", formatted)) { @@ -173,7 +172,7 @@ TestTool::Result TestTool::process() AnsiColorized(cout, formatted, {BOLD, CYAN}) << " Contract:" << endl; m_test->printSource(cout, " ", formatted); - m_test->printUpdatedSettings(cout, " ", formatted); + m_test->printSettings(cout, " ", formatted); cout << endl << outputMessages.str() << endl; return result == TestCase::TestResult::FatalError ? Result::Exception : Result::Failure; @@ -232,7 +231,7 @@ TestTool::Request TestTool::handleResponse(bool _exception) cout << endl; ofstream file(m_path.string(), ios::trunc); m_test->printSource(file); - m_test->printUpdatedSettings(file); + m_test->printSettings(file); file << "// ----" << endl; m_test->printUpdatedExpectations(file, "// "); return Request::Rerun; diff --git a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp index 3a2ac5f279ed..efed3769f58e 100644 --- a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/false, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/false); } return 0; } diff --git a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp index 72a59cba40eb..fc431ffe56d0 100644 --- a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/true, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/true); } return 0; } diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000000..8dd6e7564382 --- /dev/null +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,525 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost::unit_test::framework; +using namespace boost::test_tools; +using namespace solidity::langutil; +using namespace solidity::util; + +namespace fs = boost::filesystem; + +namespace solidity::phaser::test +{ + +class CountingAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + Population runNextRound(Population _population) override + { + ++m_currentRound; + return _population; + } + + size_t m_currentRound = 0; +}; + +class RandomisingAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + Population runNextRound(Population _population) override + { + return Population::makeRandom(_population.fitnessMetric(), _population.individuals().size(), 10, 20); + } +}; + +class AlgorithmRunnerFixture +{ +protected: + // NOTE: Regexes here should not contain spaces because we strip them before matching + regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"}; + regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; + + string individualPattern(Individual const& individual) const + { + ostringstream output; + output << individual.fitness << individual.chromosome; + return output.str(); + } + + string topChromosomePattern(size_t roundNumber, Individual const& individual) const + { + ostringstream output; + output << roundNumber << R"(\|[0-9.]+\|)" << individualPattern(individual); + return output.str(); + } + + bool nextLineMatches(stringstream& stream, regex const& pattern) const + { + string line; + if (getline(stream, line).fail()) + return false; + + return regex_match(stripWhitespace(line), pattern); + } + + shared_ptr m_fitnessMetric = make_shared(); + Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20); + stringstream m_output; + AlgorithmRunner::Options m_options; +}; + +class AlgorithmRunnerAutosaveFixture: public AlgorithmRunnerFixture +{ +public: + static vector chromosomeStrings(Population const& _population) + { + vector lines; + for (auto const& individual: _population.individuals()) + lines.push_back(toString(individual.chromosome)); + + return lines; + } + +protected: + TemporaryDirectory m_tempDir; + string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt"); + RandomisingAlgorithm m_algorithm; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest) + +BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 5; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + + CountingAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 5); + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 10); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_summary_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(""))); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_only_top_chromosome_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = true; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(topChromosomePattern(1, runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_number_for_top_chromosome_if_round_info_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty_and_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 3; + m_options.showRoundInfo = true; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 4; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + vector sourceStreams = { + CharStream("{mstore(10, 20)}", ""), + CharStream("{mstore(10, 20)\nsstore(10, 20)}", ""), + }; + vector programs = { + get(Program::load(sourceStreams[0])), + get(Program::load(sourceStreams[1])), + }; + vector> caches = { + make_shared(programs[0]), + make_shared(programs[1]), + }; + shared_ptr fitnessMetric = make_shared(vector>{ + make_shared(nullopt, caches[0]), + make_shared(nullopt, caches[1]), + }); + Population population = Population::makeRandom(fitnessMetric, 2, 0, 5); + + AlgorithmRunner runner(population, caches, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(caches[0]->currentRound() == m_options.maxRounds.value()); + BOOST_TEST(caches[1]->currentRound() == m_options.maxRounds.value()); + + CacheStats stats = caches[0]->gatherStats() + caches[1]->gatherStats(); + + for (size_t i = 0; i < m_options.maxRounds.value() - 1; ++i) + { + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + if (i > 0) + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + } + + BOOST_REQUIRE(stats.roundEntryCounts.size() == 2); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value() - 1) == 1); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value()) == 1); + + size_t round = m_options.maxRounds.value(); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalhits:" + toString(stats.hits)))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalmisses:" + toString(stats.misses)))); + BOOST_TEST(nextLineMatches(m_output, regex("Sizeofcachedcode:" + toString(stats.totalCodeSize)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_message_if_cache_stats_requested_but_cache_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {nullptr}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled")))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_partial_stats_and_message_if_some_caches_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + CharStream sourceStream = CharStream("{}", ""); + shared_ptr cache = make_shared(get(Program::load(sourceStream))); + + AlgorithmRunner runner(m_population, {cache, nullptr}, m_options, m_output); + BOOST_REQUIRE(cache->gatherStats().roundEntryCounts.size() == 0); + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled for 1 out of 2 programs")))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 0; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + assert(runner.population() == m_population); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population())); +} + +BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 1; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + assert(runner.population() != m_population); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population())); +} + +BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 5; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + vector originalContent = {"Original content"}; + { + ofstream tmpFile(m_autosavePath); + tmpFile << originalContent[0] << endl; + } + assert(fs::exists(m_autosavePath)); + assert(readLinesFromFile(m_autosavePath) == originalContent); + + runner.run(m_algorithm); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) != originalContent); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_not_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 5; + m_options.populationAutosaveFile = nullopt; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + + BOOST_TEST(!fs::exists(m_autosavePath)); +} + +BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested, AlgorithmRunnerFixture) +{ + Chromosome duplicate("afc"); + Population population(m_fitnessMetric, {duplicate, duplicate, duplicate}); + CountingAlgorithm algorithm; + + m_options.maxRounds = 1; + m_options.randomiseDuplicates = true; + m_options.minChromosomeLength = 50; + m_options.maxChromosomeLength = 50; + AlgorithmRunner runner(population, {}, m_options, m_output); + + runner.run(algorithm); + + auto const& newIndividuals = runner.population().individuals(); + + BOOST_TEST(newIndividuals.size() == 3); + BOOST_TEST(( + newIndividuals[0].chromosome == duplicate || + newIndividuals[1].chromosome == duplicate || + newIndividuals[2].chromosome == duplicate + )); + BOOST_TEST(newIndividuals[0] != newIndividuals[1]); + BOOST_TEST(newIndividuals[0] != newIndividuals[2]); + BOOST_TEST(newIndividuals[1] != newIndividuals[2]); + + BOOST_TEST((newIndividuals[0].chromosome.length() == 50 || newIndividuals[0].chromosome == duplicate)); + BOOST_TEST((newIndividuals[1].chromosome.length() == 50 || newIndividuals[1].chromosome == duplicate)); + BOOST_TEST((newIndividuals[2].chromosome.length() == 50 || newIndividuals[2].chromosome == duplicate)); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_requested, AlgorithmRunnerFixture) +{ + Chromosome duplicate("afc"); + Population population(m_fitnessMetric, {duplicate, duplicate, duplicate}); + CountingAlgorithm algorithm; + + m_options.maxRounds = 1; + m_options.randomiseDuplicates = false; + AlgorithmRunner runner(population, {}, m_options, m_output); + + runner.run(algorithm); + + BOOST_TEST(runner.population().individuals().size() == 3); + BOOST_TEST(runner.population().individuals()[0].chromosome == duplicate); + BOOST_TEST(runner.population().individuals()[1].chromosome == duplicate); + BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate); +} + +BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture) +{ + CharStream sourceStream = CharStream("{}", current_test_case().p_name); + vector> caches = { + make_shared(get(Program::load(sourceStream))), + make_shared(get(Program::load(sourceStream))), + }; + + m_options.maxRounds = 10; + AlgorithmRunner runner(m_population, caches, m_options, m_output); + CountingAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + BOOST_TEST(caches[0]->currentRound() == 0); + BOOST_TEST(caches[1]->currentRound() == 0); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 10); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 20); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index 23de34a2dce3..442f7967f612 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include diff --git a/test/yulPhaser/Common.cpp b/test/yulPhaser/Common.cpp index 93aa432fa996..39e6dd7843fe 100644 --- a/test/yulPhaser/Common.cpp +++ b/test/yulPhaser/Common.cpp @@ -15,52 +15,135 @@ along with solidity. If not, see . */ -#include +#include -#include +#include -#include +#include + +#include +#include + +#include +#include +#include using namespace std; -using namespace solidity; -using namespace solidity::yul; +using namespace boost::test_tools; +using namespace solidity::util; + +namespace solidity::phaser::test +{ + +class ReadLinesFromFileFixture +{ +protected: + TemporaryDirectory m_tempDir; +}; + +namespace +{ + +enum class TestEnum +{ + A, + B, + AB, + CD, + EF, + GH, +}; + +map const TestEnumToStringMap = +{ + {TestEnum::A, "a"}, + {TestEnum::B, "b"}, + {TestEnum::AB, "a b"}, + {TestEnum::CD, "c-d"}, + {TestEnum::EF, "e f"}, +}; +map const StringToTestEnumMap = invertMap(TestEnumToStringMap); + +} + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(CommonTest) -vector phaser::test::chromosomeLengths(Population const& _population) +BOOST_FIXTURE_TEST_CASE(readLinesFromFile_should_return_all_lines_from_a_text_file_as_strings_without_newlines, ReadLinesFromFileFixture) { - vector lengths; - for (auto const& individual: _population.individuals()) - lengths.push_back(individual.chromosome.length()); + { + ofstream tmpFile(m_tempDir.memberPath("test-file.txt")); + tmpFile << endl << "Line 1" << endl << endl << endl << "Line 2" << endl << "#" << endl << endl; + } - return lengths; + vector lines = readLinesFromFile(m_tempDir.memberPath("test-file.txt")); + BOOST_TEST((lines == vector{"", "Line 1", "", "", "Line 2", "#", ""})); } -map phaser::test::enumerateOptmisationSteps() +BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum) { - map stepIndices; - size_t i = 0; - for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) - stepIndices.insert({nameAndAbbreviation.first, i++}); + istringstream aStream("a"); + TestEnum aResult; + deserializeChoice(aStream, aResult, StringToTestEnumMap); + BOOST_CHECK(aResult == TestEnum::A); + BOOST_TEST(!aStream.fail()); - return stepIndices; + istringstream bStream("b"); + TestEnum bResult; + deserializeChoice(bStream, bResult, StringToTestEnumMap); + BOOST_CHECK(bResult == TestEnum::B); + BOOST_TEST(!bStream.fail()); + + istringstream cdStream("c-d"); + TestEnum cdResult; + deserializeChoice(cdStream, cdResult, StringToTestEnumMap); + BOOST_CHECK(cdResult == TestEnum::CD); + BOOST_TEST(!cdStream.fail()); } -string phaser::test::stripWhitespace(string const& input) +BOOST_AUTO_TEST_CASE(deserializeChoice_should_set_failbit_if_there_is_no_enum_corresponding_to_string) { - regex whitespaceRegex("\\s+"); - return regex_replace(input, whitespaceRegex, ""); + istringstream xyzStream("xyz"); + TestEnum xyzResult; + deserializeChoice(xyzStream, xyzResult, StringToTestEnumMap); + BOOST_TEST(xyzStream.fail()); } -size_t phaser::test::countSubstringOccurrences(string const& _inputString, string const& _substring) +BOOST_AUTO_TEST_CASE(deserializeChoice_does_not_have_to_support_strings_with_spaces) { - assert(_substring.size() > 0); + istringstream abStream("a b"); + TestEnum abResult; + deserializeChoice(abStream, abResult, StringToTestEnumMap); + BOOST_CHECK(abResult == TestEnum::A); + BOOST_TEST(!abStream.fail()); - size_t count = 0; - size_t lastOccurrence = 0; - while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != string::npos) - { - ++count; - lastOccurrence += _substring.size(); - } + istringstream efStream("e f"); + TestEnum efResult; + deserializeChoice(efStream, efResult, StringToTestEnumMap); + BOOST_TEST(efStream.fail()); +} + +BOOST_AUTO_TEST_CASE(serializeChoice_should_convert_enum_to_string) +{ + output_test_stream output; + + serializeChoice(output, TestEnum::A, TestEnumToStringMap); + BOOST_CHECK(output.is_equal("a")); + BOOST_TEST(!output.fail()); + + serializeChoice(output, TestEnum::AB, TestEnumToStringMap); + BOOST_CHECK(output.is_equal("a b")); + BOOST_TEST(!output.fail()); +} + +BOOST_AUTO_TEST_CASE(serializeChoice_should_set_failbit_if_there_is_no_string_corresponding_to_enum) +{ + output_test_stream output; + serializeChoice(output, TestEnum::GH, TestEnumToStringMap); + BOOST_TEST(output.fail()); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() - return count; } diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 58561806dcba..524ef6357e1f 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -22,22 +22,30 @@ #include +#include + #include +#include + using namespace std; using namespace solidity::langutil; +using namespace solidity::util; using namespace solidity::yul; namespace solidity::phaser::test { -class FitnessMetricFixture +class DummyProgramBasedMetric: public ProgramBasedMetric { -protected: - FitnessMetricFixture(): - m_sourceStream(SampleSourceCode, ""), - m_program(Program::load(m_sourceStream)) {} +public: + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const&) override { return 0; } +}; +class ProgramBasedMetricFixture +{ +protected: static constexpr char SampleSourceCode[] = "{\n" " function foo() -> result\n" @@ -52,57 +60,213 @@ class FitnessMetricFixture " mstore(foo(), bar())\n" "}\n"; - CharStream m_sourceStream; - Program m_program; + Program optimisedProgram(Program _program) const + { + [[maybe_unused]] size_t originalSize = _program.codeSize(); + Program result = move(_program); + result.optimise(m_chromosome.optimisationSteps()); + + // Make sure that the program and the chromosome we have chosen are suitable for the test + assert(result.codeSize() != originalSize); + + return result; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, ""); + Chromosome m_chromosome{vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; + Program m_program = get(Program::load(m_sourceStream)); + Program m_optimisedProgram = optimisedProgram(m_program); + shared_ptr m_programCache = make_shared(m_program); +}; + +class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture +{ +protected: + vector> m_simpleMetrics = { + make_shared(m_program, nullptr, 1), + make_shared(m_program, nullptr, 2), + make_shared(m_program, nullptr, 3), + }; + vector m_fitness = { + m_simpleMetrics[0]->evaluate(m_chromosome), + m_simpleMetrics[1]->evaluate(m_chromosome), + m_simpleMetrics[2]->evaluate(m_chromosome), + }; }; BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(FitnessMetricsTest) +BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) + +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramSizeTest) -BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome); - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) +{ + size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome); - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) == optimisedProgram.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); - Program programOptimisedOnce = m_program; - programOptimisedOnce.optimise(chromosome.optimisationSteps()); - Program programOptimisedTwice = programOptimisedOnce; - programOptimisedTwice.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != programOptimisedOnce.codeSize()); - assert(m_program.codeSize() != programOptimisedTwice.codeSize()); - assert(programOptimisedOnce.codeSize() != programOptimisedTwice.codeSize()); + ProgramSize metric(m_program, nullptr, 2); + size_t fitness = metric.evaluate(m_chromosome); - ProgramSize metric(m_program, 2); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness != programOptimisedOnce.codeSize()); + BOOST_TEST(fitness == programOptimisedTwice.codeSize()); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) +{ + ProgramSize metric(m_program, nullptr, 0); + size_t fitness = metric.evaluate(m_chromosome); - BOOST_TEST(metric.evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != programOptimisedOnce.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) == programOptimisedTwice.codeSize()); + BOOST_TEST(fitness == m_program.codeSize()); + BOOST_TEST(fitness != m_optimisedProgram.codeSize()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, FitnessMetricFixture) +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest) + +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); + RelativeProgramSize metric(m_program, nullptr, 3, 2); + size_t fitness = metric.evaluate(m_chromosome); - ProgramSize metric(m_program, 0); + BOOST_TEST(fitness != 1000); + BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, 1).evaluate(m_chromosome)); + BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) +{ + RelativeProgramSize metric(m_program, nullptr, 3, 0); - BOOST_TEST(metric.evaluate(chromosome) == m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != optimisedProgram.codeSize()); + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_is_zero, ProgramBasedMetricFixture) +{ + CharStream sourceStream = CharStream("{}", ""); + Program program = get(Program::load(sourceStream)); + + RelativeProgramSize metric(program, nullptr, 3); + + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("")) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("afcxjLTLTDoO")) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) +{ + double sizeRatio = static_cast(m_optimisedProgram.codeSize()) / m_program.codeSize(); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(FitnessMetricCombinationTest) + +BOOST_FIXTURE_TEST_CASE(FitnessMetricAverage_evaluate_should_compute_average_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricAverage metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == (m_fitness[0] + m_fitness[1] + m_fitness[2]) / 3); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricSum_evaluate_should_compute_sum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricSum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == m_fitness[0] + m_fitness[1] + m_fitness[2]); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMaximum_evaluate_should_compute_maximum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMaximum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == max(m_fitness[0], max(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMinimum_evaluate_should_compute_minimum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMinimum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == min(m_fitness[0], min(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); } BOOST_AUTO_TEST_SUITE_END() @@ -110,4 +274,3 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() } - diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp index aaa0a0b05d34..f7a5e7a92dc4 100644 --- a/test/yulPhaser/GeneticAlgorithms.cpp +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -15,121 +15,175 @@ along with solidity. If not, see . */ -#include +#include #include #include #include -#include - -#include #include #include -#include +#include #include using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; -using namespace solidity::langutil; -using namespace solidity::util; namespace solidity::phaser::test { -class DummyAlgorithm: public GeneticAlgorithm -{ -public: - using GeneticAlgorithm::GeneticAlgorithm; - void runNextRound() override { ++m_currentRound; } - - size_t m_currentRound = 0; -}; - class GeneticAlgorithmFixture { protected: shared_ptr m_fitnessMetric = make_shared(); - output_test_stream m_output; }; BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest) -BOOST_AUTO_TEST_SUITE(GeneticAlgorithmTest) +BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest) -BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, GeneticAlgorithmFixture) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture) { - DummyAlgorithm algorithm(Population(m_fitnessMetric), m_output); + auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 1, 1}); - BOOST_TEST(algorithm.m_currentRound == 0); - algorithm.run(10); - BOOST_TEST(algorithm.m_currentRound == 10); - algorithm.run(3); - BOOST_TEST(algorithm.m_currentRound == 13); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 3, 3, 3, 3})); } -BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, GeneticAlgorithmFixture) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { - // run() is allowed to print more but should at least print the first one - - DummyAlgorithm algorithm( - // NOTE: Chromosomes chosen so that they're not substrings of each other and are not - // words likely to appear in the output in normal circumstances. - Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), - m_output - ); + auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 7, 7}); - BOOST_TEST(m_output.is_empty()); - algorithm.run(1); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 1); - algorithm.run(3); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 4); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 7, 7, 7, 7})); } -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest) - -BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 3, 3, 3, 3})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 1, 1, 1, 1})); } -BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 7, 7}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({1.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 7, 7, 7, 7})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 5, 5, 5, 5})); } -BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture) +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(GenerationalElitistWithExclusivePoolsTest) + +BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_of_population, GeneticAlgorithmFixture) { - auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.2, + /* crossoverPoolSize = */ 0.2, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 1.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST((chromosomeLengths(newPopulation) == vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); +} - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 1, 1, 1, 1})); +BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) +{ + auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert(chromosomeLengths(population) == (vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.2, + /* crossoverPoolSize = */ 0.2, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 0.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); } -BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_mutating_the_elite, GeneticAlgorithmFixture) { - auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {1.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + auto population = Population::makeRandom(m_fitnessMetric, 20, 5, 5); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.8, + /* crossoverPoolSize = */ 0.0, + /* randomisationChance = */ 0.5, + /* deletionVsAdditionChance = */ 0.5, + /* percentGenesToRandomise = */ 1.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + SimulationRNG::reset(1); + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST(( + chromosomeLengths(newPopulation) == + vector{0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11} + )); +} - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); +BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_crossing_over_the_elite, GeneticAlgorithmFixture) +{ + auto population = ( + Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("ff")}) + + Population::makeRandom(m_fitnessMetric, 8, 6, 6) + ); + assert((chromosomeLengths(population) == vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.0, + /* crossoverPoolSize = */ 0.8, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 0.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 0.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + SimulationRNG::reset(1); + Population newPopulation = algorithm.runNextRound(population); + + vector const& newIndividuals = newPopulation.individuals(); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); + for (auto& individual: newIndividuals) + BOOST_TEST(( + individual.chromosome == Chromosome("aa") || + individual.chromosome == Chromosome("af") || + individual.chromosome == Chromosome("fa") || + individual.chromosome == Chromosome("ff") + )); + BOOST_TEST(any_of(newIndividuals.begin() + 2, newIndividuals.end(), [](auto& individual){ + return individual.chromosome != Chromosome("aa") && individual.chromosome != Chromosome("ff"); + })); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/Mutations.cpp b/test/yulPhaser/Mutations.cpp new file mode 100644 index 000000000000..df58cec540d9 --- /dev/null +++ b/test/yulPhaser/Mutations.cpp @@ -0,0 +1,394 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(MutationsTest) +BOOST_AUTO_TEST_SUITE(GeneRandomisationTest) + +BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_them_with_random_ones_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneRandomisation(0.1); + function mutation05 = geneRandomisation(0.5); + function mutation10 = geneRandomisation(1.0); + + SimulationRNG::reset(1); + BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 2); + BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 5); + BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 7); + SimulationRNG::reset(2); + BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 1); + BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 3); + BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 9); +} + +BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneRandomisation(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneDeletion(0.1); + function mutation05 = geneDeletion(0.5); + + SimulationRNG::reset(1); + // fcCUnDvejs + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcCU Dvejs"))); + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" D ejs"))); + SimulationRNG::reset(2); + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcUnDvejs"))); + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" Un s"))); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneDeletion(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_delete_all_genes_if_probability_is_one) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneDeletion(1.0); + + BOOST_TEST(mutation(chromosome) == Chromosome("")); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_new_genes_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneAddition(0.1); + function mutation05 = geneAddition(0.5); + + SimulationRNG::reset(1); + // f c C U n D v e j s + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f c C UC n D v e jx s"))); // 20% more + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("j f cu C U ne D v eI j sf"))); // 50% more + SimulationRNG::reset(2); + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f cu C U n D v e j s"))); // 10% more + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("L f ce Cv U n D v e jO s"))); // 40% more +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position) +{ + SimulationRNG::reset(7); + Chromosome chromosome("fcCUnDvejs"); + function mutation = geneAddition(0.1); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() > chromosome.length()); + + vector suffix( + mutatedChromosome.optimisationSteps().end() - chromosome.length(), + mutatedChromosome.optimisationSteps().end() + ); + BOOST_TEST(suffix == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) +{ + SimulationRNG::reset(81); + Chromosome chromosome("fcCUnDvejs"); + function mutation = geneAddition(0.1); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() > chromosome.length()); + + vector prefix( + mutatedChromosome.optimisationSteps().begin(), + mutatedChromosome.optimisationSteps().begin() + chromosome.length() + ); + BOOST_TEST(prefix == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneAddition(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_insert_genes_at_all_positions_if_probability_is_one) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneAddition(1.0); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() == chromosome.length() * 2 + 1); + + vector originalGenes; + for (size_t i = 0; i < mutatedChromosome.length() - 1; ++i) + if (i % 2 == 1) + originalGenes.push_back(mutatedChromosome.optimisationSteps()[i]); + + BOOST_TEST(Chromosome(originalGenes) == chromosome); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_given_probability) +{ + SimulationRNG::reset(1); + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 0.8, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + size_t cCount = 0; + size_t fCount = 0; + for (size_t i = 0; i < 10; ++i) + { + Chromosome mutatedChromosome = mutation(chromosome); + cCount += static_cast(mutatedChromosome == Chromosome("c")); + fCount += static_cast(mutatedChromosome == Chromosome("f")); + } + + // This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance. + BOOST_TEST(cCount == 7); + BOOST_TEST(fCount == 3); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_first_mutation_if_probability_is_one) +{ + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 1.0, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + for (size_t i = 0; i < 10; ++i) + BOOST_TEST(mutation(chromosome) == Chromosome("c")); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_if_probability_is_zero) +{ + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 0.0, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + for (size_t i = 0; i < 10; ++i) + BOOST_TEST(mutation(chromosome) == Chromosome("f")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point) +{ + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); + BOOST_TEST(result1 == Chromosome("aaaccc")); + + SimulationRNG::reset(1); + Chromosome result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); + BOOST_TEST(result2 == Chromosome("cccaaaaaaa")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_on_both_chromosomes) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); + Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aaa")); + BOOST_TEST(( + result1 == Chromosome("TTTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aTTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aaTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aaaTTTTTTTTTTTTTTTTT") + )); + BOOST_TEST(( + result2 == Chromosome("aaa") || + result2 == Chromosome("Taa") || + result2 == Chromosome("TTa") || + result2 == Chromosome("TTT") + )); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_splittable) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("aa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); + Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aa")); + BOOST_TEST(result1 != Chromosome("TTTTTTTTTTTTTTTTTTTT")); + BOOST_TEST(result2 != Chromosome("aa")); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_not_empty) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("a"), Chromosome("T")); + Chromosome result2 = crossover(Chromosome("T"), Chromosome("a")); + BOOST_TEST(result1 == Chromosome("a")); + BOOST_TEST(result2 == Chromosome("T")); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_work_even_if_one_chromosome_is_unsplittable) +{ + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == Chromosome("f")); + BOOST_CHECK(crossover(Chromosome("a"), Chromosome("ff")) == Chromosome("af")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_at_least_one_chromosome_is_empty) +{ + Chromosome empty(""); + Chromosome unsplittable("a"); + Chromosome splittable("aaaa"); + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + BOOST_CHECK(crossover(empty, empty) == empty); + BOOST_CHECK(crossover(unsplittable, empty) == empty); + BOOST_CHECK(crossover(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover(splittable, empty) == empty); + BOOST_CHECK(crossover(empty, splittable) == splittable); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_swap_chromosome_parts_at_given_point) +{ + Chromosome result1 = fixedPointCrossover(0.8)(Chromosome("aaaaaaaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.8)(Chromosome("cccccccccc"), Chromosome("aaaaaaaaaa")); + BOOST_TEST(result1 == Chromosome("aaaaaaaacc")); + BOOST_TEST(result2 == Chromosome("ccccccccaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_determine_crossover_point_based_on_length_of_shorter_chromosome) +{ + Chromosome result1 = fixedPointCrossover(0.4)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.4)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aacccccccc")); + BOOST_TEST(result2 == Chromosome("ccaaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_round_split_point) +{ + Chromosome result1 = fixedPointCrossover(0.49)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result2 = fixedPointCrossover(0.49)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aaccc")); + BOOST_TEST(result2 == Chromosome("ccaaa")); + + Chromosome result3 = fixedPointCrossover(0.50)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result4 = fixedPointCrossover(0.50)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result3 == Chromosome("aaacc")); + BOOST_TEST(result4 == Chromosome("cccaa")); + + Chromosome result5 = fixedPointCrossover(0.51)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result6 = fixedPointCrossover(0.51)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result5 == Chromosome("aaacc")); + BOOST_TEST(result6 == Chromosome("cccaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_position_zero_if_explicitly_requested) +{ + Chromosome result1 = fixedPointCrossover(0.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.0)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("cccccccccc")); + BOOST_TEST(result2 == Chromosome("aaaaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromosome_if_crossover_point_is_after_last_position) +{ + Chromosome result1 = fixedPointCrossover(1.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(1.0)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aaaaaccccc")); + BOOST_TEST(result2 == Chromosome("ccccc")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes) +{ + function crossover00 = fixedPointCrossover(0.0); + BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == Chromosome("a")); + BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == Chromosome("fff")); + + BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == Chromosome("a")); + + function crossover10 = fixedPointCrossover(1.0); + BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == Chromosome("f")); + BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == Chromosome("aff")); + + BOOST_CHECK(crossover10(Chromosome("f"), Chromosome("a")) == Chromosome("f")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_split_point_when_chromosome_empty) +{ + Chromosome empty(""); + Chromosome unsplittable("f"); + Chromosome splittable("aaaa"); + + function crossover00 = fixedPointCrossover(0.0); + BOOST_CHECK(crossover00(empty, empty) == empty); + BOOST_CHECK(crossover00(unsplittable, empty) == empty); + BOOST_CHECK(crossover00(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover00(splittable, empty) == empty); + BOOST_CHECK(crossover00(empty, splittable) == splittable); + + function crossover10 = fixedPointCrossover(1.0); + BOOST_CHECK(crossover10(empty, empty) == empty); + BOOST_CHECK(crossover10(unsplittable, empty) == empty); + BOOST_CHECK(crossover10(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover10(splittable, empty) == empty); + BOOST_CHECK(crossover10(empty, splittable) == splittable); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/PairSelections.cpp b/test/yulPhaser/PairSelections.cpp new file mode 100644 index 000000000000..64109470f6ed --- /dev/null +++ b/test/yulPhaser/PairSelections.cpp @@ -0,0 +1,185 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include + +#include + +#include +#include +#include + +using namespace std; + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(PairSelectionsTest) +BOOST_AUTO_TEST_SUITE(RandomPairSelectionTest) + +BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabilities) +{ + constexpr int collectionSize = 10; + constexpr int selectionSize = 100; + constexpr double relativeTolerance = 0.1; + constexpr double expectedValue = (collectionSize - 1) / 2.0; + constexpr double variance = (collectionSize * collectionSize - 1) / 12.0; + + SimulationRNG::reset(1); + vector> pairs = RandomPairSelection(selectionSize).materialise(collectionSize); + vector samples; + for (auto& [first, second]: pairs) + { + samples.push_back(first); + samples.push_back(second); + } + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) +{ + const size_t collectionSize = 200; + + vector> pairs = RandomPairSelection(0.5).materialise(collectionSize); + + BOOST_TEST(pairs.size() == 100); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<0>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<1>(pair) <= collectionSize; })); +} + +BOOST_AUTO_TEST_CASE(materialise_should_never_return_a_pair_of_identical_indices) +{ + vector> pairs = RandomPairSelection(0.5).materialise(100); + + BOOST_TEST(pairs.size() == 50); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [](auto const& pair){ return get<0>(pair) != get<1>(pair); })); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_number_of_pairs_thats_a_fraction_of_collection_size) +{ + BOOST_TEST(RandomPairSelection(0.0).materialise(10).size() == 0); + BOOST_TEST(RandomPairSelection(0.3).materialise(10).size() == 3); + BOOST_TEST(RandomPairSelection(0.5).materialise(10).size() == 5); + BOOST_TEST(RandomPairSelection(0.7).materialise(10).size() == 7); + BOOST_TEST(RandomPairSelection(1.0).materialise(10).size() == 10); +} + +BOOST_AUTO_TEST_CASE(materialise_should_support_number_of_pairs_bigger_than_collection_size) +{ + BOOST_TEST(RandomPairSelection(2.0).materialise(5).size() == 10); + BOOST_TEST(RandomPairSelection(1.5).materialise(10).size() == 15); + BOOST_TEST(RandomPairSelection(10.0).materialise(10).size() == 100); +} + +BOOST_AUTO_TEST_CASE(materialise_should_round_the_number_of_pairs_to_the_nearest_integer) +{ + BOOST_TEST(RandomPairSelection(0.49).materialise(3).size() == 1); + BOOST_TEST(RandomPairSelection(0.50).materialise(3).size() == 2); + BOOST_TEST(RandomPairSelection(0.51).materialise(3).size() == 2); + + BOOST_TEST(RandomPairSelection(1.51).materialise(3).size() == 5); + + BOOST_TEST(RandomPairSelection(0.01).materialise(2).size() == 0); + BOOST_TEST(RandomPairSelection(0.01).materialise(3).size() == 0); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_is_empty) +{ + BOOST_TEST(RandomPairSelection(0).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(0.5).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(1.0).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(2.0).materialise(0).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_has_one_element) +{ + BOOST_TEST(RandomPairSelection(0).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(0.5).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(1.0).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(2.0).materialise(1).empty()); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(PairMosaicSelectionTest) + +using IndexPairs = vector>; + +BOOST_AUTO_TEST_CASE(materialise) +{ + BOOST_TEST(PairMosaicSelection({{1, 1}}, 0.5).materialise(4) == IndexPairs({{1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(4) == IndexPairs({{1, 1}, {1, 1}, {1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 2.0).materialise(4) == IndexPairs({{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(2) == IndexPairs({{1, 1}, {1, 1}})); + + IndexPairs pairs1{{0, 1}, {1, 0}}; + BOOST_TEST(PairMosaicSelection(pairs1, 0.5).materialise(4) == IndexPairs({{0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 1.0).materialise(4) == IndexPairs({{0, 1}, {1, 0}, {0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 2.0).materialise(4) == IndexPairs({{0, 1}, {1, 0}, {0, 1}, {1, 0}, {0, 1}, {1, 0}, {0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 1.0).materialise(2) == IndexPairs({{0, 1}, {1, 0}})); + + IndexPairs pairs2{{3, 2}, {2, 3}, {1, 0}, {1, 1}}; + BOOST_TEST(PairMosaicSelection(pairs2, 0.5).materialise(4) == IndexPairs({{3, 2}, {2, 3}})); + BOOST_TEST(PairMosaicSelection(pairs2, 1.0).materialise(4) == IndexPairs({{3, 2}, {2, 3}, {1, 0}, {1, 1}})); + BOOST_TEST(PairMosaicSelection(pairs2, 2.0).materialise(4) == IndexPairs({{3, 2}, {2, 3}, {1, 0}, {1, 1}, {3, 2}, {2, 3}, {1, 0}, {1, 1}})); + + IndexPairs pairs3{{1, 0}, {1, 1}, {1, 0}, {1, 1}}; + BOOST_TEST(PairMosaicSelection(pairs3, 1.0).materialise(2) == IndexPairs({{1, 0}, {1, 1}})); +} + +BOOST_AUTO_TEST_CASE(materialise_should_round_indices) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 0.49).materialise(5) == IndexPairs({{4, 4}, {3, 3}})); + BOOST_TEST(PairMosaicSelection(pairs, 0.50).materialise(5) == IndexPairs({{4, 4}, {3, 3}, {2, 2}})); + BOOST_TEST(PairMosaicSelection(pairs, 0.51).materialise(5) == IndexPairs({{4, 4}, {3, 3}, {2, 2}})); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_is_empty) +{ + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(0).empty()); + BOOST_TEST(PairMosaicSelection({{1, 1}, {3, 3}}, 2.0).materialise(0).empty()); + BOOST_TEST(PairMosaicSelection({{5, 5}, {4, 4}, {3, 3}, {2, 2}}, 0.5).materialise(0).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_has_one_element) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 0.0).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 0.5).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 1.0).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 7.0).materialise(1).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_clamp_indices_at_collection_size) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 1.0).materialise(4) == IndexPairs({{3, 3}, {3, 3}, {2, 2}, {1, 1}})); + BOOST_TEST(PairMosaicSelection(pairs, 2.0).materialise(3) == IndexPairs({{2, 2}, {2, 2}, {2, 2}, {1, 1}, {0, 0}, {2, 2}})); + +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp new file mode 100644 index 000000000000..633462865a23 --- /dev/null +++ b/test/yulPhaser/Phaser.cpp @@ -0,0 +1,444 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::langutil; + +namespace fs = boost::filesystem; + +namespace solidity::phaser::test +{ + +class GeneticAlgorithmFactoryFixture +{ +protected: + GeneticAlgorithmFactory::Options m_options = { + /* algorithm = */ Algorithm::Random, + /* minChromosomeLength = */ 50, + /* maxChromosomeLength = */ 100, + /* randomElitePoolSize = */ 0.5, + /* gewepMutationPoolSize = */ 0.1, + /* gewepCrossoverPoolSize = */ 0.1, + /* gewepRandomisationChance = */ 0.6, + /* gewepDeletionVsAdditionChance = */ 0.3, + /* gewepGenesToRandomise = */ 0.4, + /* gewepGenesToAddOrDelete = */ 0.2, + }; +}; + +class FixtureWithPrograms +{ +protected: + vector m_sourceStreams = { + CharStream("{}", ""), + CharStream("{{}}", ""), + CharStream("{{{}}}", ""), + }; + vector m_programs = { + get(Program::load(m_sourceStreams[0])), + get(Program::load(m_sourceStreams[1])), + get(Program::load(m_sourceStreams[2])), + }; +}; + +class FitnessMetricFactoryFixture: public FixtureWithPrograms +{ +protected: + FitnessMetricFactory::Options m_options = { + /* metric = */ MetricChoice::CodeSize, + /* metricAggregator = */ MetricAggregatorChoice::Average, + /* relativeMetricScale = */ 5, + /* chromosomeRepetitions = */ 1, + }; +}; + +class PoulationFactoryFixture +{ +protected: + shared_ptr m_fitnessMetric = make_shared(); + PopulationFactory::Options m_options = { + /* minChromosomeLength = */ 0, + /* maxChromosomeLength = */ 0, + /* population = */ {}, + /* randomPopulation = */ {}, + /* populationFromFile = */ {}, + }; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(PhaserTest) +BOOST_AUTO_TEST_SUITE(GeneticAlgorithmFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_options_to_it, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::Random; + unique_ptr algorithm1 = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm1 != nullptr); + + auto randomAlgorithm = dynamic_cast(algorithm1.get()); + BOOST_REQUIRE(randomAlgorithm != nullptr); + BOOST_TEST(randomAlgorithm->options().elitePoolSize == m_options.randomElitePoolSize.value()); + BOOST_TEST(randomAlgorithm->options().minChromosomeLength == m_options.minChromosomeLength); + BOOST_TEST(randomAlgorithm->options().maxChromosomeLength == m_options.maxChromosomeLength); + + m_options.algorithm = Algorithm::GEWEP; + unique_ptr algorithm2 = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm2 != nullptr); + + auto gewepAlgorithm = dynamic_cast(algorithm2.get()); + BOOST_REQUIRE(gewepAlgorithm != nullptr); + BOOST_TEST(gewepAlgorithm->options().mutationPoolSize == m_options.gewepMutationPoolSize); + BOOST_TEST(gewepAlgorithm->options().crossoverPoolSize == m_options.gewepCrossoverPoolSize); + BOOST_TEST(gewepAlgorithm->options().randomisationChance == m_options.gewepRandomisationChance); + BOOST_TEST(gewepAlgorithm->options().deletionVsAdditionChance == m_options.gewepDeletionVsAdditionChance); + BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == m_options.gewepGenesToRandomise.value()); + BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == m_options.gewepGenesToAddOrDelete.value()); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_on_population_size_if_not_specified, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::Random; + m_options.randomElitePoolSize = nullopt; + unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm != nullptr); + + auto randomAlgorithm = dynamic_cast(algorithm.get()); + BOOST_REQUIRE(randomAlgorithm != nullptr); + BOOST_TEST(randomAlgorithm->options().elitePoolSize == 1.0 / 100.0); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_gewep_mutation_percentages_based_on_maximum_chromosome_length_if_not_specified, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::GEWEP; + m_options.gewepGenesToRandomise = nullopt; + m_options.gewepGenesToAddOrDelete = nullopt; + m_options.maxChromosomeLength = 125; + + unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm != nullptr); + + auto gewepAlgorithm = dynamic_cast(algorithm.get()); + BOOST_REQUIRE(gewepAlgorithm != nullptr); + BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == 1.0 / 125.0); + BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == 1.0 / 125.0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Sum; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto sumMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(sumMetric != nullptr); + BOOST_REQUIRE(sumMetric->metrics().size() == 1); + BOOST_REQUIRE(sumMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(sumMetric->metrics()[0].get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_programs[0])); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::CodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; + m_options.chromosomeRepetitions = 5; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto programSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); + BOOST_REQUIRE(programSizeMetric != nullptr); + BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; + m_options.relativeMetricScale = 10; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(relativeProgramSizeMetric->fixedPointPrecision() == m_options.relativeMetricScale); +} + +BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture) +{ + unique_ptr metric = FitnessMetricFactory::build( + m_options, + m_programs, + vector>(m_programs.size(), nullptr) + ); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size()); +} + +BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture) +{ + assert(m_programs.size() == 3); + vector> caches = { + make_shared(m_programs[0]), + make_shared(m_programs[1]), + make_shared(m_programs[2]), + }; + + m_options.metric = MetricChoice::RelativeCodeSize; + unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs, caches); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == caches.size()); + + for (size_t i = 0; i < caches.size(); ++i) + { + auto programBasedMetric = dynamic_cast(combinedMetric->metrics()[i].get()); + BOOST_REQUIRE(programBasedMetric != nullptr); + BOOST_TEST(programBasedMetric->programCache() == caches[i].get()); + } +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_an_empty_population_if_no_specific_options_given, PoulationFactoryFixture) +{ + m_options.population = {}; + m_options.randomPopulation = {}; + m_options.populationFromFile = {}; + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, vector{}) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_population_option, PoulationFactoryFixture) +{ + m_options.population = {"a", "afc", "xadd"}; + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, {Chromosome("a"), Chromosome("afc"), Chromosome("xadd")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_random_population_option, PoulationFactoryFixture) +{ + m_options.randomPopulation = {5, 3, 2}; + m_options.minChromosomeLength = 5; + m_options.maxChromosomeLength = 10; + + auto population = PopulationFactory::build(m_options, m_fitnessMetric); + + BOOST_TEST(population.individuals().size() == 10); + BOOST_TEST(all_of( + population.individuals().begin(), + population.individuals().end(), + [](auto const& individual){ return 5 <= individual.chromosome.length() && individual.chromosome.length() <= 10; } + )); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_population_from_file_option, PoulationFactoryFixture) +{ + map> fileContent = { + {"a.txt", {"a", "fff", "", "jxccLTa"}}, + {"b.txt", {}}, + {"c.txt", {""}}, + {"d.txt", {"c", "T"}}, + }; + + TemporaryDirectory tempDir; + for (auto const& [fileName, chromosomes]: fileContent) + { + ofstream tmpFile(tempDir.memberPath(fileName)); + for (auto const& chromosome: chromosomes) + tmpFile << chromosome << endl; + + m_options.populationFromFile.push_back(tempDir.memberPath(fileName)); + } + + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, { + Chromosome("a"), + Chromosome("fff"), + Chromosome(""), + Chromosome("jxccLTa"), + Chromosome(""), + Chromosome("c"), + Chromosome("T"), + }) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_throw_FileOpenError_if_population_file_does_not_exist, PoulationFactoryFixture) +{ + m_options.populationFromFile = {"a-file-that-does-not-exist.abcdefgh"}; + assert(!fs::exists(m_options.populationFromFile[0])); + + BOOST_CHECK_THROW(PopulationFactory::build(m_options, m_fitnessMetric), FileOpenError); +} + +BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, PoulationFactoryFixture) +{ + TemporaryDirectory tempDir; + { + ofstream tmpFile(tempDir.memberPath("population.txt")); + tmpFile << "axc" << endl << "fcL" << endl; + } + + m_options.population = {"axc", "fcL"}; + m_options.randomPopulation = {2}; + m_options.populationFromFile = {tempDir.memberPath("population.txt")}; + m_options.minChromosomeLength = 3; + m_options.maxChromosomeLength = 3; + + auto population = PopulationFactory::build(m_options, m_fitnessMetric); + + auto begin = population.individuals().begin(); + auto end = population.individuals().end(); + BOOST_TEST(population.individuals().size() == 6); + BOOST_TEST(all_of(begin, end, [](auto const& individual){ return individual.chromosome.length() == 3; })); + BOOST_TEST(count(begin, end, Individual(Chromosome("axc"), *m_fitnessMetric)) >= 2); + BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2); +} + + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ true}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + { + BOOST_REQUIRE(caches[i] != nullptr); + BOOST_TEST(toString(caches[i]->program()) == toString(m_programs[i])); + } +} + +BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ false}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + BOOST_TEST(caches[i] == nullptr); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) + +BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) +{ + TemporaryDirectory tempDir; + vector sources{"{}", "{{}}", "{{{}}}"}; + ProgramFactory::Options options{ + /* inputFiles = */ { + tempDir.memberPath("program1.yul"), + tempDir.memberPath("program2.yul"), + tempDir.memberPath("program3.yul"), + }, + /* prefix = */ "", + }; + + for (size_t i = 0; i < sources.size(); ++i) + { + ofstream tmpFile(options.inputFiles[i]); + tmpFile << sources[i] << endl; + } + + vector programs = ProgramFactory::build(options); + + BOOST_TEST(programs.size() == sources.size()); + for (size_t i = 0; i < sources.size(); ++i) + { + CharStream sourceStream(sources[i], options.inputFiles[i]); + BOOST_TEST(toString(programs[i]) == toString(get(Program::load(sourceStream)))); + } +} + +BOOST_AUTO_TEST_CASE(build_should_apply_prefix) +{ + TemporaryDirectory tempDir; + ProgramFactory::Options options{ + /* inputFiles = */ {tempDir.memberPath("program1.yul")}, + /* prefix = */ "f", + }; + + CharStream nestedSource("{{{let x:= 1}}}", ""); + Program nestedProgram = get(Program::load(nestedSource)); + Program flatProgram = get(Program::load(nestedSource)); + flatProgram.optimise(Chromosome("f").optimisationSteps()); + assert(toString(nestedProgram) != toString(flatProgram)); + + { + ofstream tmpFile(options.inputFiles[0]); + tmpFile << nestedSource.source() << endl; + } + + vector programs = ProgramFactory::build(options); + + BOOST_TEST(programs.size() == 1); + BOOST_TEST(toString(programs[0]) == toString(flatProgram)); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 98532c9dceee..7a9172749bb7 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -15,9 +15,10 @@ along with solidity. If not, see . */ -#include +#include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include +#include #include #include #include @@ -226,6 +228,70 @@ BOOST_FIXTURE_TEST_CASE(select_should_return_empty_population_if_selection_is_em BOOST_TEST(population.select(selection).individuals().empty()); } +BOOST_FIXTURE_TEST_CASE(mutate_should_return_population_containing_individuals_indicated_by_selection_with_mutation_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + RangeSelection selection(0.25, 0.75); + assert(selection.materialise(population.individuals().size()) == (vector{1, 2})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("fc"), Chromosome("fg")}); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + RangeSelection selection(0.0, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector{0, 1})); + + BOOST_TEST( + population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == + Population(m_fitnessMetric, {Chromosome("fa"), Chromosome("fa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_return_empty_population_if_selection_is_empty, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")}); + RangeSelection selection(0.0, 0.0); + assert(selection.materialise(population.individuals().size()).empty()); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)).individuals().empty()); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individuals_indicated_by_selection_with_crossover_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + PairMosaicSelection selection({{0, 1}, {2, 1}}, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 1}, {2, 1}, {0, 1}, {2, 1}})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ac"), Chromosome("gc"), Chromosome("gc")}); + + BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + PairMosaicSelection selection({{0, 0}, {1, 1}}, 2.0); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 0}, {1, 1}, {0, 0}, {1, 1}})); + + BOOST_TEST( + population.crossover(selection, fixedPointCrossover(0.5)) == + Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa"), Chromosome("aa"), Chromosome("aa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_return_empty_population_if_selection_is_empty, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")}); + PairMosaicSelection selection({}, 0.0); + assert(selection.materialise(population.individuals().size()).empty()); + + BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)).individuals().empty()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index 7d9c81cd3427..e69f0fc5c654 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); Program programCopy(program); @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) CharStream sourceStream(sourceCode, current_test_case().p_name); sourceStream.setPosition(5); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(CodeSize::codeSize(program.ast()) == 2); } @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(load_should_disambiguate) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. Block const& parentBlock = skipRedundantBlocks(program.ast()); @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(program.ast().statements.size() == 3); BOOST_TEST(holds_alternative(program.ast().statements[0])); @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. Block const& parentBlock = skipRedundantBlocks(program.ast()); @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed) string sourceCode("invalid program\n"); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); + BOOST_TEST(holds_alternative(Program::load(sourceStream))); } BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed) @@ -186,7 +186,133 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyze ); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); + BOOST_TEST(holds_alternative(Program::load(sourceStream))); +} + +BOOST_AUTO_TEST_CASE(load_should_accept_yul_objects_as_input) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_analysis_of_object_code_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_parsing_of_nested_object_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_extract_nested_object_with_deployed_suffix_if_present) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"C_178_deployed\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_fall_back_to_parsing_the_whole_object_if_there_is_no_subobject_with_the_right_name) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " object \"subobject\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + " object \"C_177_deployed\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); + + Block const& parentBlock = skipRedundantBlocks(get(programOrErrors).ast()); + BOOST_TEST(parentBlock.statements.size() == 1); + BOOST_TEST(holds_alternative(parentBlock.statements[0])); +} + +BOOST_AUTO_TEST_CASE(load_should_ignore_data_in_objects) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " data \"C_178_deployed\" hex\"4123\"\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(optimise) @@ -200,7 +326,7 @@ BOOST_AUTO_TEST_CASE(optimise) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); [[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast()); assert(parentBlockBefore.statements.size() == 2); @@ -231,7 +357,7 @@ BOOST_AUTO_TEST_CASE(output_operator) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // NOTE: The snippet above was chosen so that the few optimisations applied automatically by load() // as of now do not change the code significantly. If that changes, you may have to update it. @@ -250,7 +376,7 @@ BOOST_AUTO_TEST_CASE(toJson) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); Json::Value parsingResult; string errors; @@ -270,7 +396,7 @@ BOOST_AUTO_TEST_CASE(codeSize) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast())); } diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000000..1ed6024bcafa --- /dev/null +++ b/test/yulPhaser/ProgramCache.cpp @@ -0,0 +1,255 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; + +namespace solidity::phaser::test +{ + +class ProgramCacheFixture +{ +protected: + static constexpr char SampleSourceCode[] = + "{\n" + " for { let i := 0 } not(eq(i, 15)) { i := add(i, 1) }\n" + " {\n" + " let x := 1\n" + " mstore(i, 2)\n" + " }\n" + "}\n"; + + Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const + { + Program result = move(_program); + result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps()); + return result; + } + + static set cachedKeys(ProgramCache const& _programCache) + { + set keys; + for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair) + keys.insert(pair->first); + + return keys; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test"); + Program m_program = get(Program::load(m_sourceStream)); + ProgramCache m_programCache{m_program}; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(ProgramCacheTest) + +BOOST_AUTO_TEST_CASE(CacheStats_operator_plus_should_add_stats_together) +{ + CacheStats statsA{11, 12, 13, {{1, 14}, {2, 15}}}; + CacheStats statsB{21, 22, 23, {{2, 24}, {3, 25}}}; + CacheStats statsC{32, 34, 36, {{1, 14}, {2, 39}, {3, 25}}}; + + BOOST_CHECK(statsA + statsB == statsC); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture) +{ + Program expectedProgram = optimisedProgram(m_program, "IuO"); + assert(toString(expectedProgram) != toString(m_program)); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(expectedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, ProgramCacheFixture) +{ + Program programI = optimisedProgram(m_program, "I"); + Program programIu = optimisedProgram(programI, "u"); + Program programIuO = optimisedProgram(programIu, "O"); + assert(toString(m_program) != toString(programI)); + assert(toString(m_program) != toString(programIu)); + assert(toString(m_program) != toString(programIuO)); + assert(toString(programI) != toString(programIu)); + assert(toString(programI) != toString(programIuO)); + assert(toString(programIu) != toString(programIuO)); + + BOOST_REQUIRE(m_programCache.size() == 0); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(programIuO)); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI)); + BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu)); + BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture) +{ + string steps = "IuOIuO"; + + Program cachedProgram = m_programCache.optimiseProgram("IuO", 2); + + ProgramCache cacheNoRepetitions(m_program); + Program cachedProgramNoRepetitions = cacheNoRepetitions.optimiseProgram("IuOIuO"); + + BOOST_TEST(toString(cachedProgram) == toString(cachedProgramNoRepetitions)); + + for (size_t size = 1; size <= 6; ++size) + { + BOOST_REQUIRE(m_programCache.contains(steps.substr(0, size))); + BOOST_REQUIRE(cacheNoRepetitions.contains(steps.substr(0, size))); + BOOST_TEST( + toString(*cacheNoRepetitions.find(steps.substr(0, size))) == + toString(*m_programCache.find(steps.substr(0, size))) + ); + } +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move_it_to_the_next_round, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + + m_programCache.optimiseProgram("Iu"); + m_programCache.optimiseProgram("Ia"); + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + + m_programCache.optimiseProgram("IuOI"); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia", "IuO", "IuOI"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("IuO")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("IuOI")->second.roundNumber == 1); +} + +BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_TEST(m_programCache.size() == 0); + + m_programCache.optimiseProgram("Iu"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + + m_programCache.optimiseProgram("a"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.optimiseProgram("af"); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a", "af"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(2); + + BOOST_TEST(m_programCache.currentRound() == 2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"a", "af"})); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(3); + + BOOST_TEST(m_programCache.currentRound() == 3); + BOOST_TEST(m_programCache.size() == 0); +} + +BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture) +{ + size_t sizeI = optimisedProgram(m_program, "I").codeSize(); + size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize(); + size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize(); + size_t sizeL = optimisedProgram(m_program, "L").codeSize(); + size_t sizeLT = optimisedProgram(m_program, "LT").codeSize(); + + m_programCache.optimiseProgram("L"); + m_programCache.optimiseProgram("Iu"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu"})); + CacheStats expectedStats1{0, 3, sizeL + sizeI + sizeIu, {{0, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats1); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats2{2, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 4}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.startRound(1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats3{5, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 1}, {1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats3); + + m_programCache.startRound(2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + CacheStats expectedStats4{5, 4, sizeI + sizeIu + sizeIuO, {{1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats4); + + m_programCache.optimiseProgram("LT"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "LT", "I", "Iu", "IuO"})); + CacheStats expectedStats5{5, 6, sizeL + sizeLT + sizeI + sizeIu + sizeIuO, {{1, 3}, {2, 2}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats5); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Selections.cpp b/test/yulPhaser/Selections.cpp index ce870ce80273..02a85f4f33b5 100644 --- a/test/yulPhaser/Selections.cpp +++ b/test/yulPhaser/Selections.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index f158fcbf6aa4..0cdacc428d63 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include diff --git a/test/yulPhaser/TestHelpers.cpp b/test/yulPhaser/TestHelpers.cpp new file mode 100644 index 000000000000..2379016fe246 --- /dev/null +++ b/test/yulPhaser/TestHelpers.cpp @@ -0,0 +1,133 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; +using namespace solidity::phaser; +using namespace solidity::phaser::test; + +namespace fs = boost::filesystem; + +function phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome) +{ + return [_newChromosome = move(_newChromosome)](Chromosome const&) { return _newChromosome; }; +} + +function phaser::test::geneSubstitution(size_t _geneIndex, string _geneValue) +{ + return [=](Chromosome const& _chromosome) + { + vector newGenes = _chromosome.optimisationSteps(); + assert(_geneIndex < newGenes.size()); + newGenes[_geneIndex] = _geneValue; + + return Chromosome(newGenes); + }; +} + +vector phaser::test::chromosomeLengths(Population const& _population) +{ + vector lengths; + for (auto const& individual: _population.individuals()) + lengths.push_back(individual.chromosome.length()); + + return lengths; +} + +map phaser::test::enumerateOptmisationSteps() +{ + map stepIndices; + size_t i = 0; + for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) + stepIndices.insert({nameAndAbbreviation.first, i++}); + + return stepIndices; +} + +size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome const& _chromosome2) +{ + size_t count = 0; + for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i) + count += static_cast(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]); + + return count + abs(static_cast(_chromosome1.length() - _chromosome2.length())); +} + +TemporaryDirectory::TemporaryDirectory(std::string const& _prefix): + m_path((fs::temp_directory_path() / fs::unique_path(_prefix + "%%%%-%%%%-%%%%-%%%%")).string()) +{ + // Prefix should just be a file name and not contain anything that would make us step out of /tmp. + assert(fs::path(_prefix) == fs::path(_prefix).stem()); + + fs::create_directory(m_path); +} + +TemporaryDirectory::~TemporaryDirectory() +{ + // A few paranoid sanity checks just to be extra sure we're not deleting someone's homework. + assert(m_path.find(fs::temp_directory_path().string()) == 0); + assert(fs::path(m_path) != fs::temp_directory_path()); + assert(fs::path(m_path) != fs::path(m_path).root_path()); + assert(!fs::path(m_path).empty()); + + boost::system::error_code errorCode; + uintmax_t numRemoved = fs::remove_all(m_path, errorCode); + if (errorCode.value() != boost::system::errc::success) + { + cerr << "Failed to completely remove temporary directory '" << m_path << "'. "; + cerr << "Only " << numRemoved << " files were actually removed." << endl; + cerr << "Reason: " << errorCode.message() << endl; + } +} + +string TemporaryDirectory::memberPath(string const& _relativePath) const +{ + assert(fs::path(_relativePath).is_relative()); + + return (fs::path(m_path) / _relativePath).string(); +} + +string phaser::test::stripWhitespace(string const& input) +{ + regex whitespaceRegex("\\s+"); + return regex_replace(input, whitespaceRegex, ""); +} + +size_t phaser::test::countSubstringOccurrences(string const& _inputString, string const& _substring) +{ + assert(_substring.size() > 0); + + size_t count = 0; + size_t lastOccurrence = 0; + while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != string::npos) + { + ++count; + lastOccurrence += _substring.size(); + } + + return count; +} diff --git a/test/yulPhaser/Common.h b/test/yulPhaser/TestHelpers.h similarity index 72% rename from test/yulPhaser/Common.h rename to test/yulPhaser/TestHelpers.h index e73260d7adb7..2bf755b18423 100644 --- a/test/yulPhaser/Common.h +++ b/test/yulPhaser/TestHelpers.h @@ -30,9 +30,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -49,19 +51,59 @@ class ChromosomeLengthMetric: public FitnessMetric { public: using FitnessMetric::FitnessMetric; - size_t evaluate(Chromosome const& _chromosome) const override { return _chromosome.length(); } + size_t evaluate(Chromosome const& _chromosome) override { return _chromosome.length(); } }; +// MUTATIONS + +/// Mutation that always replaces the whole chromosome with the one specified in the parameter. +std::function wholeChromosomeReplacement(Chromosome _newChromosome); + +/// Mutation that always replaces the optimisation step at position @a _geneIndex with @a _geneValue. +/// +/// The chromosome must be long enough for this position to exist. +std::function geneSubstitution(size_t _geneIndex, std::string _geneValue); + // CHROMOSOME AND POPULATION HELPERS /// Returns a vector containing lengths of all chromosomes in the population (in the same order). std::vector chromosomeLengths(Population const& _population); +/// Returns the number of genes that differ between two chromosomes. +/// If the chromnosomes have different lengths, the positions that are present in only one of them +/// are counted as mismatches. +size_t countDifferences(Chromosome const& _chromosome1, Chromosome const& _chromosome2); + /// Assigns indices from 0 to N to all optimisation steps available in the OptimiserSuite. /// This is a convenience helper to make it easier to test their distribution with tools made for /// integers. std::map enumerateOptmisationSteps(); +// FILESYSTEM UTILITIES + +/** + * An object that creates a unique temporary directory and automatically deletes it and its + * content upon being destroyed. + * + * The directory is guaranteed to be newly created and empty. Directory names are generated + * randomly. If a directory with the same name already exists (very unlikely but possible) the + * object won't reuse it and will fail with an exception instead. + */ +class TemporaryDirectory +{ +public: + TemporaryDirectory(std::string const& _prefix = "yul-phaser-test-"); + ~TemporaryDirectory(); + + std::string const& path() const { return m_path; } + + /// Converts a path relative to the directory held by the object into an absolute one. + std::string memberPath(std::string const& _relativePath) const; + +private: + std::string m_path; +}; + // STRING UTILITIES /// Returns the input string with all the whitespace characters (spaces, line endings, etc.) removed. diff --git a/test/yulPhaser/CommonTest.cpp b/test/yulPhaser/TestHelpersTest.cpp similarity index 54% rename from test/yulPhaser/CommonTest.cpp rename to test/yulPhaser/TestHelpersTest.cpp index 9bcab992314f..7fc4ef0afa90 100644 --- a/test/yulPhaser/CommonTest.cpp +++ b/test/yulPhaser/TestHelpersTest.cpp @@ -15,23 +15,27 @@ along with solidity. If not, see . */ -#include +#include #include +#include #include +#include #include using namespace std; using namespace solidity::yul; using namespace boost::test_tools; +namespace fs = boost::filesystem; + namespace solidity::phaser::test { BOOST_AUTO_TEST_SUITE(Phaser) -BOOST_AUTO_TEST_SUITE(CommonTest) +BOOST_AUTO_TEST_SUITE(TestHelpersTest) BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_length) { @@ -40,6 +44,23 @@ BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_le BOOST_TEST(ChromosomeLengthMetric{}.evaluate(Chromosome("aaaaa")) == 5); } +BOOST_AUTO_TEST_CASE(wholeChromosomeReplacement_should_replace_whole_chromosome_with_another) +{ + function mutation = wholeChromosomeReplacement(Chromosome("aaa")); + BOOST_TEST(mutation(Chromosome("ccc")) == Chromosome("aaa")); +} + +BOOST_AUTO_TEST_CASE(geneSubstitution_should_change_a_single_gene_at_a_given_index) +{ + Chromosome chromosome("aaccff"); + + function mutation1 = geneSubstitution(0, chromosome.optimisationSteps()[5]); + BOOST_TEST(mutation1(chromosome) == Chromosome("faccff")); + + function mutation2 = geneSubstitution(5, chromosome.optimisationSteps()[0]); + BOOST_TEST(mutation2(chromosome) == Chromosome("aaccfa")); +} + BOOST_AUTO_TEST_CASE(chromosomeLengths_should_return_lengths_of_all_chromosomes_in_a_population) { shared_ptr fitnessMetric = make_shared(); @@ -51,6 +72,34 @@ BOOST_AUTO_TEST_CASE(chromosomeLengths_should_return_lengths_of_all_chromosomes_ BOOST_TEST((chromosomeLengths(population2) == vector{})); } +BOOST_AUTO_TEST_CASE(countDifferences_should_return_zero_for_identical_chromosomes) +{ + BOOST_TEST(countDifferences(Chromosome(), Chromosome()) == 0); + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("a")) == 0); + BOOST_TEST(countDifferences(Chromosome("afxT"), Chromosome("afxT")) == 0); +} + +BOOST_AUTO_TEST_CASE(countDifferences_should_count_mismatched_positions_in_chromosomes_of_the_same_length) +{ + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("f")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("ac")) == 1); + BOOST_TEST(countDifferences(Chromosome("ac"), Chromosome("cc")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("cc")) == 2); + BOOST_TEST(countDifferences(Chromosome("afxT"), Chromosome("Txfa")) == 4); +} + +BOOST_AUTO_TEST_CASE(countDifferences_should_count_missing_characters_as_differences) +{ + BOOST_TEST(countDifferences(Chromosome(""), Chromosome("a")) == 1); + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("")) == 2); + BOOST_TEST(countDifferences(Chromosome("aaa"), Chromosome("")) == 3); + + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("aaaa")) == 2); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("aacc")) == 2); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("cccc")) == 4); +} + BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_available_optimisation_steps) { map stepsAndAbbreviations = OptimiserSuite::stepNameToAbbreviationMap(); @@ -69,6 +118,63 @@ BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_ava } } +BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_create_and_delete_a_unique_and_empty_directory) +{ + fs::path dirPath; + { + TemporaryDirectory tempDir("temporary-directory-test-"); + dirPath = tempDir.path(); + + BOOST_TEST(dirPath.stem().string().find("temporary-directory-test-") == 0); + BOOST_TEST(fs::equivalent(dirPath.parent_path(), fs::temp_directory_path())); + BOOST_TEST(fs::is_directory(dirPath)); + BOOST_TEST(fs::is_empty(dirPath)); + } + BOOST_TEST(!fs::exists(dirPath)); +} + +BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_delete_its_directory_even_if_not_empty) +{ + fs::path dirPath; + { + TemporaryDirectory tempDir("temporary-directory-test-"); + dirPath = tempDir.path(); + + BOOST_TEST(fs::is_directory(dirPath)); + + { + ofstream tmpFile((dirPath / "test-file.txt").string()); + tmpFile << "Delete me!" << endl; + } + assert(fs::is_regular_file(dirPath / "test-file.txt")); + } + BOOST_TEST(!fs::exists(dirPath / "test-file.txt")); +} + +BOOST_AUTO_TEST_CASE(TemporaryDirectory_memberPath_should_construct_paths_relative_to_the_temporary_directory) +{ + TemporaryDirectory tempDir("temporary-directory-test-"); + + BOOST_TEST(fs::equivalent(tempDir.memberPath(""), tempDir.path())); + BOOST_TEST(fs::equivalent(tempDir.memberPath("."), tempDir.path() / fs::path("."))); + BOOST_TEST(fs::equivalent(tempDir.memberPath(".."), tempDir.path() / fs::path(".."))); + + // NOTE: fs::equivalent() only works with paths that actually exist + { + ofstream file; + file.open(tempDir.memberPath("file.txt"), ios::out); + } + BOOST_TEST(fs::equivalent(tempDir.memberPath("file.txt"), tempDir.path() / fs::path("file.txt"))); + + { + fs::create_directories(tempDir.memberPath("a/b/")); + + ofstream file; + file.open(tempDir.memberPath("a/b/file.txt"), ios::out); + } + BOOST_TEST(fs::equivalent(tempDir.memberPath("a/b/file.txt"), tempDir.path() / fs::path("a") / fs::path("b") / fs::path("file.txt"))); +} + BOOST_AUTO_TEST_CASE(stripWhitespace_should_remove_all_whitespace_characters_from_a_string) { BOOST_TEST(stripWhitespace("") == ""); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 21c0b8c913a6..241a80f46e89 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,12 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Common.h + yulPhaser/Common.cpp + yulPhaser/AlgorithmRunner.h + yulPhaser/AlgorithmRunner.cpp + yulPhaser/Phaser.h + yulPhaser/Phaser.cpp yulPhaser/GeneticAlgorithms.h yulPhaser/GeneticAlgorithms.cpp yulPhaser/Population.h @@ -23,8 +29,14 @@ add_executable(yul-phaser yulPhaser/FitnessMetrics.cpp yulPhaser/Chromosome.h yulPhaser/Chromosome.cpp + yulPhaser/Mutations.h + yulPhaser/Mutations.cpp + yulPhaser/PairSelections.h + yulPhaser/PairSelections.cpp yulPhaser/Selections.h yulPhaser/Selections.cpp + yulPhaser/ProgramCache.h + yulPhaser/ProgramCache.cpp yulPhaser/Program.h yulPhaser/Program.cpp yulPhaser/SimulationRNG.h diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000000..c402e5d844d3 --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,202 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::phaser; + +void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) +{ + populationAutosave(); + printInitialPopulation(); + cacheClear(); + + clock_t totalTimeStart = clock(); + for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) + { + clock_t roundTimeStart = clock(); + cacheStartRound(round + 1); + + m_population = _algorithm.runNextRound(m_population); + randomiseDuplicates(); + + printRoundSummary(round, roundTimeStart, totalTimeStart); + printCacheStats(); + populationAutosave(); + } +} + +void AlgorithmRunner::printRoundSummary( + size_t _round, + clock_t _roundTimeStart, + clock_t _totalTimeStart +) const +{ + clock_t now = clock(); + double roundTime = static_cast(now - _roundTimeStart) / CLOCKS_PER_SEC; + double totalTime = static_cast(now - _totalTimeStart) / CLOCKS_PER_SEC; + + if (!m_options.showOnlyTopChromosome) + { + if (m_options.showRoundInfo) + { + m_outputStream << "---------- ROUND " << _round + 1; + m_outputStream << " [round: " << fixed << setprecision(1) << roundTime << " s,"; + m_outputStream << " total: " << fixed << setprecision(1) << totalTime << " s]"; + m_outputStream << " ----------" << endl; + } + else if (m_population.individuals().size() > 0) + m_outputStream << endl; + + m_outputStream << m_population; + } + else if (m_population.individuals().size() > 0) + { + if (m_options.showRoundInfo) + { + m_outputStream << setw(5) << _round + 1 << " | "; + m_outputStream << setw(5) << fixed << setprecision(1) << totalTime << " | "; + } + + m_outputStream << m_population.individuals()[0] << endl; + } +} + +void AlgorithmRunner::printInitialPopulation() const +{ + if (!m_options.showInitialPopulation) + return; + + m_outputStream << "---------- INITIAL POPULATION ----------" << endl; + m_outputStream << m_population; +} + +void AlgorithmRunner::printCacheStats() const +{ + if (!m_options.showCacheStats) + return; + + CacheStats totalStats{}; + size_t disabledCacheCount = 0; + for (size_t i = 0; i < m_programCaches.size(); ++i) + if (m_programCaches[i] != nullptr) + totalStats += m_programCaches[i]->gatherStats(); + else + ++disabledCacheCount; + + m_outputStream << "---------- CACHE STATS ----------" << endl; + + if (disabledCacheCount < m_programCaches.size()) + { + for (auto& [round, count]: totalStats.roundEntryCounts) + m_outputStream << "Round " << round << ": " << count << " entries" << endl; + m_outputStream << "Total hits: " << totalStats.hits << endl; + m_outputStream << "Total misses: " << totalStats.misses << endl; + m_outputStream << "Size of cached code: " << totalStats.totalCodeSize << endl; + } + + if (disabledCacheCount == m_programCaches.size()) + m_outputStream << "Program cache disabled" << endl; + else if (disabledCacheCount > 0) + { + m_outputStream << "Program cache disabled for " << disabledCacheCount << " out of "; + m_outputStream << m_programCaches.size() << " programs" << endl; + } +} + +void AlgorithmRunner::populationAutosave() const +{ + if (!m_options.populationAutosaveFile.has_value()) + return; + + ofstream outputStream(m_options.populationAutosaveFile.value(), ios::out | ios::trunc); + assertThrow( + outputStream.is_open(), + FileOpenError, + "Could not open file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno) + ); + + for (auto& individual: m_population.individuals()) + outputStream << individual.chromosome << endl; + + assertThrow( + !outputStream.bad(), + FileWriteError, + "Error while writing to file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno) + ); +} + +void AlgorithmRunner::cacheClear() +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->clear(); +} + +void AlgorithmRunner::cacheStartRound(size_t _roundNumber) +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->startRound(_roundNumber); +} + +void AlgorithmRunner::randomiseDuplicates() +{ + if (m_options.randomiseDuplicates) + { + assert(m_options.minChromosomeLength.has_value()); + assert(m_options.maxChromosomeLength.has_value()); + + m_population = randomiseDuplicates( + m_population, + m_options.minChromosomeLength.value(), + m_options.maxChromosomeLength.value() + ); + } +} + +Population AlgorithmRunner::randomiseDuplicates( + Population _population, + size_t _minChromosomeLength, + size_t _maxChromosomeLength +) +{ + if (_population.individuals().size() == 0) + return _population; + + vector chromosomes{_population.individuals()[0].chromosome}; + size_t duplicateCount = 0; + for (size_t i = 1; i < _population.individuals().size(); ++i) + if (_population.individuals()[i].chromosome == _population.individuals()[i - 1].chromosome) + ++duplicateCount; + else + chromosomes.push_back(_population.individuals()[i].chromosome); + + return ( + Population(_population.fitnessMetric(), chromosomes) + + Population::makeRandom(_population.fitnessMetric(), duplicateCount, _minChromosomeLength, _maxChromosomeLength) + ); +} diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h new file mode 100644 index 000000000000..4326ce227b5e --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -0,0 +1,98 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Contains the implementation of a class that manages the execution of a genetic algorithm. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Manages a population and executes a genetic algorithm on it. It's independent of the + * implementation details of a specific algorithm which is pluggable via @a GeneticAlgorithm class. + * + * The class is also responsible for providing text feedback on the execution of the algorithm + * to the associated output stream. + */ +class AlgorithmRunner +{ +public: + struct Options + { + std::optional maxRounds = std::nullopt; + std::optional populationAutosaveFile = std::nullopt; + bool randomiseDuplicates = false; + std::optional minChromosomeLength = std::nullopt; + std::optional maxChromosomeLength = std::nullopt; + bool showInitialPopulation = false; + bool showOnlyTopChromosome = false; + bool showRoundInfo = true; + bool showCacheStats = false; + }; + + AlgorithmRunner( + Population _initialPopulation, + std::vector> _programCaches, + Options _options, + std::ostream& _outputStream + ): + m_population(std::move(_initialPopulation)), + m_programCaches(std::move(_programCaches)), + m_options(std::move(_options)), + m_outputStream(_outputStream) {} + + void run(GeneticAlgorithm& _algorithm); + + Options const& options() const { return m_options; } + Population const& population() const { return m_population; } + +private: + void printRoundSummary( + size_t _round, + clock_t _roundTimeStart, + clock_t _totalTimeStart + ) const; + void printInitialPopulation() const; + void printCacheStats() const; + void populationAutosave() const; + void randomiseDuplicates(); + void cacheClear(); + void cacheStartRound(size_t _roundNumber); + + static Population randomiseDuplicates( + Population _population, + size_t _minChromosomeLength, + size_t _maxChromosomeLength + ); + + Population m_population; + std::vector> m_programCaches; + Options m_options; + std::ostream& m_outputStream; +}; + +} diff --git a/tools/yulPhaser/Common.cpp b/tools/yulPhaser/Common.cpp new file mode 100644 index 000000000000..aa7ba85b5c26 --- /dev/null +++ b/tools/yulPhaser/Common.cpp @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +vector phaser::readLinesFromFile(string const& _path) +{ + ifstream inputStream(_path); + assertThrow(inputStream.is_open(), FileOpenError, "Could not open file '" + _path + "': " + strerror(errno)); + + string line; + vector lines; + while (!getline(inputStream, line).fail()) + lines.push_back(line); + + assertThrow(!inputStream.bad(), FileReadError, "Error while reading from file '" + _path + "': " + strerror(errno)); + + return lines; +} diff --git a/tools/yulPhaser/Common.h b/tools/yulPhaser/Common.h new file mode 100644 index 000000000000..c37afa2fbd22 --- /dev/null +++ b/tools/yulPhaser/Common.h @@ -0,0 +1,77 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Miscellaneous utilities for use in yul-phaser. + */ + +#pragma once + +#include +#include +#include +#include + +namespace solidity::phaser +{ + +/// Loads the whole file into memory, splits the content into lines, strips newlines and +/// returns the result as a list of strings. +/// +/// Throws FileOpenError if the file does not exist or cannot be opened for reading. +/// Throws FileReadError if any read operation fails during the whole process. +std::vector readLinesFromFile(std::string const& _path); + +/// Reads a token from the input stream and translates it to a string using a map. +/// Sets the failbit in the stream if there's no matching value in the map. +template +std::istream& deserializeChoice( + std::istream& _inputStream, + C& _choice, + std::map const& _stringToValueMap +) +{ + std::string deserializedValue; + _inputStream >> deserializedValue; + + auto const& pair = _stringToValueMap.find(deserializedValue); + if (pair != _stringToValueMap.end()) + _choice = pair->second; + else + _inputStream.setstate(std::ios_base::failbit); + + return _inputStream; +} + +/// Translates a value to a string using a map and prints it to the output stream. +/// Sets the failbit if the value is not in the map. +template +std::ostream& serializeChoice( + std::ostream& _outputStream, + C const& _choice, + std::map const& _valueToStringMap +) +{ + auto const& pair = _valueToStringMap.find(_choice); + if (pair != _valueToStringMap.end()) + _outputStream << pair->second; + else + _outputStream.setstate(std::ios_base::failbit); + + return _outputStream; +} + +} diff --git a/tools/yulPhaser/Exceptions.h b/tools/yulPhaser/Exceptions.h index ae75d19ef9c6..6807d7180c1c 100644 --- a/tools/yulPhaser/Exceptions.h +++ b/tools/yulPhaser/Exceptions.h @@ -22,6 +22,13 @@ namespace solidity::phaser { -struct InvalidProgram: virtual util::Exception {}; +struct BadInput: virtual util::Exception {}; +struct InvalidProgram: virtual BadInput {}; +struct NoInputFiles: virtual BadInput {}; +struct MissingFile: virtual BadInput {}; + +struct FileOpenError: virtual util::Exception {}; +struct FileReadError: virtual util::Exception {}; +struct FileWriteError: virtual util::Exception {}; } diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index be7ea549729e..5279cea723f9 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -17,14 +17,102 @@ #include +#include + +#include + using namespace std; +using namespace solidity::util; using namespace solidity::phaser; -size_t ProgramSize::evaluate(Chromosome const& _chromosome) const +Program const& ProgramBasedMetric::program() const +{ + if (m_programCache == nullptr) + return m_program.value(); + else + return m_programCache->program(); +} + +Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) { - Program programCopy = m_program; + if (m_programCache == nullptr) + return optimisedProgramNoCache(_chromosome); + + return m_programCache->optimiseProgram( + toString(_chromosome), + m_repetitionCount + ); +} + +Program ProgramBasedMetric::optimisedProgramNoCache(Chromosome const& _chromosome) const +{ + Program programCopy = program(); for (size_t i = 0; i < m_repetitionCount; ++i) programCopy.optimise(_chromosome.optimisationSteps()); - return programCopy.codeSize(); + return programCopy; +} + +size_t ProgramSize::evaluate(Chromosome const& _chromosome) +{ + return optimisedProgram(_chromosome).codeSize(); +} + +size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) +{ + size_t const scalingFactor = pow(10, m_fixedPointPrecision); + + size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(); + if (unoptimisedSize == 0) + return scalingFactor; + + size_t optimisedSize = optimisedProgram(_chromosome).codeSize(); + + return static_cast(round( + static_cast(optimisedSize) / unoptimisedSize * scalingFactor + )); +} + +size_t FitnessMetricAverage::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total / m_metrics.size(); +} + +size_t FitnessMetricSum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total; +} + +size_t FitnessMetricMaximum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t maximum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + maximum = max(maximum, m_metrics[i]->evaluate(_chromosome)); + + return maximum; +} + +size_t FitnessMetricMinimum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t minimum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + minimum = min(minimum, m_metrics[i]->evaluate(_chromosome)); + + return minimum; } diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index 7fac5f0804de..72e8111522c5 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -22,8 +22,10 @@ #include #include +#include #include +#include namespace solidity::phaser { @@ -43,25 +45,139 @@ class FitnessMetric FitnessMetric& operator=(FitnessMetric const&) = delete; virtual ~FitnessMetric() = default; - virtual size_t evaluate(Chromosome const& _chromosome) const = 0; + virtual size_t evaluate(Chromosome const& _chromosome) = 0; }; /** - * Fitness metric based on the size of a specific program after applying the optimisations from the - * chromosome to it. + * Abstract base class for fitness metrics that return values based on program size. + * + * The class provides utilities for optimising programs according to the information stored in + * chromosomes. Allows using @a ProgramCache. + * + * It can also store weights for the @a CodeSize metric. It does not do anything with + * them because it does not actually compute the code size but they are readily available for use + * by derived classes. */ -class ProgramSize: public FitnessMetric +class ProgramBasedMetric: public FitnessMetric { public: - explicit ProgramSize(Program _program, size_t _repetitionCount = 1): + explicit ProgramBasedMetric( + std::optional _program, + std::shared_ptr _programCache, + size_t _repetitionCount = 1 + ): m_program(std::move(_program)), - m_repetitionCount(_repetitionCount) {} + m_programCache(std::move(_programCache)), + m_repetitionCount(_repetitionCount) + { + assert(m_program.has_value() == (m_programCache == nullptr)); + } - size_t evaluate(Chromosome const& _chromosome) const override; + Program const& program() const; + ProgramCache const* programCache() const { return m_programCache.get(); } + size_t repetitionCount() const { return m_repetitionCount; } + + Program optimisedProgram(Chromosome const& _chromosome); + Program optimisedProgramNoCache(Chromosome const& _chromosome) const; private: - Program m_program; + std::optional m_program; + std::shared_ptr m_programCache; size_t m_repetitionCount; }; +/** + * Fitness metric based on the size of a specific program after applying the optimisations from the + * chromosome to it. + */ +class ProgramSize: public ProgramBasedMetric +{ +public: + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric based on the size of a specific program after applying the optimisations from the + * chromosome to it in relation to the original, unoptimised program. + * + * Since metric values are integers, the class multiplies the ratio by 10^@a _fixedPointPrecision + * before rounding it. + */ +class RelativeProgramSize: public ProgramBasedMetric +{ +public: + explicit RelativeProgramSize( + std::optional _program, + std::shared_ptr _programCache, + size_t _fixedPointPrecision, + size_t _repetitionCount = 1 + ): + ProgramBasedMetric(std::move(_program), std::move(_programCache), _repetitionCount), + m_fixedPointPrecision(_fixedPointPrecision) {} + + size_t fixedPointPrecision() const { return m_fixedPointPrecision; } + + size_t evaluate(Chromosome const& _chromosome) override; + +private: + size_t m_fixedPointPrecision; +}; + +/** + * Abstract base class for fitness metrics that compute their value based on values of multiple + * other, nested metrics. + */ +class FitnessMetricCombination: public FitnessMetric +{ +public: + explicit FitnessMetricCombination(std::vector> _metrics): + m_metrics(std::move(_metrics)) {} + + std::vector> const& metrics() const { return m_metrics; } + +protected: + std::vector> m_metrics; +}; + +/** + * Fitness metric that returns the average of values of its nested metrics. + */ +class FitnessMetricAverage: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the sum of values of its nested metrics. + */ +class FitnessMetricSum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the highest of values of its nested metrics. + */ +class FitnessMetricMaximum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the lowest of values of its nested metrics. + */ +class FitnessMetricMinimum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + } diff --git a/tools/yulPhaser/GeneticAlgorithms.cpp b/tools/yulPhaser/GeneticAlgorithms.cpp index 756a410f7663..432f3fe38aae 100644 --- a/tools/yulPhaser/GeneticAlgorithms.cpp +++ b/tools/yulPhaser/GeneticAlgorithms.cpp @@ -16,35 +16,51 @@ */ #include +#include #include +#include using namespace std; using namespace solidity::phaser; -void GeneticAlgorithm::run(optional _numRounds) -{ - for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) - { - runNextRound(); - - m_outputStream << "---------- ROUND " << round << " ----------" << endl; - m_outputStream << m_population; - } -} - -void RandomAlgorithm::runNextRound() +Population RandomAlgorithm::runNextRound(Population _population) { RangeSelection elite(0.0, m_options.elitePoolSize); - Population elitePopulation = m_population.select(elite); - size_t replacementCount = m_population.individuals().size() - elitePopulation.individuals().size(); + Population elitePopulation = _population.select(elite); + size_t replacementCount = _population.individuals().size() - elitePopulation.individuals().size(); - m_population = + return move(elitePopulation) + Population::makeRandom( - m_population.fitnessMetric(), + _population.fitnessMetric(), replacementCount, m_options.minChromosomeLength, m_options.maxChromosomeLength ); } + +Population GenerationalElitistWithExclusivePools::runNextRound(Population _population) +{ + double elitePoolSize = 1.0 - (m_options.mutationPoolSize + m_options.crossoverPoolSize); + RangeSelection elite(0.0, elitePoolSize); + + return + _population.select(elite) + + _population.select(elite).mutate( + RandomSelection(m_options.mutationPoolSize / elitePoolSize), + alternativeMutations( + m_options.randomisationChance, + geneRandomisation(m_options.percentGenesToRandomise), + alternativeMutations( + m_options.deletionVsAdditionChance, + geneDeletion(m_options.percentGenesToAddOrDelete), + geneAddition(m_options.percentGenesToAddOrDelete) + ) + ) + ) + + _population.select(elite).crossover( + RandomPairSelection(m_options.crossoverPoolSize / elitePoolSize), + randomPointCrossover() + ); +} diff --git a/tools/yulPhaser/GeneticAlgorithms.h b/tools/yulPhaser/GeneticAlgorithms.h index a7600d8942c0..a2a7484d674b 100644 --- a/tools/yulPhaser/GeneticAlgorithms.h +++ b/tools/yulPhaser/GeneticAlgorithms.h @@ -22,45 +22,25 @@ #include -#include -#include - namespace solidity::phaser { /** * Abstract base class for genetic algorithms. - * - * The main feature is the @a run() method that executes the algorithm, updating the internal - * population during each round and printing the results to the stream provided to the constructor. - * - * Derived classes can provide specific methods for updating the population by implementing - * the @a runNextRound() method. + * The main feature is the @a runNextRound() method that executes one round of the algorithm, + * on the supplied population. */ class GeneticAlgorithm { public: - GeneticAlgorithm(Population _initialPopulation, std::ostream& _outputStream): - m_population(std::move(_initialPopulation)), - m_outputStream(_outputStream) {} - + GeneticAlgorithm() {} GeneticAlgorithm(GeneticAlgorithm const&) = delete; GeneticAlgorithm& operator=(GeneticAlgorithm const&) = delete; virtual ~GeneticAlgorithm() = default; - Population const& population() const { return m_population; } - - void run(std::optional _numRounds = std::nullopt); - /// The method that actually implements the algorithm. Should use @a m_population as input and /// replace it with the updated state after the round. - virtual void runNextRound() = 0; - -protected: - Population m_population; - -private: - std::ostream& m_outputStream; + virtual Population runNextRound(Population _population) = 0; }; /** @@ -95,18 +75,65 @@ class RandomAlgorithm: public GeneticAlgorithm } }; - explicit RandomAlgorithm( - Population _initialPopulation, - std::ostream& _outputStream, - Options const& _options - ): - GeneticAlgorithm(_initialPopulation, _outputStream), + explicit RandomAlgorithm(Options const& _options): + m_options(_options) + { + assert(_options.isValid()); + } + + Options const& options() const { return m_options; } + + Population runNextRound(Population _population) override; + +private: + Options m_options; +}; + +/** + * A generational, elitist genetic algorithm that replaces the population by mutating and crossing + * over chromosomes from the elite. + * + * The elite consists of individuals not included in the crossover and mutation pools. + * The crossover operator used is @a randomPointCrossover. The mutation operator is randomly chosen + * from three possibilities: @a geneRandomisation, @a geneDeletion or @a geneAddition (with + * configurable probabilities). Each mutation also has a parameter determining the chance of a gene + * being affected by it. + */ +class GenerationalElitistWithExclusivePools: public GeneticAlgorithm +{ +public: + struct Options + { + double mutationPoolSize; ///< Percentage of population to regenerate using mutations in each round. + double crossoverPoolSize; ///< Percentage of population to regenerate using crossover in each round. + double randomisationChance; ///< The chance of choosing @a geneRandomisation as the mutation to perform + double deletionVsAdditionChance; ///< The chance of choosing @a geneDeletion as the mutation if randomisation was not chosen. + double percentGenesToRandomise; ///< The chance of any given gene being mutated in gene randomisation. + double percentGenesToAddOrDelete; ///< The chance of a gene being added (or deleted) in gene addition (or deletion). + + bool isValid() const + { + return ( + 0 <= mutationPoolSize && mutationPoolSize <= 1.0 && + 0 <= crossoverPoolSize && crossoverPoolSize <= 1.0 && + 0 <= randomisationChance && randomisationChance <= 1.0 && + 0 <= deletionVsAdditionChance && deletionVsAdditionChance <= 1.0 && + 0 <= percentGenesToRandomise && percentGenesToRandomise <= 1.0 && + 0 <= percentGenesToAddOrDelete && percentGenesToAddOrDelete <= 1.0 && + mutationPoolSize + crossoverPoolSize <= 1.0 + ); + } + }; + + GenerationalElitistWithExclusivePools(Options const& _options): m_options(_options) { assert(_options.isValid()); } - void runNextRound() override; + Options const& options() const { return m_options; } + + Population runNextRound(Population _population) override; private: Options m_options; diff --git a/tools/yulPhaser/Mutations.cpp b/tools/yulPhaser/Mutations.cpp new file mode 100644 index 000000000000..86f815198d07 --- /dev/null +++ b/tools/yulPhaser/Mutations.cpp @@ -0,0 +1,147 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +function phaser::geneRandomisation(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + for (auto const& step: _chromosome.optimisationSteps()) + optimisationSteps.push_back( + SimulationRNG::bernoulliTrial(_chance) ? + Chromosome::randomOptimisationStep() : + step + ); + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::geneDeletion(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + for (auto const& step: _chromosome.optimisationSteps()) + if (!SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(step); + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::geneAddition(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + + if (SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + + for (auto const& step: _chromosome.optimisationSteps()) + { + optimisationSteps.push_back(step); + if (SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + } + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::alternativeMutations( + double _firstMutationChance, + function _mutation1, + function _mutation2 +) +{ + return [=](Chromosome const& _chromosome) + { + if (SimulationRNG::bernoulliTrial(_firstMutationChance)) + return _mutation1(_chromosome); + else + return _mutation2(_chromosome); + }; +} + +namespace +{ + +Chromosome buildChromosomesBySwappingParts( + Chromosome const& _chromosome1, + Chromosome const& _chromosome2, + size_t _crossoverPoint +) +{ + assert(_crossoverPoint <= _chromosome1.length()); + assert(_crossoverPoint <= _chromosome2.length()); + + auto begin1 = _chromosome1.optimisationSteps().begin(); + auto begin2 = _chromosome2.optimisationSteps().begin(); + + return Chromosome( + vector(begin1, begin1 + _crossoverPoint) + + vector(begin2 + _crossoverPoint, _chromosome2.optimisationSteps().end()) + ); +} + +} + +function phaser::randomPointCrossover() +{ + return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2) + { + size_t minLength = min(_chromosome1.length(), _chromosome2.length()); + + // Don't use position 0 (because this just swaps the values) unless it's the only choice. + size_t minPoint = (minLength > 0? 1 : 0); + assert(minPoint <= minLength); + + size_t randomPoint = SimulationRNG::uniformInt(minPoint, minLength); + return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, randomPoint); + }; +} + +function phaser::fixedPointCrossover(double _crossoverPoint) +{ + assert(0.0 <= _crossoverPoint && _crossoverPoint <= 1.0); + + return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2) + { + size_t minLength = min(_chromosome1.length(), _chromosome2.length()); + size_t concretePoint = static_cast(round(minLength * _crossoverPoint)); + + return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, concretePoint); + }; +} diff --git a/tools/yulPhaser/Mutations.h b/tools/yulPhaser/Mutations.h new file mode 100644 index 000000000000..bff48c52b204 --- /dev/null +++ b/tools/yulPhaser/Mutations.h @@ -0,0 +1,73 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Mutation and crossover operators for use in genetic algorithms. + */ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +using Mutation = Chromosome(Chromosome const&); +using Crossover = Chromosome(Chromosome const&, Chromosome const&); + +// MUTATIONS + +/// Creates a mutation operator that iterates over all genes in a chromosome and with probability +/// @a _chance replaces a gene with a random one (which could also be the same as the original). +std::function geneRandomisation(double _chance); + +/// Creates a mutation operator that iterates over all genes in a chromosome and with probability +/// @a _chance deletes it. +std::function geneDeletion(double _chance); + +/// Creates a mutation operator that iterates over all positions in a chromosome (including spots +/// at the beginning and at the end of the sequence) and with probability @a _chance insert a new, +/// randomly chosen gene. +std::function geneAddition(double _chance); + +/// Creates a mutation operator that always applies one of the mutations passed to it. +/// The probability that the chosen mutation is the first one is @a _firstMutationChance. +/// randomly chosen gene. +std::function alternativeMutations( + double _firstMutationChance, + std::function _mutation1, + std::function _mutation2 +); + +// CROSSOVER + +/// Creates a crossover operator that randomly selects a number between 0 and 1 and uses it as the +/// position at which to perform perform @a fixedPointCrossover. +std::function randomPointCrossover(); + +/// Creates a crossover operator that always chooses a point that lies at @a _crossoverPoint +/// percent of the length of the shorter chromosome. Then creates a new chromosome by +/// splitting both inputs at the crossover point and stitching output from the first half or first +/// input and the second half of the second input. +/// +/// Avoids selecting position 0 (since this just produces a chromosome identical to the second one) +/// unless there is no other choice (i.e. one of the chromosomes is empty). +std::function fixedPointCrossover(double _crossoverPoint); + +} diff --git a/tools/yulPhaser/PairSelections.cpp b/tools/yulPhaser/PairSelections.cpp new file mode 100644 index 000000000000..8f3fd0f7f3c1 --- /dev/null +++ b/tools/yulPhaser/PairSelections.cpp @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace std; +using namespace solidity::phaser; + +vector> RandomPairSelection::materialise(size_t _poolSize) const +{ + if (_poolSize < 2) + return {}; + + size_t count = static_cast(round(_poolSize * m_selectionSize)); + + vector> selection; + for (size_t i = 0; i < count; ++i) + { + size_t index1 = SimulationRNG::uniformInt(0, _poolSize - 1); + size_t index2; + do + { + index2 = SimulationRNG::uniformInt(0, _poolSize - 1); + } while (index1 == index2); + + selection.push_back({index1, index2}); + } + + return selection; +} + +vector> PairMosaicSelection::materialise(size_t _poolSize) const +{ + if (_poolSize < 2) + return {}; + + size_t count = static_cast(round(_poolSize * m_selectionSize)); + + vector> selection; + for (size_t i = 0; i < count; ++i) + { + tuple pair = m_pattern[i % m_pattern.size()]; + selection.push_back({min(get<0>(pair), _poolSize - 1), min(get<1>(pair), _poolSize - 1)}); + } + + return selection; +} diff --git a/tools/yulPhaser/PairSelections.h b/tools/yulPhaser/PairSelections.h new file mode 100644 index 000000000000..7778d656783a --- /dev/null +++ b/tools/yulPhaser/PairSelections.h @@ -0,0 +1,99 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Contains an abstract base class representing a selection of pairs of elements from a collection + * and its concrete implementations. + */ + +#pragma once + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Abstract base class for selections of pairs elements from a collection. + * + * An instance of this class represents a specific method of selecting a set of pairs of elements + * from containers of arbitrary sizes. The selected pairs always point at a subset of the elements + * from the container but may indicate the same element more than once. The pairs themselves can + * repeat too. The selection may or may not be fixed - it's up to a specific implementation whether + * subsequent calls for the same container produce the same indices or not. + * + * Derived classes are meant to override the @a materialise() method. + * This method is expected to produce pairs of selected elements given the size of the collection. + */ +class PairSelection +{ +public: + PairSelection() = default; + PairSelection(PairSelection const&) = delete; + PairSelection& operator=(PairSelection const&) = delete; + virtual ~PairSelection() = default; + + virtual std::vector> materialise(size_t _poolSize) const = 0; +}; + +/** + * A selection that selects pairs of random elements from a container. The resulting set of pairs + * may contain the same pair more than once but does not contain pairs of duplicates. Always + * selects as many pairs as the size of the container multiplied by @a _selectionSize (unless the + * container is empty). + */ +class RandomPairSelection: public PairSelection +{ +public: + explicit RandomPairSelection(double _selectionSize): + m_selectionSize(_selectionSize) {} + + std::vector> materialise(size_t _poolSize) const override; + +private: + double m_selectionSize; +}; + +/** + * A selection that selects pairs of elements at specific, fixed positions indicated by a repeating + * "pattern". If the positions in the pattern exceed the size of the container, they are capped at + * the maximum available position. Always selects as many pairs as the size of the container + * multiplied by @a _selectionSize (unless the container is empty). + * + * E.g. if the pattern is {{0, 1}, {3, 9}} and collection size is 5, the selection will materialise + * into {{0, 1}, {3, 4}, {0, 1}, {3, 4}, {0, 1}}. If the size is 3, it will be + * {{0, 1}, {2, 2}, {0, 1}}. + */ +class PairMosaicSelection: public PairSelection +{ +public: + explicit PairMosaicSelection(std::vector> _pattern, double _selectionSize = 1.0): + m_pattern(move(_pattern)), + m_selectionSize(_selectionSize) + { + assert(m_pattern.size() > 0 || _selectionSize == 0.0); + } + + std::vector> materialise(size_t _poolSize) const override; + +private: + std::vector> m_pattern; + double m_selectionSize; +}; + +} diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp new file mode 100644 index 000000000000..2e38edbf097f --- /dev/null +++ b/tools/yulPhaser/Phaser.cpp @@ -0,0 +1,718 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::phaser; + +namespace po = boost::program_options; + +namespace +{ + +map const PhaserModeToStringMap = +{ + {PhaserMode::RunAlgorithm, "run-algorithm"}, + {PhaserMode::PrintOptimisedPrograms, "print-optimised-programs"}, + {PhaserMode::PrintOptimisedASTs, "print-optimised-asts"}, +}; +map const StringToPhaserModeMap = invertMap(PhaserModeToStringMap); + +map const AlgorithmToStringMap = +{ + {Algorithm::Random, "random"}, + {Algorithm::GEWEP, "GEWEP"}, +}; +map const StringToAlgorithmMap = invertMap(AlgorithmToStringMap); + +map MetricChoiceToStringMap = +{ + {MetricChoice::CodeSize, "code-size"}, + {MetricChoice::RelativeCodeSize, "relative-code-size"}, +}; +map const StringToMetricChoiceMap = invertMap(MetricChoiceToStringMap); + +map const MetricAggregatorChoiceToStringMap = +{ + {MetricAggregatorChoice::Average, "average"}, + {MetricAggregatorChoice::Sum, "sum"}, + {MetricAggregatorChoice::Maximum, "maximum"}, + {MetricAggregatorChoice::Minimum, "minimum"}, +}; +map const StringToMetricAggregatorChoiceMap = invertMap(MetricAggregatorChoiceToStringMap); + +} + +istream& phaser::operator>>(istream& _inputStream, PhaserMode& _phaserMode) { return deserializeChoice(_inputStream, _phaserMode, StringToPhaserModeMap); } +ostream& phaser::operator<<(ostream& _outputStream, PhaserMode _phaserMode) { return serializeChoice(_outputStream, _phaserMode, PhaserModeToStringMap); } +istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } +ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricChoice _metric) { return serializeChoice(_outputStream, _metric, MetricChoiceToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricAggregatorChoice& _aggregator) { return deserializeChoice(_inputStream, _aggregator, StringToMetricAggregatorChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricAggregatorChoice _aggregator) { return serializeChoice(_outputStream, _aggregator, MetricAggregatorChoiceToStringMap); } + +GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["algorithm"].as(), + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments.count("random-elite-pool-size") > 0 ? + _arguments["random-elite-pool-size"].as() : + optional{}, + _arguments["gewep-mutation-pool-size"].as(), + _arguments["gewep-crossover-pool-size"].as(), + _arguments["gewep-randomisation-chance"].as(), + _arguments["gewep-deletion-vs-addition-chance"].as(), + _arguments.count("gewep-genes-to-randomise") > 0 ? + _arguments["gewep-genes-to-randomise"].as() : + optional{}, + _arguments.count("gewep-genes-to-add-or-delete") > 0 ? + _arguments["gewep-genes-to-add-or-delete"].as() : + optional{}, + }; +} + +unique_ptr GeneticAlgorithmFactory::build( + Options const& _options, + size_t _populationSize +) +{ + assert(_populationSize > 0); + + switch (_options.algorithm) + { + case Algorithm::Random: + { + double elitePoolSize = 1.0 / _populationSize; + + if (_options.randomElitePoolSize.has_value()) + elitePoolSize = _options.randomElitePoolSize.value(); + + return make_unique(RandomAlgorithm::Options{ + /* elitePoolSize = */ elitePoolSize, + /* minChromosomeLength = */ _options.minChromosomeLength, + /* maxChromosomeLength = */ _options.maxChromosomeLength, + }); + } + case Algorithm::GEWEP: + { + double percentGenesToRandomise = 1.0 / _options.maxChromosomeLength; + double percentGenesToAddOrDelete = percentGenesToRandomise; + + if (_options.gewepGenesToRandomise.has_value()) + percentGenesToRandomise = _options.gewepGenesToRandomise.value(); + if (_options.gewepGenesToAddOrDelete.has_value()) + percentGenesToAddOrDelete = _options.gewepGenesToAddOrDelete.value(); + + return make_unique(GenerationalElitistWithExclusivePools::Options{ + /* mutationPoolSize = */ _options.gewepMutationPoolSize, + /* crossoverPoolSize = */ _options.gewepCrossoverPoolSize, + /* randomisationChance = */ _options.gewepRandomisationChance, + /* deletionVsAdditionChance = */ _options.gewepDeletionVsAdditionChance, + /* percentGenesToRandomise = */ percentGenesToRandomise, + /* percentGenesToAddOrDelete = */ percentGenesToAddOrDelete, + }); + } + default: + assertThrow(false, solidity::util::Exception, "Invalid Algorithm value."); + } +} + +FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["metric"].as(), + _arguments["metric-aggregator"].as(), + _arguments["relative-metric-scale"].as(), + _arguments["chromosome-repetitions"].as(), + }; +} + +unique_ptr FitnessMetricFactory::build( + Options const& _options, + vector _programs, + vector> _programCaches +) +{ + assert(_programCaches.size() == _programs.size()); + assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files."); + + vector> metrics; + switch (_options.metric) + { + case MetricChoice::CodeSize: + { + for (size_t i = 0; i < _programs.size(); ++i) + metrics.push_back(make_unique( + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), + _options.chromosomeRepetitions + )); + + break; + } + case MetricChoice::RelativeCodeSize: + { + for (size_t i = 0; i < _programs.size(); ++i) + metrics.push_back(make_unique( + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), + _options.relativeMetricScale, + _options.chromosomeRepetitions + )); + break; + } + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); + } + + switch (_options.metricAggregator) + { + case MetricAggregatorChoice::Average: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Sum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Maximum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Minimum: + return make_unique(move(metrics)); + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricAggregatorChoice value."); + } +} + +PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments.count("population") > 0 ? + _arguments["population"].as>() : + vector{}, + _arguments.count("random-population") > 0 ? + _arguments["random-population"].as>() : + vector{}, + _arguments.count("population-from-file") > 0 ? + _arguments["population-from-file"].as>() : + vector{}, + }; +} + +Population PopulationFactory::build( + Options const& _options, + shared_ptr _fitnessMetric +) +{ + Population population = buildFromStrings(_options.population, _fitnessMetric); + + size_t combinedSize = 0; + for (size_t populationSize: _options.randomPopulation) + combinedSize += populationSize; + + population = move(population) + buildRandom( + combinedSize, + _options.minChromosomeLength, + _options.maxChromosomeLength, + _fitnessMetric + ); + + for (string const& populationFilePath: _options.populationFromFile) + population = move(population) + buildFromFile(populationFilePath, _fitnessMetric); + + return population; +} + +Population PopulationFactory::buildFromStrings( + vector const& _geneSequences, + shared_ptr _fitnessMetric +) +{ + vector chromosomes; + for (string const& geneSequence: _geneSequences) + chromosomes.emplace_back(geneSequence); + + return Population(move(_fitnessMetric), move(chromosomes)); +} + +Population PopulationFactory::buildRandom( + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength, + shared_ptr _fitnessMetric +) +{ + return Population::makeRandom( + move(_fitnessMetric), + _populationSize, + _minChromosomeLength, + _maxChromosomeLength + ); +} + +Population PopulationFactory::buildFromFile( + string const& _filePath, + shared_ptr _fitnessMetric +) +{ + return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric)); +} + +ProgramCacheFactory::Options ProgramCacheFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["program-cache"].as(), + }; +} + +vector> ProgramCacheFactory::build( + Options const& _options, + vector _programs +) +{ + vector> programCaches; + for (Program& program: _programs) + programCaches.push_back(_options.programCacheEnabled ? make_shared(move(program)) : nullptr); + + return programCaches; +} + +ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["input-files"].as>(), + _arguments["prefix"].as(), + }; +} + +vector ProgramFactory::build(Options const& _options) +{ + vector inputPrograms; + for (auto& path: _options.inputFiles) + { + CharStream sourceCode = loadSource(path); + variant programOrErrors = Program::load(sourceCode); + if (holds_alternative(programOrErrors)) + { + cerr << get(programOrErrors) << endl; + assertThrow(false, InvalidProgram, "Failed to load program " + path); + } + + get(programOrErrors).optimise(Chromosome(_options.prefix).optimisationSteps()); + inputPrograms.push_back(move(get(programOrErrors))); + } + + return inputPrograms; +} + +CharStream ProgramFactory::loadSource(string const& _sourcePath) +{ + assertThrow(boost::filesystem::exists(_sourcePath), MissingFile, "Source file does not exist: " + _sourcePath); + + string sourceCode = readFileAsString(_sourcePath); + return CharStream(sourceCode, _sourcePath); +} + +void Phaser::main(int _argc, char** _argv) +{ + optional arguments = parseCommandLine(_argc, _argv); + if (!arguments.has_value()) + return; + + initialiseRNG(arguments.value()); + + runPhaser(arguments.value()); +} + +Phaser::CommandLineDescription Phaser::buildCommandLineDescription() +{ + size_t const lineLength = po::options_description::m_default_line_length; + size_t const minDescriptionLength = lineLength - 23; + + po::options_description keywordDescription( + "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" + "\n" + "Usage: yul-phaser [options] \n" + "Reads as Yul code and tries to find the best order in which to run optimisation" + " phases using a genetic algorithm.\n" + "Example:\n" + "yul-phaser program.yul\n" + "\n" + "Allowed options", + lineLength, + minDescriptionLength + ); + + po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength); + generalDescription.add_options() + ("help", "Show help message and exit.") + ("input-files", po::value>()->required()->value_name(""), "Input files.") + ( + "prefix", + po::value()->value_name("")->default_value(""), + "Initial optimisation steps automatically applied to every input program.\n" + "The result is treated as if it was the actual input, i.e. the steps are not considered " + "a part of the chromosomes and cannot be mutated. The values of relative metric values " + "are also relative to the fitness of a program with these steps applied rather than the " + "fitness of the original program.\n" + "Note that phaser always adds a 'hgo' prefix to ensure that chromosomes can " + "contain arbitrary optimisation steps. This implicit prefix cannot be changed or " + "or removed using this option. The value given here is applied after it." + ) + ("seed", po::value()->value_name(""), "Seed for the random number generator.") + ( + "rounds", + po::value()->value_name(""), + "The number of rounds after which the algorithm should stop. (default=no limit)." + ) + ( + "mode", + po::value()->value_name("")->default_value(PhaserMode::RunAlgorithm), + "Mode of operation. The default is to run the algorithm but you can also tell phaser " + "to do something else with its parameters, e.g. just print the optimised programs and exit." + ) + ; + keywordDescription.add(generalDescription); + + po::options_description algorithmDescription("ALGORITHM", lineLength, minDescriptionLength); + algorithmDescription.add_options() + ( + "algorithm", + po::value()->value_name("")->default_value(Algorithm::GEWEP), + "Algorithm" + ) + ( + "no-randomise-duplicates", + po::bool_switch(), + "By default, after each round of the algorithm duplicate chromosomes are removed from" + "the population and replaced with randomly generated ones. " + "This option disables this postprocessing." + ) + ( + "min-chromosome-length", + po::value()->value_name("")->default_value(12), + "Minimum length of randomly generated chromosomes." + ) + ( + "max-chromosome-length", + po::value()->value_name("")->default_value(30), + "Maximum length of randomly generated chromosomes." + ) + ; + keywordDescription.add(algorithmDescription); + + po::options_description gewepAlgorithmDescription("GEWEP ALGORITHM", lineLength, minDescriptionLength); + gewepAlgorithmDescription.add_options() + ( + "gewep-mutation-pool-size", + po::value()->value_name("")->default_value(0.25), + "Percentage of population to regenerate using mutations in each round." + ) + ( + "gewep-crossover-pool-size", + po::value()->value_name("")->default_value(0.25), + "Percentage of population to regenerate using crossover in each round." + ) + ( + "gewep-randomisation-chance", + po::value()->value_name("")->default_value(0.9), + "The chance of choosing gene randomisation as the mutation to perform." + ) + ( + "gewep-deletion-vs-addition-chance", + po::value()->value_name("")->default_value(0.5), + "The chance of choosing gene deletion as the mutation if randomisation was not chosen." + ) + ( + "gewep-genes-to-randomise", + po::value()->value_name(""), + "The chance of any given gene being mutated in gene randomisation. " + "(default=1/max-chromosome-length)" + ) + ( + "gewep-genes-to-add-or-delete", + po::value()->value_name(""), + "The chance of a gene being added (or deleted) in gene addition (or deletion). " + "(default=1/max-chromosome-length)" + ) + ; + keywordDescription.add(gewepAlgorithmDescription); + + po::options_description randomAlgorithmDescription("RANDOM ALGORITHM", lineLength, minDescriptionLength); + randomAlgorithmDescription.add_options() + ( + "random-elite-pool-size", + po::value()->value_name(""), + "Percentage of the population preserved in each round. " + "(default=one individual, regardless of population size)" + ) + ; + keywordDescription.add(randomAlgorithmDescription); + + po::options_description populationDescription("POPULATION", lineLength, minDescriptionLength); + populationDescription.add_options() + ( + "population", + po::value>()->multitoken()->value_name(""), + "List of chromosomes to be included in the initial population. " + "You can specify multiple values separated with spaces or invoke the option multiple times " + "and all the values will be included." + ) + ( + "random-population", + po::value>()->value_name(""), + "The number of randomly generated chromosomes to be included in the initial population." + ) + ( + "population-from-file", + po::value>()->value_name(""), + "A text file with a list of chromosomes (one per line) to be included in the initial population." + ) + ( + "population-autosave", + po::value()->value_name(""), + "If specified, the population is saved in the specified file after each round. (default=autosave disabled)" + ) + ; + keywordDescription.add(populationDescription); + + po::options_description metricsDescription("METRICS", lineLength, minDescriptionLength); + metricsDescription.add_options() + ( + "metric", + po::value()->value_name("")->default_value(MetricChoice::RelativeCodeSize), + "Metric used to evaluate the fitness of a chromosome." + ) + ( + "metric-aggregator", + po::value()->value_name("")->default_value(MetricAggregatorChoice::Average), + "Operator used to combine multiple fitness metric obtained by evaluating a chromosome " + "separately for each input program." + ) + ( + "relative-metric-scale", + po::value()->value_name("")->default_value(3), + "Scaling factor for values produced by relative fitness metrics. \n" + "Since all metrics must produce integer values, the fractional part of the result is discarded. " + "To keep the numbers meaningful, a relative metric multiples its values by a scaling factor " + "and this option specifies the exponent of this factor. " + "For example with value of 3 the factor is 10^3 = 1000 and the metric will return " + "500 to represent 0.5, 1000 for 1.0, 2000 for 2.0 and so on. " + "Using a bigger factor allows discerning smaller relative differences between chromosomes " + "but makes the numbers less readable and may also lose precision if the numbers are very large." + ) + ( + "chromosome-repetitions", + po::value()->value_name("")->default_value(1), + "Number of times to repeat the sequence optimisation steps represented by a chromosome." + ) + ; + keywordDescription.add(metricsDescription); + + po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength); + cacheDescription.add_options() + ( + "program-cache", + po::bool_switch(), + "Enables caching of intermediate programs corresponding to chromosome prefixes.\n" + "This speeds up fitness evaluation by a lot but eats tons of memory if the chromosomes are long. " + "Disabled by default since there's currently no way to set an upper limit on memory usage but " + "highly recommended if your computer has enough RAM." + ) + ; + keywordDescription.add(cacheDescription); + + po::options_description outputDescription("OUTPUT", lineLength, minDescriptionLength); + outputDescription.add_options() + ( + "show-initial-population", + po::bool_switch(), + "Print the state of the population before the algorithm starts." + ) + ( + "show-only-top-chromosome", + po::bool_switch(), + "Print only the best chromosome found in each round rather than the whole population." + ) + ( + "hide-round", + po::bool_switch(), + "Hide information about the current round (round number and elapsed time)." + ) + ( + "show-cache-stats", + po::bool_switch(), + "Print information about cache size and effectiveness after each round." + ) + ( + "show-seed", + po::bool_switch(), + "Print the selected random seed." + ) + ; + keywordDescription.add(outputDescription); + + po::positional_options_description positionalDescription; + positionalDescription.add("input-files", -1); + + return {keywordDescription, positionalDescription}; +} + +optional Phaser::parseCommandLine(int _argc, char** _argv) +{ + auto [keywordDescription, positionalDescription] = buildCommandLineDescription(); + + po::variables_map arguments; + po::notify(arguments); + + po::command_line_parser parser(_argc, _argv); + parser.options(keywordDescription).positional(positionalDescription); + po::store(parser.run(), arguments); + + if (arguments.count("help") > 0) + { + cout << keywordDescription << endl; + return nullopt; + } + + if (arguments.count("input-files") == 0) + assertThrow(false, NoInputFiles, "Missing argument: input-files."); + + return arguments; +} + +void Phaser::initialiseRNG(po::variables_map const& _arguments) +{ + uint32_t seed; + if (_arguments.count("seed") > 0) + seed = _arguments["seed"].as(); + else + seed = SimulationRNG::generateSeed(); + + SimulationRNG::reset(seed); + if (_arguments["show-seed"].as()) + cout << "Random seed: " << seed << endl; +} + +AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map const& _arguments) +{ + return { + _arguments.count("rounds") > 0 ? static_cast>(_arguments["rounds"].as()) : nullopt, + _arguments.count("population-autosave") > 0 ? static_cast>(_arguments["population-autosave"].as()) : nullopt, + !_arguments["no-randomise-duplicates"].as(), + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments["show-initial-population"].as(), + _arguments["show-only-top-chromosome"].as(), + !_arguments["hide-round"].as(), + _arguments["show-cache-stats"].as(), + }; +} + +void Phaser::runPhaser(po::variables_map const& _arguments) +{ + auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); + auto cacheOptions = ProgramCacheFactory::Options::fromCommandLine(_arguments); + auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments); + auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); + + vector programs = ProgramFactory::build(programOptions); + vector> programCaches = ProgramCacheFactory::build(cacheOptions, programs); + + unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, programs, programCaches); + Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); + + if (_arguments["mode"].as() == PhaserMode::RunAlgorithm) + runAlgorithm(_arguments, move(population), move(programCaches)); + else + printOptimisedProgramsOrASTs(_arguments, population, move(programs), _arguments["mode"].as()); +} + +void Phaser::runAlgorithm( + po::variables_map const& _arguments, + Population _population, + vector> _programCaches +) +{ + auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); + + unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( + algorithmOptions, + _population.individuals().size() + ); + + AlgorithmRunner algorithmRunner(move(_population), move(_programCaches), buildAlgorithmRunnerOptions(_arguments), cout); + algorithmRunner.run(*geneticAlgorithm); +} + +void Phaser::printOptimisedProgramsOrASTs( + po::variables_map const& _arguments, + Population const& _population, + vector _programs, + PhaserMode phaserMode +) +{ + assert(phaserMode == PhaserMode::PrintOptimisedPrograms || phaserMode == PhaserMode::PrintOptimisedASTs); + assert(_programs.size() == _arguments["input-files"].as>().size()); + + if (_population.individuals().size() == 0) + { + cout << "" << endl; + return; + } + + vector const& paths = _arguments["input-files"].as>(); + for (auto& individual: _population.individuals()) + { + cout << "Chromosome: " << individual.chromosome << endl; + + for (size_t i = 0; i < _programs.size(); ++i) + { + for (size_t j = 0; j < _arguments["chromosome-repetitions"].as(); ++j) + _programs[i].optimise(individual.chromosome.optimisationSteps()); + + cout << "Program: " << paths[i] << endl; + if (phaserMode == PhaserMode::PrintOptimisedPrograms) + cout << _programs[i] << endl; + else + cout << _programs[i].toJson() << endl; + } + } +} diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h new file mode 100644 index 000000000000..77e9e48c8eae --- /dev/null +++ b/tools/yulPhaser/Phaser.h @@ -0,0 +1,249 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Contains the main class that controls yul-phaser based on command-line parameters and + * associated factories for building instances of phaser's components. + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ + +class CharStream; + +} + +namespace solidity::phaser +{ + +class FitnessMetric; +class GeneticAlgorithm; +class Population; +class Program; +class ProgramCache; + +enum class PhaserMode +{ + RunAlgorithm, + PrintOptimisedPrograms, + PrintOptimisedASTs, +}; + +enum class Algorithm +{ + Random, + GEWEP, +}; + +enum class MetricChoice +{ + CodeSize, + RelativeCodeSize, +}; + +enum class MetricAggregatorChoice +{ + Average, + Sum, + Maximum, + Minimum, +}; + +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::PhaserMode& _phaserMode); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::PhaserMode _phaserMode); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricChoice _metric); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricAggregatorChoice& _aggregator); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricAggregatorChoice _aggregator); + +/** + * Builds and validates instances of @a GeneticAlgorithm and its derived classes. + */ +class GeneticAlgorithmFactory +{ +public: + struct Options + { + Algorithm algorithm; + size_t minChromosomeLength; + size_t maxChromosomeLength; + std::optional randomElitePoolSize; + double gewepMutationPoolSize; + double gewepCrossoverPoolSize; + double gewepRandomisationChance; + double gewepDeletionVsAdditionChance; + std::optional gewepGenesToRandomise; + std::optional gewepGenesToAddOrDelete; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::unique_ptr build( + Options const& _options, + size_t _populationSize + ); +}; + +/** + * Builds and validates instances of @a FitnessMetric and its derived classes. + */ +class FitnessMetricFactory +{ +public: + struct Options + { + MetricChoice metric; + MetricAggregatorChoice metricAggregator; + size_t relativeMetricScale; + size_t chromosomeRepetitions; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::unique_ptr build( + Options const& _options, + std::vector _programs, + std::vector> _programCaches + ); +}; + +/** + * Builds and validates instances of @a Population. + */ +class PopulationFactory +{ +public: + struct Options + { + size_t minChromosomeLength; + size_t maxChromosomeLength; + std::vector population; + std::vector randomPopulation; + std::vector populationFromFile; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static Population build( + Options const& _options, + std::shared_ptr _fitnessMetric + ); + static Population buildFromStrings( + std::vector const& _geneSequences, + std::shared_ptr _fitnessMetric + ); + static Population buildRandom( + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength, + std::shared_ptr _fitnessMetric + ); + static Population buildFromFile( + std::string const& _filePath, + std::shared_ptr _fitnessMetric + ); +}; + +/** + * Builds and validates instances of @a ProgramCache. + */ +class ProgramCacheFactory +{ +public: + struct Options + { + bool programCacheEnabled; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::vector> build( + Options const& _options, + std::vector _programs + ); +}; + +/** + * Builds and validates instances of @a Program. + */ +class ProgramFactory +{ +public: + struct Options + { + std::vector inputFiles; + std::string prefix; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::vector build(Options const& _options); + +private: + static langutil::CharStream loadSource(std::string const& _sourcePath); +}; + +/** + * Main class that controls yul-phaser based on command-line parameters. The class is responsible + * for command-line parsing, initialisation of global objects (like the random number generator), + * creating instances of main components using factories and feeding them into @a AlgorithmRunner. + */ +class Phaser +{ +public: + static void main(int argc, char** argv); + +private: + struct CommandLineDescription + { + boost::program_options::options_description keywordDescription; + boost::program_options::positional_options_description positionalDescription; + }; + + static CommandLineDescription buildCommandLineDescription(); + static std::optional parseCommandLine(int _argc, char** _argv); + static void initialiseRNG(boost::program_options::variables_map const& _arguments); + static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments); + + static void runPhaser(boost::program_options::variables_map const& _arguments); + static void runAlgorithm( + boost::program_options::variables_map const& _arguments, + Population _population, + std::vector> _programCaches + ); + static void printOptimisedProgramsOrASTs( + boost::program_options::variables_map const& _arguments, + Population const& _population, + std::vector _programs, + PhaserMode phaserMode + ); +}; + +} diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index b39f5dad5d9a..b336daf73f39 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -42,8 +43,7 @@ ostream& operator<<(ostream& _stream, Population const& _population); ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) { - _stream << "Fitness: " << _individual.fitness; - _stream << ", optimisations: " << _individual.chromosome; + _stream << _individual.fitness << " " << _individual.chromosome; return _stream; } @@ -58,7 +58,7 @@ bool phaser::isFitter(Individual const& a, Individual const& b) } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, function _chromosomeLengthGenerator ) @@ -71,7 +71,7 @@ Population Population::makeRandom( } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength @@ -93,15 +93,45 @@ Population Population::select(Selection const& _selection) const return Population(m_fitnessMetric, selectedIndividuals); } +Population Population::mutate(Selection const& _selection, function _mutation) const +{ + vector mutatedIndividuals; + for (size_t i: _selection.materialise(m_individuals.size())) + mutatedIndividuals.emplace_back(_mutation(m_individuals[i].chromosome), *m_fitnessMetric); + + return Population(m_fitnessMetric, mutatedIndividuals); +} + +Population Population::crossover(PairSelection const& _selection, function _crossover) const +{ + vector crossedIndividuals; + for (auto const& [i, j]: _selection.materialise(m_individuals.size())) + { + auto childChromosome = _crossover( + m_individuals[i].chromosome, + m_individuals[j].chromosome + ); + crossedIndividuals.emplace_back(move(childChromosome), *m_fitnessMetric); + } + + return Population(m_fitnessMetric, crossedIndividuals); +} + +namespace solidity::phaser +{ + Population operator+(Population _a, Population _b) { // This operator is meant to be used only with populations sharing the same metric (and, to make // things simple, "the same" here means the same exact object in memory). assert(_a.m_fitnessMetric == _b.m_fitnessMetric); + using ::operator+; // Import the std::vector concat operator from CommonData.h return Population(_a.m_fitnessMetric, move(_a.m_individuals) + move(_b.m_individuals)); } +} + bool Population::operator==(Population const& _other) const { // We consider populations identical only if they share the same exact instance of the metric. @@ -120,7 +150,7 @@ ostream& phaser::operator<<(ostream& _stream, Population const& _population) } vector Population::chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, vector _chromosomes ) { diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index f4f42346de22..40d51498b181 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -28,17 +29,7 @@ namespace solidity::phaser { -class Population; - -} - -// This operator+ must be declared in the global namespace. Otherwise it would shadow global -// operator+ overloads from CommonData.h (e.g. the one for vector) in the namespace it was declared in. -solidity::phaser::Population operator+(solidity::phaser::Population _a, solidity::phaser::Population _b); - -namespace solidity::phaser -{ - +class PairSelection; class Selection; /** @@ -53,7 +44,7 @@ struct Individual Individual(Chromosome _chromosome, size_t _fitness): chromosome(std::move(_chromosome)), fitness(_fitness) {} - Individual(Chromosome _chromosome, FitnessMetric const& _fitnessMetric): + Individual(Chromosome _chromosome, FitnessMetric& _fitnessMetric): chromosome(std::move(_chromosome)), fitness(_fitnessMetric.evaluate(chromosome)) {} @@ -83,7 +74,7 @@ class Population { public: explicit Population( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, std::vector _chromosomes = {} ): Population( @@ -92,21 +83,24 @@ class Population ) {} static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, std::function _chromosomeLengthGenerator ); static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength ); Population select(Selection const& _selection) const; - friend Population (::operator+)(Population _a, Population _b); + Population mutate(Selection const& _selection, std::function _mutation) const; + Population crossover(PairSelection const& _selection, std::function _crossover) const; + + friend Population operator+(Population _a, Population _b); - std::shared_ptr fitnessMetric() const { return m_fitnessMetric; } + std::shared_ptr fitnessMetric() { return m_fitnessMetric; } std::vector const& individuals() const { return m_individuals; } static size_t uniformChromosomeLength(size_t _min, size_t _max) { return SimulationRNG::uniformInt(_min, _max); } @@ -118,17 +112,17 @@ class Population friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); private: - explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): + explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): m_fitnessMetric(std::move(_fitnessMetric)), m_individuals{sortedIndividuals(std::move(_individuals))} {} static std::vector chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, std::vector _chromosomes ); static std::vector sortedIndividuals(std::vector _individuals); - std::shared_ptr m_fitnessMetric; + std::shared_ptr m_fitnessMetric; std::vector m_individuals; }; diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 42cc8bf61438..a457750d2227 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -17,17 +17,16 @@ #include -#include - #include #include -#include +#include #include #include #include #include #include +#include #include #include #include @@ -57,6 +56,16 @@ ostream& operator<<(ostream& _stream, Program const& _program); } +ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors) +{ + SourceReferenceFormatter formatter(_outputStream); + + for (auto const& error: _errors) + formatter.printErrorInformation(*error); + + return _outputStream; +} + Program::Program(Program const& program): m_ast(make_unique(get(ASTCopier{}(*program.m_ast)))), m_dialect{program.m_dialect}, @@ -64,16 +73,29 @@ Program::Program(Program const& program): { } -Program Program::load(CharStream& _sourceCode) +variant Program::load(CharStream& _sourceCode) { // ASSUMPTION: parseSource() rewinds the stream on its own Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - unique_ptr ast = parseSource(dialect, _sourceCode); - unique_ptr analysisInfo = analyzeAST(dialect, *ast); + + variant, ErrorList> astOrErrors = parseObject(dialect, _sourceCode); + if (holds_alternative(astOrErrors)) + return get(astOrErrors); + + variant, ErrorList> analysisInfoOrErrors = analyzeAST( + dialect, + *get>(astOrErrors) + ); + if (holds_alternative(analysisInfoOrErrors)) + return get(analysisInfoOrErrors); Program program( dialect, - disambiguateAST(dialect, *ast, *analysisInfo) + disambiguateAST( + dialect, + *get>(astOrErrors), + *get>(analysisInfoOrErrors) + ) ); program.optimise({ FunctionHoister::name, @@ -100,21 +122,47 @@ string Program::toJson() const return jsonPrettyPrint(serializedAst); } -unique_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) +variant, ErrorList> Program::parseObject(Dialect const& _dialect, CharStream _source) { ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(move(_source)); - Parser parser(errorReporter, _dialect); - unique_ptr ast = parser.parse(scanner, false); - assertThrow(ast != nullptr, InvalidProgram, "Error parsing source"); - assert(errorReporter.errors().empty()); - - return ast; + ObjectParser parser(errorReporter, _dialect); + shared_ptr object = parser.parse(scanner, false); + if (object == nullptr || !errorReporter.errors().empty()) + // NOTE: It's possible to get errors even if the returned object is non-null. + // For example when there are errors in a nested object. + return errors; + + Object* deployedObject = nullptr; + if (object->subObjects.size() > 0) + for (auto& subObject: object->subObjects) + // solc --ir produces an object with a subobject of the same name as the outer object + // but suffixed with "_deployed". + // The other object references the nested one which makes analysis fail. Below we try to + // extract just the nested one for that reason. This is just a heuristic. If there's no + // subobject with such a suffix we fall back to accepting the whole object as is. + if (subObject != nullptr && subObject->name.str() == object->name.str() + "_deployed") + { + deployedObject = dynamic_cast(subObject.get()); + if (deployedObject != nullptr) + break; + } + Object* selectedObject = (deployedObject != nullptr ? deployedObject : object.get()); + + // NOTE: I'm making a copy of the whole AST to get unique_ptr rather than shared_ptr. + // This is a slight performance hit but it's much less than the parsing itself. + // unique_ptr lets me be sure that two Program instances can never share the AST by mistake. + // The public API of the class does not provide access to the smart pointer so it won't be hard + // to switch to shared_ptr if the copying turns out to be an issue (though it would be better + // to refactor ObjectParser and Object to use unique_ptr instead). + auto astCopy = make_unique(get(ASTCopier{}(*selectedObject->code))); + + return variant, ErrorList>(move(astCopy)); } -unique_ptr Program::analyzeAST(Dialect const& _dialect, Block const& _ast) +variant, ErrorList> Program::analyzeAST(Dialect const& _dialect, Block const& _ast) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -122,10 +170,11 @@ unique_ptr Program::analyzeAST(Dialect const& _dialect, Block c AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect); bool analysisSuccessful = analyzer.analyze(_ast); - assertThrow(analysisSuccessful, InvalidProgram, "Error analyzing source"); - assert(errorReporter.errors().empty()); + if (!analysisSuccessful) + return errors; - return analysisInfo; + assert(errorReporter.errors().empty()); + return variant, ErrorList>(move(analysisInfo)); } unique_ptr Program::disambiguateAST( diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 5e240e98dc3f..10fed6de2f58 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -20,10 +20,13 @@ #include #include +#include + #include #include #include #include +#include #include namespace solidity::langutil @@ -41,6 +44,13 @@ struct Dialect; } +namespace std +{ + +std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors); + +} + namespace solidity::phaser { @@ -65,7 +75,7 @@ class Program Program operator=(Program const& program) = delete; Program operator=(Program&& program) = delete; - static Program load(langutil::CharStream& _sourceCode); + static std::variant load(langutil::CharStream& _sourceCode); void optimise(std::vector const& _optimisationSteps); size_t codeSize() const { return computeCodeSize(*m_ast); } @@ -84,11 +94,11 @@ class Program m_nameDispenser(_dialect, *m_ast, {}) {} - static std::unique_ptr parseSource( + static std::variant, langutil::ErrorList> parseObject( yul::Dialect const& _dialect, langutil::CharStream _source ); - static std::unique_ptr analyzeAST( + static std::variant, langutil::ErrorList> analyzeAST( yul::Dialect const& _dialect, yul::Block const& _ast ); diff --git a/tools/yulPhaser/ProgramCache.cpp b/tools/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000000..7acec16bdfea --- /dev/null +++ b/tools/yulPhaser/ProgramCache.cpp @@ -0,0 +1,151 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +using namespace std; +using namespace solidity::yul; +using namespace solidity::phaser; + +CacheStats& CacheStats::operator+=(CacheStats const& _other) +{ + hits += _other.hits; + misses += _other.misses; + totalCodeSize += _other.totalCodeSize; + + for (auto& [round, count]: _other.roundEntryCounts) + if (roundEntryCounts.find(round) != roundEntryCounts.end()) + roundEntryCounts.at(round) += count; + else + roundEntryCounts.insert({round, count}); + + return *this; +} + +bool CacheStats::operator==(CacheStats const& _other) const +{ + return + hits == _other.hits && + misses == _other.misses && + totalCodeSize == _other.totalCodeSize && + roundEntryCounts == _other.roundEntryCounts; +} + +Program ProgramCache::optimiseProgram( + string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount +) +{ + string targetOptimisations = _abbreviatedOptimisationSteps; + for (size_t i = 1; i < _repetitionCount; ++i) + targetOptimisations += _abbreviatedOptimisationSteps; + + size_t prefixSize = 0; + for (size_t i = 1; i <= targetOptimisations.size(); ++i) + { + auto const& pair = m_entries.find(targetOptimisations.substr(0, i)); + if (pair != m_entries.end()) + { + pair->second.roundNumber = m_currentRound; + ++prefixSize; + ++m_hits; + } + else + break; + } + + Program intermediateProgram = ( + prefixSize == 0 ? + m_program : + m_entries.at(targetOptimisations.substr(0, prefixSize)).program + ); + + for (size_t i = prefixSize + 1; i <= targetOptimisations.size(); ++i) + { + string stepName = OptimiserSuite::stepAbbreviationToNameMap().at(targetOptimisations[i - 1]); + intermediateProgram.optimise({stepName}); + + m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}}); + ++m_misses; + } + + return intermediateProgram; +} + +void ProgramCache::startRound(size_t _roundNumber) +{ + assert(_roundNumber > m_currentRound); + m_currentRound = _roundNumber; + + for (auto pair = m_entries.begin(); pair != m_entries.end();) + { + assert(pair->second.roundNumber < m_currentRound); + + if (pair->second.roundNumber < m_currentRound - 1) + m_entries.erase(pair++); + else + ++pair; + } +} + +void ProgramCache::clear() +{ + m_entries.clear(); + m_currentRound = 0; +} + +Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) const +{ + auto const& pair = m_entries.find(_abbreviatedOptimisationSteps); + if (pair == m_entries.end()) + return nullptr; + + return &(pair->second.program); +} + +CacheStats ProgramCache::gatherStats() const +{ + return { + /* hits = */ m_hits, + /* misses = */ m_misses, + /* totalCodeSize = */ calculateTotalCachedCodeSize(), + /* roundEntryCounts = */ countRoundEntries(), + }; +} + +size_t ProgramCache::calculateTotalCachedCodeSize() const +{ + size_t size = 0; + for (auto const& pair: m_entries) + size += pair.second.program.codeSize(); + + return size; +} + +map ProgramCache::countRoundEntries() const +{ + map counts; + for (auto& pair: m_entries) + if (counts.find(pair.second.roundNumber) != counts.end()) + ++counts.at(pair.second.roundNumber); + else + counts.insert({pair.second.roundNumber, 1}); + + return counts; +} diff --git a/tools/yulPhaser/ProgramCache.h b/tools/yulPhaser/ProgramCache.h new file mode 100644 index 000000000000..8cc83009866e --- /dev/null +++ b/tools/yulPhaser/ProgramCache.h @@ -0,0 +1,117 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +/** + * Structure used by @a ProgramCache to store intermediate programs and metadata associated + * with them. + */ +struct CacheEntry +{ + Program program; + size_t roundNumber; + + CacheEntry(Program _program, size_t _roundNumber): + program(std::move(_program)), + roundNumber(_roundNumber) {} +}; + +/** + * Stores statistics about current cache usage. + */ +struct CacheStats +{ + size_t hits; + size_t misses; + size_t totalCodeSize; + std::map roundEntryCounts; + + CacheStats& operator+=(CacheStats const& _other); + CacheStats operator+(CacheStats const& _other) const { return CacheStats(*this) += _other; } + + bool operator==(CacheStats const& _other) const; + bool operator!=(CacheStats const& _other) const { return !(*this == _other); } +}; + +/** + * Class that optimises programs one step at a time which allows it to store and later reuse the + * results of the intermediate steps. + * + * The cache keeps track of the current round number and associates newly created entries with it. + * @a startRound() must be called at the beginning of a round so that entries that are too old + * can be purged. The current strategy is to store programs corresponding to all possible prefixes + * encountered in the current and the previous rounds. Entries older than that get removed to + * conserve memory. + * + * @a gatherStats() allows getting statistics useful for determining cache effectiveness. + * + * The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited + * experiments) but there's room for improvement. We could fit more useful programs in + * the cache by being more picky about which ones we choose. + * + * There is currently no way to purge entries without starting a new round. Since the programs + * take a lot of memory, this may lead to the cache eating up all the available RAM if sequences are + * long and programs large. A limiter based on entry count or total program size would be useful. + */ +class ProgramCache +{ +public: + explicit ProgramCache(Program _program): + m_program(std::move(_program)) {} + + Program optimiseProgram( + std::string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount = 1 + ); + void startRound(size_t _nextRoundNumber); + void clear(); + + size_t size() const { return m_entries.size(); } + Program const* find(std::string const& _abbreviatedOptimisationSteps) const; + bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; } + + CacheStats gatherStats() const; + + std::map const& entries() const { return m_entries; }; + Program const& program() const { return m_program; } + size_t currentRound() const { return m_currentRound; } + +private: + size_t calculateTotalCachedCodeSize() const; + std::map countRoundEntries() const; + + // The best matching data structure here would be a trie of chromosome prefixes but since + // the programs are orders of magnitude larger than the prefixes, it does not really matter. + // A map should be good enough. + std::map m_entries; + + Program m_program; + size_t m_currentRound = 0; + size_t m_hits = 0; + size_t m_misses = 0; +}; + +} diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 1103db5a8bc5..2b596f4ed732 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -52,5 +52,5 @@ uint32_t SimulationRNG::generateSeed() // This is not a secure way to seed the generator but it's good enough for simulation purposes. // The only thing that matters for us is that the sequence is different on each run and that // it fits the expected distribution. It does not have to be 100% unpredictable. - return time(0); + return time(nullptr); } diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 321ebe0881e9..d0c00fcdace1 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -16,157 +16,74 @@ */ #include -#include -#include -#include -#include -#include +#include -#include -#include -#include - -#include -#include +#include #include -#include - -using namespace std; -using namespace solidity::langutil; -using namespace solidity::phaser; -using namespace solidity::util; - -namespace po = boost::program_options; -namespace -{ - -struct CommandLineParsingResult -{ - int exitCode; - po::variables_map arguments; -}; - - -void initializeRNG(po::variables_map const& arguments) -{ - uint32_t seed; - if (arguments.count("seed") > 0) - seed = arguments["seed"].as(); - else - seed = SimulationRNG::generateSeed(); - - SimulationRNG::reset(seed); - cout << "Random seed: " << seed << endl; -} - -CharStream loadSource(string const& _sourcePath) -{ - assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); - - string sourceCode = readFileAsString(_sourcePath); - return CharStream(sourceCode, _sourcePath); -} - -void runAlgorithm(string const& _sourcePath) -{ - constexpr size_t populationSize = 20; - constexpr size_t minChromosomeLength = 12; - constexpr size_t maxChromosomeLength = 30; - - CharStream sourceCode = loadSource(_sourcePath); - shared_ptr fitnessMetric = make_shared(Program::load(sourceCode), 5); - auto population = Population::makeRandom( - fitnessMetric, - populationSize, - minChromosomeLength, - maxChromosomeLength - ); - RandomAlgorithm( - population, - cout, - { - /* elitePoolSize = */ 1.0 / populationSize, - /* minChromosomeLength = */ minChromosomeLength, - /* maxChromosomeLength = */ maxChromosomeLength, - } - ).run(); -} - -CommandLineParsingResult parseCommandLine(int argc, char** argv) +int main(int argc, char** argv) { - po::options_description description( - "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" - "\n" - "Usage: yul-phaser [options] \n" - "Reads as Yul code and tries to find the best order in which to run optimisation" - " phases using a genetic algorithm.\n" - "Example:\n" - "yul-phaser program.yul\n" - "\n" - "Allowed options", - po::options_description::m_default_line_length, - po::options_description::m_default_line_length - 23 - ); - - description.add_options() - ("help", "Show help message and exit.") - ("input-file", po::value()->required(), "Input file") - ("seed", po::value(), "Seed for the random number generator") - ; - - po::positional_options_description positionalDescription; - po::variables_map arguments; - positionalDescription.add("input-file", 1); - po::notify(arguments); - try { - po::command_line_parser parser(argc, argv); - parser.options(description).positional(positionalDescription); - po::store(parser.run(), arguments); + solidity::phaser::Phaser::main(argc, argv); + return 0; } - catch (po::error const & _exception) + catch (boost::program_options::error const& exception) { - cerr << _exception.what() << endl; - return {1, move(arguments)}; - } + // Bad input data. Invalid command-line parameters. - if (arguments.count("help") > 0) - { - cout << description << endl; - return {2, move(arguments)}; + std::cerr << std::endl; + std::cerr << "ERROR: " << exception.what() << std::endl; + return 1; } + catch (solidity::phaser::BadInput const& exception) + { + // Bad input data. Syntax errors in the input program, semantic errors in command-line + // parameters, etc. - if (arguments.count("input-file") == 0) + std::cerr << std::endl; + std::cerr << "ERROR: " << exception.what() << std::endl; + return 1; + } + catch (solidity::util::Exception const& exception) { - cerr << "Missing argument: input-file." << endl; - return {1, move(arguments)}; + // Something's seriously wrong. Probably a bug in the program or a missing handler (which + // is really also a bug). The exception should have been handled gracefully by this point + // if it's something that can happen in normal usage. E.g. an error in the input or a + // failure of some part of the system that's outside of control of the application (disk, + // network, etc.). The bug should be reported and investigated so our job here is just to + // provide as much useful information about it as possible. + + std::cerr << std::endl; + std::cerr << "UNCAUGHT EXCEPTION!" << std::endl; + + // We can print some useful diagnostic info for this particular exception type. + std::cerr << "Location: " << exception.lineInfo() << std::endl; + + char const* const* function = boost::get_error_info(exception); + if (function != nullptr) + std::cerr << "Function: " << *function << std::endl; + + // Let it crash. The terminate() will print some more stuff useful for debugging like + // what() and the actual exception type. + throw; } - - return {0, arguments}; -} - -} - -int main(int argc, char** argv) -{ - CommandLineParsingResult parsingResult = parseCommandLine(argc, argv); - if (parsingResult.exitCode != 0) - return parsingResult.exitCode; - - initializeRNG(parsingResult.arguments); - - try + catch (std::exception const&) { - runAlgorithm(parsingResult.arguments["input-file"].as()); + // Again, probably a bug but this time it's just plain std::exception so there's no point + // in doing anything special. terminate() will do an adequate job. + std::cerr << std::endl; + std::cerr << "UNCAUGHT EXCEPTION!" << std::endl; + throw; } - catch (InvalidProgram const& _exception) + catch (...) { - cerr << "ERROR: " << _exception.what() << endl; - return 1; + // Some people don't believe these exist. + // I have no idea what this is and it's flying towards me so technically speaking it's an + // unidentified flying object. + std::cerr << std::endl; + std::cerr << "UFO SPOTTED!" << std::endl; + throw; } - - return 0; }