Merge pull request #4957 from ethereum/develop_v0425

Merge to release for 0.4.25
This commit is contained in:
chriseth 2018-09-13 18:38:41 +02:00 committed by GitHub
commit 59dbf8f108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1000 additions and 66 deletions

3
.gitignore vendored
View File

@ -35,6 +35,7 @@ build/
docs/_build docs/_build
docs/utils/__pycache__ docs/utils/__pycache__
docs/utils/*.pyc docs/utils/*.pyc
/deps/downloads/
# vim stuff # vim stuff
*.swp *.swp
@ -43,3 +44,5 @@ docs/utils/*.pyc
.idea .idea
browse.VC.db browse.VC.db
CMakeLists.txt.user CMakeLists.txt.user
/CMakeSettings.json
/.vs

View File

@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # 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}) project(solidity VERSION ${PROJECT_VERSION})
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)

View File

@ -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) ### 0.4.24 (2018-05-16)
Language Features: Language Features:

View File

@ -155,6 +155,23 @@ jobs:
- store_artifacts: *solc_artifact - store_artifacts: *solc_artifact
- persist_to_workspace: *all_artifacts - 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: test_x86_linux:
docker: docker:
- image: buildpack-deps:artful - image: buildpack-deps:artful
@ -217,6 +234,7 @@ workflows:
version: 2 version: 2
build_all: build_all:
jobs: jobs:
- test_buglist: *build_on_tags
- build_emscripten: *build_on_tags - build_emscripten: *build_on_tags
- test_emscripten_solcjs: - test_emscripten_solcjs:
<<: *build_on_tags <<: *build_on_tags

View File

@ -132,17 +132,6 @@ elseif (DEFINED MSVC)
add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement 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 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 # disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification

View File

@ -6,13 +6,20 @@ else()
set(JSONCPP_CMAKE_COMMAND ${CMAKE_COMMAND}) set(JSONCPP_CMAKE_COMMAND ${CMAKE_COMMAND})
endif() endif()
include(GNUInstallDirs)
set(prefix "${CMAKE_BINARY_DIR}/deps") 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") set(JSONCPP_INCLUDE_DIR "${prefix}/include")
if(NOT MSVC) # TODO: Investigate why this breaks some emscripten builds and
set(JSONCPP_EXTRA_FLAGS "-std=c++11") # 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() endif()
set(byproducts "") set(byproducts "")
@ -30,14 +37,13 @@ ExternalProject_Add(jsoncpp-project
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_LIBDIR=lib
# Build static lib but suitable to be included in a shared lib. # Build static lib but suitable to be included in a shared lib.
-DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS} -DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS}
-DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_TESTS=OFF
-DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF
-DCMAKE_CXX_FLAGS=${JSONCPP_EXTRA_FLAGS} -DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS}
# Overwrite build and install commands to force Release build on MSVC. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
BUILD_COMMAND cmake --build <BINARY_DIR> --config Release
INSTALL_COMMAND cmake --build <BINARY_DIR> --config Release --target install
${byproducts} ${byproducts}
) )

View File

@ -1,4 +1,29 @@
[ [
{
"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", "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.", "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.",
@ -7,6 +32,15 @@
"fixed": "0.4.23", "fixed": "0.4.23",
"severity": "very low" "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", "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.", "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.",

View File

@ -56,6 +56,20 @@ conditions
is an object that can contain a boolean value ``optimizer``, which is an object that can contain a boolean value ``optimizer``, which
means that the optimizer has to be switched on to enable the bug. means that the optimizer has to be switched on to enable the bug.
If no conditions are given, assume that the bug is present. 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 .. literalinclude:: bugs.json
:language: js :language: js

View File

@ -1,6 +1,7 @@
{ {
"0.1.0": { "0.1.0": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -18,6 +19,7 @@
}, },
"0.1.1": { "0.1.1": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -35,6 +37,7 @@
}, },
"0.1.2": { "0.1.2": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -52,6 +55,7 @@
}, },
"0.1.3": { "0.1.3": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -69,6 +73,8 @@
}, },
"0.1.4": { "0.1.4": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -86,6 +92,8 @@
}, },
"0.1.5": { "0.1.5": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -103,6 +111,8 @@
}, },
"0.1.6": { "0.1.6": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -121,6 +131,8 @@
}, },
"0.1.7": { "0.1.7": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -139,6 +151,8 @@
}, },
"0.2.0": { "0.2.0": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -157,6 +171,8 @@
}, },
"0.2.1": { "0.2.1": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -175,6 +191,8 @@
}, },
"0.2.2": { "0.2.2": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
"SkipEmptyStringLiteral", "SkipEmptyStringLiteral",
@ -193,6 +211,8 @@
}, },
"0.3.0": { "0.3.0": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -211,6 +231,8 @@
}, },
"0.3.1": { "0.3.1": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -228,6 +250,8 @@
}, },
"0.3.2": { "0.3.2": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -245,6 +269,8 @@
}, },
"0.3.3": { "0.3.3": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -261,6 +287,8 @@
}, },
"0.3.4": { "0.3.4": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -277,6 +305,8 @@
}, },
"0.3.5": { "0.3.5": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -293,6 +323,8 @@
}, },
"0.3.6": { "0.3.6": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -307,6 +339,8 @@
}, },
"0.4.0": { "0.4.0": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -321,6 +355,8 @@
}, },
"0.4.1": { "0.4.1": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -335,6 +371,8 @@
}, },
"0.4.10": { "0.4.10": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -345,6 +383,9 @@
}, },
"0.4.11": { "0.4.11": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -354,6 +395,9 @@
}, },
"0.4.12": { "0.4.12": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput" "ECRecoverMalformedInput"
@ -362,6 +406,9 @@
}, },
"0.4.13": { "0.4.13": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput" "ECRecoverMalformedInput"
@ -370,6 +417,9 @@
}, },
"0.4.14": { "0.4.14": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue" "DelegateCallReturnValue"
], ],
@ -377,32 +427,54 @@
}, },
"0.4.15": { "0.4.15": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector" "ZeroFunctionSelector"
], ],
"released": "2017-08-08" "released": "2017-08-08"
}, },
"0.4.16": { "0.4.16": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector" "ZeroFunctionSelector"
], ],
"released": "2017-08-24" "released": "2017-08-24"
}, },
"0.4.17": { "0.4.17": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector" "ZeroFunctionSelector"
], ],
"released": "2017-09-21" "released": "2017-09-21"
}, },
"0.4.18": { "0.4.18": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder"
],
"released": "2017-10-18" "released": "2017-10-18"
}, },
"0.4.19": { "0.4.19": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder"
],
"released": "2017-11-30" "released": "2017-11-30"
}, },
"0.4.2": { "0.4.2": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -415,29 +487,53 @@
"released": "2016-09-17" "released": "2016-09-17"
}, },
"0.4.20": { "0.4.20": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder"
],
"released": "2018-02-14" "released": "2018-02-14"
}, },
"0.4.21": { "0.4.21": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"PublicLibFunctionsDoNotReturnNestedArrays",
"NestedArrayFunctionCallDecoder"
],
"released": "2018-03-07" "released": "2018-03-07"
}, },
"0.4.22": { "0.4.22": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"EventStructWrongData",
"OneOfTwoConstructorsSkipped" "OneOfTwoConstructorsSkipped"
], ],
"released": "2018-04-16" "released": "2018-04-16"
}, },
"0.4.23": { "0.4.23": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData"
],
"released": "2018-04-19" "released": "2018-04-19"
}, },
"0.4.24": { "0.4.24": {
"bugs": [], "bugs": [
"ExpExponentCleanup",
"EventStructWrongData"
],
"released": "2018-05-16" "released": "2018-05-16"
}, },
"0.4.25": {
"bugs": [],
"released": "2018-09-12"
},
"0.4.3": { "0.4.3": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -450,6 +546,8 @@
}, },
"0.4.4": { "0.4.4": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -461,6 +559,8 @@
}, },
"0.4.5": { "0.4.5": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -473,6 +573,8 @@
}, },
"0.4.6": { "0.4.6": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -484,6 +586,8 @@
}, },
"0.4.7": { "0.4.7": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -494,6 +598,8 @@
}, },
"0.4.8": { "0.4.8": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",
@ -504,6 +610,8 @@
}, },
"0.4.9": { "0.4.9": {
"bugs": [ "bugs": [
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
"DelegateCallReturnValue", "DelegateCallReturnValue",
"ECRecoverMalformedInput", "ECRecoverMalformedInput",

View File

@ -864,15 +864,35 @@ void TypeChecker::visitManually(
bool TypeChecker::visit(EventDefinition const& _eventDef) bool TypeChecker::visit(EventDefinition const& _eventDef)
{ {
solAssert(_eventDef.visibility() > Declaration::Visibility::Internal, "");
unsigned numIndexed = 0; unsigned numIndexed = 0;
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters()) for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
{ {
if (var->isIndexed()) if (var->isIndexed())
{
numIndexed++; 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()) if (!type(*var)->canLiveOutsideStorage())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (!type(*var)->interfaceType(false)) if (!type(*var)->interfaceType(false))
m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type."); 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) if (_eventDef.isAnonymous() && numIndexed > 4)
m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");

View File

@ -2069,7 +2069,9 @@ bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _
{ {
if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) if (Token::isCompareOp(_op) || Token::isShiftOp(_op))
return true; 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; return true;
else else
return false; return false;

View File

@ -243,22 +243,17 @@ bool Scanner::skipWhitespace()
return sourcePos() != startPosition; return sourcePos() != startPosition;
} }
bool Scanner::skipWhitespaceExceptLF() void Scanner::skipWhitespaceExceptUnicodeLinebreak()
{ {
int const startPosition = sourcePos(); while (isWhiteSpace(m_char) && !isUnicodeLinebreak())
while (isWhiteSpace(m_char) && !isLineTerminator(m_char))
advance(); advance();
// Return whether or not we skipped any characters.
return sourcePos() != startPosition;
} }
Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::skipSingleLineComment()
{ {
// The line terminator at the end of the line is not considered // Line terminator is not part of the comment. If it is a
// to be part of the single-line comment; it is recognized // non-ascii line terminator, it will result in a parser error.
// separately by the lexical grammar and becomes part of the while (!isUnicodeLinebreak())
// stream of input elements for the syntactic grammar
while (!isLineTerminator(m_char))
if (!advance()) break; if (!advance()) break;
return Token::Whitespace; return Token::Whitespace;
@ -268,7 +263,9 @@ Token::Value Scanner::scanSingleLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/' at /// advance(); //consume the last '/' at ///
skipWhitespaceExceptLF();
skipWhitespaceExceptUnicodeLinebreak();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
if (isLineTerminator(m_char)) if (isLineTerminator(m_char))
@ -287,6 +284,10 @@ Token::Value Scanner::scanSingleLineDocComment()
break; // next line is not a documentation comment, we are done 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); addCommentLiteralChar(m_char);
advance(); advance();
} }
@ -321,6 +322,9 @@ Token::Value Scanner::scanMultiLineDocComment()
bool endFound = false; bool endFound = false;
bool charsAdded = false; bool charsAdded = false;
while (isWhiteSpace(m_char) && !isLineTerminator(m_char))
advance();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
//handle newlines in multline comments //handle newlines in multline comments
@ -372,7 +376,7 @@ Token::Value Scanner::scanSlash()
if (m_char == '/') if (m_char == '/')
{ {
if (!advance()) /* double slash comment directly before EOS */ if (!advance()) /* double slash comment directly before EOS */
return Token::Whitespace; return Token::Whitespace;
else if (m_char == '/') else if (m_char == '/')
{ {
// doxygen style /// comment // doxygen style /// comment
@ -390,24 +394,27 @@ Token::Value Scanner::scanSlash()
{ {
// doxygen style /** natspec comment // doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */ if (!advance()) /* slash star comment before EOS */
return Token::Whitespace; return Token::Illegal;
else if (m_char == '*') else if (m_char == '*')
{ {
advance(); //consume the last '*' at /** advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
// special case of a closed normal multiline comment // "/**/"
if (!m_source.isPastEndOfInput() && m_source.get(0) == '/') if (m_char == '/')
advance(); //skip the closing slash
else // we actually have a multiline documentation comment
{ {
Token::Value comment; advance(); //skip the closing slash
m_nextSkippedComment.location.start = firstSlashPosition; return Token::Whitespace;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = sourcePos();
m_nextSkippedComment.token = comment;
} }
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 else
return skipMultiLineComment(); return skipMultiLineComment();
@ -435,11 +442,6 @@ void Scanner::scanToken()
m_nextToken.location.start = sourcePos(); m_nextToken.location.start = sourcePos();
switch (m_char) switch (m_char)
{ {
case '\n':
case ' ':
case '\t':
token = selectToken(Token::Whitespace);
break;
case '"': case '"':
case '\'': case '\'':
token = scanString(); token = scanString();
@ -675,18 +677,38 @@ bool Scanner::scanEscape()
if (!scanHexByte(c)) if (!scanHexByte(c))
return false; return false;
break; break;
default:
return false;
} }
addLiteralChar(c); addLiteralChar(c);
return true; 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() Token::Value Scanner::scanString()
{ {
char const quote = m_char; char const quote = m_char;
advance(); // consume quote advance(); // consume quote
LiteralScope literal(this, LITERAL_TYPE_STRING); LiteralScope literal(this, LITERAL_TYPE_STRING);
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) while (m_char != quote && !isSourcePastEndOfInput() && !isUnicodeLinebreak())
{ {
char c = m_char; char c = m_char;
advance(); advance();
@ -710,7 +732,7 @@ Token::Value Scanner::scanHexString()
char const quote = m_char; char const quote = m_char;
advance(); // consume quote advance(); // consume quote
LiteralScope literal(this, LITERAL_TYPE_STRING); LiteralScope literal(this, LITERAL_TYPE_STRING);
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) while (m_char != quote && !isSourcePastEndOfInput())
{ {
char c = m_char; char c = m_char;
if (!scanHexByte(c)) if (!scanHexByte(c))

View File

@ -197,8 +197,8 @@ private:
/// Skips all whitespace and @returns true if something was skipped. /// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace(); bool skipWhitespace();
/// Skips all whitespace except Line feeds and returns true if something was skipped /// Skips all whitespace that are neither '\r' nor '\n'.
bool skipWhitespaceExceptLF(); void skipWhitespaceExceptUnicodeLinebreak();
Token::Value skipSingleLineComment(); Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment(); Token::Value skipMultiLineComment();
@ -218,6 +218,9 @@ private:
/// is scanned. /// is scanned.
bool scanEscape(); bool scanEscape();
/// @returns true iff we are currently positioned at a unicode line break.
bool isUnicodeLinebreak();
/// Return the current source position. /// Return the current source position.
int sourcePos() const { return m_source.position(); } int sourcePos() const { return m_source.position(); }
bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); }

View File

@ -32,12 +32,9 @@ set VERSION=%2
IF "%VERSION%"=="2015" ( IF "%VERSION%"=="2015" (
set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
) ELSE ( ) ELSE (
set "DLLS=MSVC_DLLS_NOT_FOUND"
IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\" ( FOR /d %%d IN ("C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\*"
set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll" "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\*") DO set "DLLS=%%d\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"
)
) )
7z a solidity-windows.zip ^ 7z a solidity-windows.zip ^

134
test/buglistTests.js Executable file
View File

@ -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)
})
})
}

View File

@ -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;
}

View File

@ -1036,6 +1036,7 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts)
BOOST_AUTO_TEST_CASE(event_structs) BOOST_AUTO_TEST_CASE(event_structs)
{ {
char const* text = R"( char const* text = R"(
pragma experimental ABIEncoderV2;
contract C { contract C {
struct S { uint a; T[] sub; bytes b; } struct S { uint a; T[] sub; bytes b; }
struct T { uint[2] x; } struct T { uint[2] x; }

View File

@ -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_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) BOOST_AUTO_TEST_CASE(event_indexed_string)
{ {
char const* sourceCode = R"( 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")); 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) BOOST_AUTO_TEST_CASE(cleanup_in_compound_assign)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -23,6 +23,8 @@
#include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Scanner.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -393,6 +395,110 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string)
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); 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() BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,7 @@
contract C {
function f() public {
emit;
}
}
// ----
// ParserError: (45-46): Expected event name or path.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,4 @@
contract c {
event E(uint[2][]);
}
// ----

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.