diff options
author | chriseth <chris@ethereum.org> | 2018-09-14 00:38:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-14 00:38:41 +0800 |
commit | 59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (patch) | |
tree | 76b1734d7360d73825f9737a1c7b530468f4bddd | |
parent | e67f0147998a9e3835ed3ce8bf6a0a0c634216c5 (diff) | |
parent | 76812ed5675eb9d9bcd8f939adfec3ba4f02e970 (diff) | |
download | dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar.gz dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar.bz2 dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar.lz dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar.xz dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.tar.zst dexon-solidity-59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb.zip |
Merge pull request #4957 from ethereum/develop_v0425
Merge to release for 0.4.25
32 files changed, 1000 insertions, 66 deletions
@@ -35,6 +35,7 @@ build/ docs/_build docs/utils/__pycache__ docs/utils/*.pyc +/deps/downloads/ # vim stuff *.swp @@ -43,3 +44,5 @@ docs/utils/*.pyc .idea browse.VC.db CMakeLists.txt.user +/CMakeSettings.json +/.vs diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c05208f..f30872af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.24") +set(PROJECT_VERSION "0.4.25") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index 6a408ae8..bfe82f8c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.4.25 (2018-09-12) + +Important Bugfixes: + * Code Generator: Properly perform cleanup for exponentiation and non-256 bit types. + * Type Checker: Report error when using indexed structs in events with experimental ABIEncoderV2. This used to log wrong values. + * Type Checker: Report error when using structs in events without experimental ABIEncoderV2. This used to crash or log the wrong values. + * Parser: Consider all unicode line terminators (LF, VF, FF, CR, NEL, LS, PS) for single-line comments + and string literals. They are invalid in strings and will end comments. + * Parser: Disallow unterminated multi-line comments at the end of input. + * Parser: Treat ``/** /`` as unterminated multi-line comment. + ### 0.4.24 (2018-05-16) Language Features: @@ -155,6 +155,23 @@ jobs: - store_artifacts: *solc_artifact - persist_to_workspace: *all_artifacts + test_buglist: + docker: + - image: circleci/node + environment: + TERM: xterm + steps: + - checkout + - run: + name: JS deps + command: | + npm install download + npm install JSONPath + npm install mktemp + - run: + name: Test buglist + command: ./test/buglistTests.js + test_x86_linux: docker: - image: buildpack-deps:artful @@ -217,6 +234,7 @@ workflows: version: 2 build_all: jobs: + - test_buglist: *build_on_tags - build_emscripten: *build_on_tags - test_emscripten_solcjs: <<: *build_on_tags diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 683d1d2e..3ae5bf2a 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -132,17 +132,6 @@ elseif (DEFINED MSVC) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions - # Always use Release variant of C++ runtime. - # We don't want to provide Debug variants of all dependencies. Some default - # flags set by CMake must be tweaked. - string(REPLACE "/MDd" "/MD" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/D_DEBUG" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/MDd" "/MD" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") - string(REPLACE "/D_DEBUG" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") - string(REPLACE "/RTC1" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") - set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS OFF) - # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index 3d6b37ed..ea3218ef 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -6,13 +6,20 @@ else() set(JSONCPP_CMAKE_COMMAND ${CMAKE_COMMAND}) endif() -include(GNUInstallDirs) set(prefix "${CMAKE_BINARY_DIR}/deps") -set(JSONCPP_LIBRARY "${prefix}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}") +set(JSONCPP_LIBRARY "${prefix}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}") set(JSONCPP_INCLUDE_DIR "${prefix}/include") -if(NOT MSVC) - set(JSONCPP_EXTRA_FLAGS "-std=c++11") +# TODO: Investigate why this breaks some emscripten builds and +# check whether this can be removed after updating the emscripten +# versions used in the CI runs. +if(EMSCRIPTEN) + # Do not include all flags in CMAKE_CXX_FLAGS for emscripten, + # but only use -std=c++11. Using all flags causes build failures + # at the moment. + set(JSONCPP_CXX_FLAGS -std=c++11) +else() + set(JSONCPP_CXX_FLAGS ${CMAKE_CXX_FLAGS}) endif() set(byproducts "") @@ -30,14 +37,13 @@ ExternalProject_Add(jsoncpp-project CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_INSTALL_LIBDIR=lib # Build static lib but suitable to be included in a shared lib. -DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS} -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF - -DCMAKE_CXX_FLAGS=${JSONCPP_EXTRA_FLAGS} - # Overwrite build and install commands to force Release build on MSVC. - BUILD_COMMAND cmake --build <BINARY_DIR> --config Release - INSTALL_COMMAND cmake --build <BINARY_DIR> --config Release --target install + -DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ${byproducts} ) diff --git a/docs/bugs.json b/docs/bugs.json index b464be18..c1e377a4 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,5 +1,30 @@ [ { + "name": "ExpExponentCleanup", + "summary": "Using the ** operator with an exponent of type shorter than 256 bits can result in unexpected values.", + "description": "Higher order bits in the exponent are not properly cleaned before the EXP opcode is applied if the type of the exponent expression is smaller than 256 bits and not smaller than the type of the base. In that case, the result might be larger than expected if the exponent is assumed to lie within the value range of the type. Literal numbers as exponents are unaffected as are exponents or bases of type uint256.", + "fixed": "0.4.25", + "severity": "medium/high", + "check": {"regex-source": "[^/]\\*\\* *[^/0-9 ]"} + }, + { + "name": "EventStructWrongData", + "summary": "Using structs in events logged wrong data.", + "description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.", + "introduced": "0.4.17", + "fixed": "0.4.25", + "severity": "very low", + "check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"} + }, + { + "name": "PublicLibFunctionsDoNotReturnNestedArrays", + "summary": "Calls to public library functions (internal functions are safe) that return nested arrays return only zeroes.", + "description": "The compiler does not complain about public library functions (internal functions are safe) returning nested arrays, but it also does not return it correctly. Thus, the function caller receives only zeroes.", + "introduced": "0.4.11", + "fixed": "0.4.22", + "severity": "low" + }, + { "name": "OneOfTwoConstructorsSkipped", "summary": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored.", "description": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored. There will be a compiler warning about the old-style constructor, so contracts only using new-style constructors are fine.", @@ -7,6 +32,15 @@ "fixed": "0.4.23", "severity": "very low" }, + { + "name": "NestedArrayFunctionCallDecoder", + "summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.", + "description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.", + "introduced": "0.1.4", + "fixed": "0.4.22", + "severity": "medium", + "check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"} + }, { "name": "ZeroFunctionSelector", "summary": "It is possible to craft the name of a function such that it is executed instead of the fallback function in very specific circumstances.", diff --git a/docs/bugs.rst b/docs/bugs.rst index 7629830d..8e3382c8 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -56,6 +56,20 @@ conditions is an object that can contain a boolean value ``optimizer``, which means that the optimizer has to be switched on to enable the bug. If no conditions are given, assume that the bug is present. +check + This field contains different checks that can be used to determine + whether a smart contract + contains the bug or not. The first type of check are Javascript regular + expressions that are to be matched against the source code ("source-regex"). + If there is no match, then the bug is very likely + not present. If there is a match, the bug might be present. For improved + accuracy, the checks should be applied to the source code after stripping + comments. + The second type of check are patterns to be applied to the compact AST of + the Solidity program ("ast-compact-json-path"). The specified search query + is a `JsonPath <https://github.com/json-path/JsonPath>`_ expression. + If at least one path of the Solidity AST matches the query, the bug is + likely present. .. literalinclude:: bugs.json :language: js diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 2fe1d226..ef084660 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,7 @@ { "0.1.0": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -18,6 +19,7 @@ }, "0.1.1": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -35,6 +37,7 @@ }, "0.1.2": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -52,6 +55,7 @@ }, "0.1.3": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -69,6 +73,8 @@ }, "0.1.4": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -86,6 +92,8 @@ }, "0.1.5": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -103,6 +111,8 @@ }, "0.1.6": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -121,6 +131,8 @@ }, "0.1.7": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -139,6 +151,8 @@ }, "0.2.0": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -157,6 +171,8 @@ }, "0.2.1": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -175,6 +191,8 @@ }, "0.2.2": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -193,6 +211,8 @@ }, "0.3.0": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -211,6 +231,8 @@ }, "0.3.1": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -228,6 +250,8 @@ }, "0.3.2": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -245,6 +269,8 @@ }, "0.3.3": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -261,6 +287,8 @@ }, "0.3.4": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -277,6 +305,8 @@ }, "0.3.5": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -293,6 +323,8 @@ }, "0.3.6": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -307,6 +339,8 @@ }, "0.4.0": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -321,6 +355,8 @@ }, "0.4.1": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -335,6 +371,8 @@ }, "0.4.10": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -345,6 +383,9 @@ }, "0.4.11": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -354,6 +395,9 @@ }, "0.4.12": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" @@ -362,6 +406,9 @@ }, "0.4.13": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" @@ -370,6 +417,9 @@ }, "0.4.14": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue" ], @@ -377,32 +427,54 @@ }, "0.4.15": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-08-08" }, "0.4.16": { "bugs": [ + "ExpExponentCleanup", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-08-24" }, "0.4.17": { "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-09-21" }, "0.4.18": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder" + ], "released": "2017-10-18" }, "0.4.19": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder" + ], "released": "2017-11-30" }, "0.4.2": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -415,29 +487,53 @@ "released": "2016-09-17" }, "0.4.20": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder" + ], "released": "2018-02-14" }, "0.4.21": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", + "PublicLibFunctionsDoNotReturnNestedArrays", + "NestedArrayFunctionCallDecoder" + ], "released": "2018-03-07" }, "0.4.22": { "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData", "OneOfTwoConstructorsSkipped" ], "released": "2018-04-16" }, "0.4.23": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData" + ], "released": "2018-04-19" }, "0.4.24": { - "bugs": [], + "bugs": [ + "ExpExponentCleanup", + "EventStructWrongData" + ], "released": "2018-05-16" }, + "0.4.25": { + "bugs": [], + "released": "2018-09-12" + }, "0.4.3": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -450,6 +546,8 @@ }, "0.4.4": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -461,6 +559,8 @@ }, "0.4.5": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -473,6 +573,8 @@ }, "0.4.6": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -484,6 +586,8 @@ }, "0.4.7": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -494,6 +598,8 @@ }, "0.4.8": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -504,6 +610,8 @@ }, "0.4.9": { "bugs": [ + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 30302908..2062458e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -864,15 +864,35 @@ void TypeChecker::visitManually( bool TypeChecker::visit(EventDefinition const& _eventDef) { + solAssert(_eventDef.visibility() > Declaration::Visibility::Internal, ""); unsigned numIndexed = 0; for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters()) { if (var->isIndexed()) + { numIndexed++; + if ( + _eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + dynamic_cast<ReferenceType const*>(type(*var).get()) + ) + m_errorReporter.typeError( + var->location(), + "Indexed reference types cannot yet be used with ABIEncoderV2." + ); + } if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type."); + if ( + !_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + !typeSupportedByOldABIEncoder(*type(*var)) + ) + m_errorReporter.typeError( + var->location(), + "This type is only supported in the new experimental ABI encoder. " + "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ); } if (_eventDef.isAnonymous() && numIndexed > 4) m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4bcc1fa9..f38c1e67 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -2069,7 +2069,9 @@ bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _ { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; - else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod)) + else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp)) + // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 + // It would suffice to clean the exponent, though. return true; else return false; diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index 6541f6c2..dbe1f389 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -243,22 +243,17 @@ bool Scanner::skipWhitespace() return sourcePos() != startPosition; } -bool Scanner::skipWhitespaceExceptLF() +void Scanner::skipWhitespaceExceptUnicodeLinebreak() { - int const startPosition = sourcePos(); - while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) + while (isWhiteSpace(m_char) && !isUnicodeLinebreak()) advance(); - // Return whether or not we skipped any characters. - return sourcePos() != startPosition; } Token::Value Scanner::skipSingleLineComment() { - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar - while (!isLineTerminator(m_char)) + // Line terminator is not part of the comment. If it is a + // non-ascii line terminator, it will result in a parser error. + while (!isUnicodeLinebreak()) if (!advance()) break; return Token::Whitespace; @@ -268,7 +263,9 @@ Token::Value Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' at /// - skipWhitespaceExceptLF(); + + skipWhitespaceExceptUnicodeLinebreak(); + while (!isSourcePastEndOfInput()) { if (isLineTerminator(m_char)) @@ -287,6 +284,10 @@ Token::Value Scanner::scanSingleLineDocComment() break; // next line is not a documentation comment, we are done } + else if (isUnicodeLinebreak()) + // Any line terminator that is not '\n' is considered to end the + // comment. + break; addCommentLiteralChar(m_char); advance(); } @@ -321,6 +322,9 @@ Token::Value Scanner::scanMultiLineDocComment() bool endFound = false; bool charsAdded = false; + while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) + advance(); + while (!isSourcePastEndOfInput()) { //handle newlines in multline comments @@ -372,7 +376,7 @@ Token::Value Scanner::scanSlash() if (m_char == '/') { if (!advance()) /* double slash comment directly before EOS */ - return Token::Whitespace; + return Token::Whitespace; else if (m_char == '/') { // doxygen style /// comment @@ -390,24 +394,27 @@ Token::Value Scanner::scanSlash() { // doxygen style /** natspec comment if (!advance()) /* slash star comment before EOS */ - return Token::Whitespace; + return Token::Illegal; else if (m_char == '*') { advance(); //consume the last '*' at /** - skipWhitespaceExceptLF(); - // special case of a closed normal multiline comment - if (!m_source.isPastEndOfInput() && m_source.get(0) == '/') - advance(); //skip the closing slash - else // we actually have a multiline documentation comment + // "/**/" + if (m_char == '/') { - Token::Value comment; - m_nextSkippedComment.location.start = firstSlashPosition; - comment = scanMultiLineDocComment(); - m_nextSkippedComment.location.end = sourcePos(); - m_nextSkippedComment.token = comment; + advance(); //skip the closing slash + return Token::Whitespace; } - return Token::Whitespace; + // we actually have a multiline documentation comment + Token::Value comment; + m_nextSkippedComment.location.start = firstSlashPosition; + comment = scanMultiLineDocComment(); + m_nextSkippedComment.location.end = sourcePos(); + m_nextSkippedComment.token = comment; + if (comment == Token::Illegal) + return Token::Illegal; + else + return Token::Whitespace; } else return skipMultiLineComment(); @@ -435,11 +442,6 @@ void Scanner::scanToken() m_nextToken.location.start = sourcePos(); switch (m_char) { - case '\n': - case ' ': - case '\t': - token = selectToken(Token::Whitespace); - break; case '"': case '\'': token = scanString(); @@ -675,18 +677,38 @@ bool Scanner::scanEscape() if (!scanHexByte(c)) return false; break; + default: + return false; } addLiteralChar(c); return true; } +bool Scanner::isUnicodeLinebreak() +{ + if (0x0a <= m_char && m_char <= 0x0d) + // line feed, vertical tab, form feed, carriage return + return true; + else if (!m_source.isPastEndOfInput(1) && uint8_t(m_source.get(0)) == 0xc2 && uint8_t(m_source.get(1)) == 0x85) + // NEL - U+0085, C2 85 in utf8 + return true; + else if (!m_source.isPastEndOfInput(2) && uint8_t(m_source.get(0)) == 0xe2 && uint8_t(m_source.get(1)) == 0x80 && ( + uint8_t(m_source.get(2)) == 0xa8 || uint8_t(m_source.get(2)) == 0xa9 + )) + // LS - U+2028, E2 80 A8 in utf8 + // PS - U+2029, E2 80 A9 in utf8 + return true; + else + return false; +} + Token::Value Scanner::scanString() { char const quote = m_char; advance(); // consume quote LiteralScope literal(this, LITERAL_TYPE_STRING); - while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) + while (m_char != quote && !isSourcePastEndOfInput() && !isUnicodeLinebreak()) { char c = m_char; advance(); @@ -710,7 +732,7 @@ Token::Value Scanner::scanHexString() char const quote = m_char; advance(); // consume quote LiteralScope literal(this, LITERAL_TYPE_STRING); - while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) + while (m_char != quote && !isSourcePastEndOfInput()) { char c = m_char; if (!scanHexByte(c)) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 0adaa6fd..602532e4 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -197,8 +197,8 @@ private: /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); - /// Skips all whitespace except Line feeds and returns true if something was skipped - bool skipWhitespaceExceptLF(); + /// Skips all whitespace that are neither '\r' nor '\n'. + void skipWhitespaceExceptUnicodeLinebreak(); Token::Value skipSingleLineComment(); Token::Value skipMultiLineComment(); @@ -218,6 +218,9 @@ private: /// is scanned. bool scanEscape(); + /// @returns true iff we are currently positioned at a unicode line break. + bool isUnicodeLinebreak(); + /// Return the current source position. int sourcePos() const { return m_source.position(); } bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } diff --git a/scripts/release.bat b/scripts/release.bat index be95b35e..b15b49b7 100644 --- a/scripts/release.bat +++ b/scripts/release.bat @@ -32,12 +32,9 @@ set VERSION=%2 IF "%VERSION%"=="2015" ( set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" ) ELSE ( - - IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\" ( - set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll" - ) ELSE ( - set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll" - ) + set "DLLS=MSVC_DLLS_NOT_FOUND" + FOR /d %%d IN ("C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\*" + "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\*") DO set "DLLS=%%d\x86\Microsoft.VC141.CRT\msvc*.dll" ) 7z a solidity-windows.zip ^ diff --git a/test/buglistTests.js b/test/buglistTests.js new file mode 100755 index 00000000..f24f0cb6 --- /dev/null +++ b/test/buglistTests.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node + +"use strict"; + +var util = require('util') +var exec = util.promisify(require('child_process').exec) +var mktemp = require('mktemp'); +var download = require('download') +var JSONPath = require('JSONPath') +var fs = require('fs') +var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8')) + +var bugsByName = {} +for (var i in bugs) +{ + if (bugs[i].name in bugsByName) + { + throw "Duplicate bug name: " + bugs[i].name + } + bugsByName[bugs[i].name] = bugs[i] +} + +var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8') + +var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g + +runTests() + +async function runTests() +{ + var result; + while ((result = testVectorParser.exec(tests)) !== null) + { + var name = result[1] + var buggy = result[2].split('\n--\n') + var fine = result[3].split('\n--\n') + console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances") + + try { + await checkRegex(name, buggy, fine) + await checkJSONPath(name, buggy, fine) + } catch (err) { + console.error("Error: " + err) + } + } +} + +function checkRegex(name, buggy, fine) +{ + return new Promise(function(resolve, reject) { + var regexStr = bugsByName[name].check['regex-source'] + if (regexStr !== undefined) + { + var regex = RegExp(regexStr) + for (var i in buggy) + { + if (!regex.exec(buggy[i])) + { + reject("Bug " + name + ": Buggy source does not match: " + buggy[i]) + } + } + for (var i in fine) + { + if (regex.exec(fine[i])) + { + reject("Bug " + name + ": Non-buggy source matches: " + fine[i]) + } + } + } + resolve() + }) +} + +async function checkJSONPath(name, buggy, fine) +{ + var jsonPath = bugsByName[name].check['ast-compact-json-path'] + if (jsonPath !== undefined) + { + var url = "http://github.com/ethereum/solidity/releases/download/v" + bugsByName[name].introduced + "/solc-static-linux" + try { + var tmpdir = await mktemp.createDir('XXXXX') + var binary = tmpdir + "/solc-static-linux" + await download(url, tmpdir) + exec("chmod +x " + binary) + for (var i in buggy) + { + var result = await checkJsonPathTest(buggy[i], tmpdir, binary, jsonPath, i) + if (!result) + throw "Bug " + name + ": Buggy source does not contain path: " + buggy[i] + } + for (var i in fine) + { + var result = await checkJsonPathTest(fine[i], tmpdir, binary, jsonPath, i + buggy.length) + if (result) + throw "Bug " + name + ": Non-buggy source contains path: " + fine[i] + } + exec("rm -r " + tmpdir) + } catch (err) { + throw err + } + } +} + +function checkJsonPathTest(code, tmpdir, binary, query, idx) { + return new Promise(function(resolve, reject) { + var solFile = tmpdir + "/jsonPath" + idx + ".sol" + var astFile = tmpdir + "/ast" + idx + ".json" + writeFilePromise(solFile, code) + .then(() => { + return exec(binary + " --ast-compact-json " + solFile + " > " + astFile) + }) + .then(() => { + var jsonRE = /(\{[\s\S]*\})/ + var ast = JSON.parse(jsonRE.exec(fs.readFileSync(astFile, 'utf8'))[0]) + var result = JSONPath({json: ast, path: query}) + if (result.length > 0) + resolve(true) + else + resolve(false) + }) + .catch((err) => { + reject(err) + }) + }) +} + +function writeFilePromise(filename, data) { + return new Promise(function(resolve, reject) { + fs.writeFile(filename, data, 'utf8', function(err) { + if (err) reject(err) + else resolve(data) + }) + }) +} diff --git a/test/buglist_test_vectors.md b/test/buglist_test_vectors.md new file mode 100644 index 00000000..e683f481 --- /dev/null +++ b/test/buglist_test_vectors.md @@ -0,0 +1,148 @@ +# NestedArrayFunctionCallDecoder + +## buggy + +function f() pure returns (uint[2][2]) { } + +-- + +function f() returns (uint[2][2] a) { } + +-- + +function f() returns (uint x, uint[200][2] a) { } + +-- + +function f() returns (uint[200][2] a, uint x) { } + +-- + +function f() returns (uint[200][2] a, uint x); + +-- + +function f() returns ( + uint + [ + 200 + ] + [2] + a, uint x); + +-- + +function f() returns ( + uint + [ + ContractName.ConstantName + ] + [2] + a, uint x); + +## fine + +function f() returns (uint[2]) { } + +-- + +function f() public pure returns (uint[2][] a) { } + +-- + +function f() public pure returns (uint[ 2 ] [ ] a) { } + +-- + +function f() public pure returns (uint x, uint[] a) { } + +-- + +function f(uint[2][2]) { } + +-- + +function f() m(uint[2][2]) { } + +-- + +function f() returns (uint, uint) { uint[2][2] memory x; } + +# ExpExponentCleanup + +## buggy + +x ** y + +-- + +x ** uint8(y) + +-- + +x**y + +## fine + +x ** 2 + +-- + +x**2 + +-- + +x**200 + +-- + +/** bla **/ + +-- + +/**/ + +# EventStructWrongData + +## buggy + +pragma experimental ABIEncoderV2; +contract C +{ + struct S { uint x; } + event E(S); + event F(S); + enum A { B, C } + event G(A); + function f(S s); +} + +-- + +pragma experimental ABIEncoderV2; +contract C +{ + struct S { uint x; } + event E(S indexed); + event F(uint, S, bool); +} + +## fine + +pragma experimental ABIEncoderV2; +contract C +{ + struct S { uint x; } + enum A { B, C } + event G(A); +} + +-- + +pragma experimental ABIEncoderV2; +contract C +{ + struct S { uint x; } + function f(S s); + S s1; +} diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 107abc26..3475b018 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -1036,6 +1036,7 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts) BOOST_AUTO_TEST_CASE(event_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 1efcfde0..f2cc78bf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3516,6 +3516,209 @@ BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } +BOOST_AUTO_TEST_CASE(event_struct_memory_v2) +{ + 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)); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(x)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E((uint256))"))); +} + +BOOST_AUTO_TEST_CASE(event_struct_storage_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + struct S { uint a; } + event E(S); + S s; + function createEvent(uint x) public { + s.a = x; + emit E(s); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(x)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E((uint256))"))); +} + +BOOST_AUTO_TEST_CASE(event_dynamic_array_memory) +{ + 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); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])"))); +} + +BOOST_AUTO_TEST_CASE(event_dynamic_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[](3); + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::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(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])"))); +} + +BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) +{ + char const* sourceCode = R"( + contract C { + event E(uint[]); + uint[] arr; + function createEvent(uint x) public { + arr.length = 3; + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])"))); +} + +BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + event E(uint[]); + uint[] arr; + function createEvent(uint x) public { + arr.length = 3; + arr[0] = x; + arr[1] = x + 1; + arr[2] = x + 2; + emit E(arr); + } + } + )"; + compileAndRun(sourceCode); + u256 x(42); + callContractFunction("createEvent(uint256)", x); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::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.length = 2; + arr[0].length = 2; + arr[1].length = 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(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])"))); +} + BOOST_AUTO_TEST_CASE(event_indexed_string) { char const* sourceCode = R"( @@ -10540,6 +10743,46 @@ BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) 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"( diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 020bce7f..4ccc6788 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -23,6 +23,8 @@ #include <libsolidity/parsing/Scanner.h> #include <boost/test/unit_test.hpp> +using namespace std; + namespace dev { namespace solidity @@ -393,6 +395,110 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) +{ + // This used to parse as "comment", "identifier" + Scanner scanner(CharStream("/** / x")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(multiline_doc_comment_at_eos) +{ + // This used to parse as "whitespace" + Scanner scanner(CharStream("/**")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(multiline_comment_at_eos) +{ + Scanner scanner(CharStream("/*")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) +{ + for (auto const& nl: {"\r", "\n"}) + { + Scanner scanner(CharStream("// abc " + string(nl) + " def ")); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + +BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_comment) +{ + for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) + { + Scanner scanner(CharStream("// abc " + string(nl) + " def ")); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + for (size_t i = 0; i < string(nl).size() - 1; i++) + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + +BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) +{ + for (auto const& nl: {"\r", "\n"}) + { + Scanner scanner(CharStream("/// abc " + string(nl) + " def ")); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + +BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment) +{ + for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) + { + Scanner scanner(CharStream("/// abc " + string(nl) + " def ")); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + for (size_t i = 0; i < string(nl).size() - 1; i++) + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + +BOOST_AUTO_TEST_CASE(regular_line_breaks_in_strings) +{ + for (auto const& nl: {"\n", "\r"}) + { + Scanner scanner(CharStream("\"abc " + string(nl) + " def\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + +BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) +{ + for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) + { + Scanner scanner(CharStream("\"abc " + string(nl) + " def\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + for (size_t i = 0; i < string(nl).size(); i++) + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/syntaxTests/emit/emit_empty.sol b/test/libsolidity/syntaxTests/emit/emit_empty.sol new file mode 100644 index 00000000..819d88fe --- /dev/null +++ b/test/libsolidity/syntaxTests/emit/emit_empty.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + emit; + } +} +// ---- +// ParserError: (45-46): Expected event name or path. diff --git a/test/libsolidity/syntaxTests/emit_non_event.sol b/test/libsolidity/syntaxTests/emit/emit_non_event.sol index 1df6990d..1df6990d 100644 --- a/test/libsolidity/syntaxTests/emit_non_event.sol +++ b/test/libsolidity/syntaxTests/emit/emit_non_event.sol diff --git a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol new file mode 100644 index 00000000..aaf6028a --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol @@ -0,0 +1,7 @@ +pragma experimental ABIEncoderV2; +contract c { + event E(uint[] indexed); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (59-65): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/events/event_array_v2.sol b/test/libsolidity/syntaxTests/events/event_array_v2.sol new file mode 100644 index 00000000..9ccd9fc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_array_v2.sol @@ -0,0 +1,6 @@ +pragma experimental ABIEncoderV2; +contract c { + event E(uint[]); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array.sol b/test/libsolidity/syntaxTests/events/event_nested_array.sol new file mode 100644 index 00000000..70af63b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_nested_array.sol @@ -0,0 +1,5 @@ +contract c { + event E(uint[][]); +} +// ---- +// TypeError: (25-33): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_2.sol new file mode 100644 index 00000000..5825650e --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_nested_array_2.sol @@ -0,0 +1,4 @@ +contract c { + event E(uint[2][]); +} +// ---- diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol new file mode 100644 index 00000000..fd59e962 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol @@ -0,0 +1,6 @@ +contract c { + struct S { uint x; uint[][] arr; } + event E(S); +} +// ---- +// TypeError: (61-62): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol new file mode 100644 index 00000000..ffae5b9c --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol @@ -0,0 +1,7 @@ +pragma experimental ABIEncoderV2; +contract c { + event E(uint[][] indexed); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (59-67): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol new file mode 100644 index 00000000..efc7439e --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol @@ -0,0 +1,6 @@ +pragma experimental ABIEncoderV2; +contract c { + event E(uint[][]); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/events/event_struct.sol b/test/libsolidity/syntaxTests/events/event_struct.sol new file mode 100644 index 00000000..c955dc5e --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_struct.sol @@ -0,0 +1,6 @@ +contract c { + struct S { uint a ; } + event E(S); +} +// ---- +// TypeError: (51-52): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol new file mode 100644 index 00000000..69ee5017 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol @@ -0,0 +1,6 @@ +contract c { + struct S { uint a ; } + event E(S indexed); +} +// ---- +// TypeError: (51-52): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol new file mode 100644 index 00000000..a8e0837f --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol @@ -0,0 +1,8 @@ +pragma experimental ABIEncoderV2; +contract c { + struct S { uint a ; } + event E(S indexed); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (85-86): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/events/event_struct_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_v2.sol new file mode 100644 index 00000000..97ca61b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_struct_v2.sol @@ -0,0 +1,7 @@ +pragma experimental ABIEncoderV2; +contract c { + struct S { uint a ; } + event E(S); +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. |