diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | docs/contracts.rst | 101 | ||||
-rw-r--r-- | docs/security-considerations.rst | 23 | ||||
-rw-r--r-- | docs/using-the-compiler.rst | 21 | ||||
-rw-r--r-- | libdevcore/CommonData.h | 17 | ||||
-rw-r--r-- | libjulia/optimiser/README.md | 61 | ||||
-rw-r--r-- | libsolc/CMakeLists.txt | 9 | ||||
-rw-r--r-- | libsolc/libsolc.cpp (renamed from solc/jsonCompiler.cpp) | 2 | ||||
-rw-r--r-- | libsolc/libsolc.h (renamed from solc/jsonCompiler.h) | 0 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 14 | ||||
-rwxr-xr-x | scripts/bytecodecompare/storebytecode.sh | 2 | ||||
-rwxr-xr-x | scripts/test_emscripten.sh | 2 | ||||
-rwxr-xr-x | scripts/travis-emscripten/build_emscripten.sh | 4 | ||||
-rw-r--r-- | solc/CMakeLists.txt | 8 | ||||
-rw-r--r-- | test/CMakeLists.txt | 4 | ||||
-rw-r--r-- | test/fuzzer.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/JSONCompiler.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 6 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 10 |
20 files changed, 251 insertions, 41 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 105de851..8993f372 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,9 +43,10 @@ configure_project(TESTS) add_subdirectory(libdevcore) add_subdirectory(libevmasm) add_subdirectory(libsolidity) -add_subdirectory(solc) +add_subdirectory(libsolc) if (NOT EMSCRIPTEN) + add_subdirectory(solc) add_subdirectory(liblll) add_subdirectory(lllc) endif() diff --git a/Changelog.md b/Changelog.md index cf6498a1..4cd83eeb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Features: * Type Checker: More detailed errors for invalid array lengths (such as division by zero). Bugfixes: + * Parser: Disallow event declarations with no parameter list. ### 0.4.18 (2017-10-18) diff --git a/docs/contracts.rst b/docs/contracts.rst index 2b0956bb..ca4e79c0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -426,12 +426,20 @@ value types and strings. bytes32 constant myHash = keccak256("abc"); } +.. index:: ! functions + +.. _functions: + +********* +Functions +********* + +.. index:: ! view function, function;view .. _view-functions: -************** View Functions -************** +============== Functions can be declared ``view`` in which case they promise not to modify the state. @@ -465,11 +473,12 @@ The following statements are considered modifying the state: .. warning:: The compiler does not enforce yet that a ``view`` method is not modifying state. +.. index:: ! pure function, function;pure + .. _pure-functions: -************** Pure Functions -************** +============== Functions can be declared ``pure`` in which case they promise not to read from or modify the state. @@ -498,9 +507,8 @@ In addition to the list of state modifying statements explained above, the follo .. _fallback-function: -***************** Fallback Function -***************** +================= A contract can have exactly one unnamed function. This function cannot have arguments and cannot return anything. @@ -577,6 +585,85 @@ Please ensure you test your fallback function thoroughly to ensure the execution } } +.. index:: ! overload + +.. _overload-function: + +Function Overloading +==================== + +A Contract can have multiple functions of the same name but with different arguments. +This also applies to inherited functions. The following example shows overloading of the +``f`` function in the scope of contract ``A``. + +:: + + pragma solidity ^0.4.16; + + contract A { + function f(uint _in) public pure returns (uint out) { + out = 1; + } + + function f(uint _in, bytes32 _key) public pure returns (uint out) { + out = 2; + } + } + +Overloaded functions are also present in the external interface. It is an error if two +externally visible functions differ by their Solidity types but not by their external types. + +:: + + // This will not compile + pragma solidity ^0.4.16; + + contract A { + function f(B _in) public pure returns (B out) { + out = _in; + } + + function f(address _in) public pure returns (address out) { + out = _in; + } + } + + contract B { + } + + +Both ``f`` function overloads above end up accepting the address type for the ABI although +they are considered different inside Solidity. + +Overload resolution and Argument matching +----------------------------------------- + +Overloaded functions are selected by matching the function declarations in the current scope +to the arguments supplied in the function call. Functions are selected as overload candidates +if all arguments can be implicitly converted to the expected types. If there is not exactly one +candidate, resolution fails. + +.. note:: + Return parameters are not taken into account for overload resolution. + +:: + + pragma solidity ^0.4.16; + + contract A { + function f(uint8 _in) public pure returns (uint8 out) { + out = _in; + } + + function f(uint256 _in) public pure returns (uint256 out) { + out = _in; + } + } + +Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8`` +and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly +converted to ``uint8``. + .. index:: ! event .. _events: @@ -854,7 +941,7 @@ derived override, but this function will bypass } If ``Base1`` calls a function of ``super``, it does not simply -call this function on one of its base contracts. Rather, it +call this function on one of its base contracts. Rather, it calls this function on the next base contract in the final inheritance graph, so it will call ``Base2.kill()`` (note that the final inheritance sequence is -- starting with the most diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 337a3d3f..1e2138fa 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -69,10 +69,27 @@ complete contract): } The problem is not too serious here because of the limited gas as part -of ``send``, but it still exposes a weakness: Ether transfer always -includes code execution, so the recipient could be a contract that calls +of ``send``, but it still exposes a weakness: Ether transfer can always +include code execution, so the recipient could be a contract that calls back into ``withdraw``. This would let it get multiple refunds and -basically retrieve all the Ether in the contract. +basically retrieve all the Ether in the contract. In particular, the +following contract will allow an attacker to refund multiple times +as it uses ``call`` which forwards all remaining gas by default: + +:: + + pragma solidity ^0.4.0; + + // THIS CONTRACT CONTAINS A BUG - DO NOT USE + contract Fund { + /// Mapping of ether shares of the contract. + mapping(address => uint) shares; + /// Withdraw your share. + function withdraw() { + if (msg.sender.call.value(shares[msg.sender])()) + shares[msg.sender] = 0; + } + } To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as outlined further below: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index c12750c8..f1f13b82 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -177,7 +177,8 @@ Output Description start: 0, end: 100 ], - // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc + // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc. + // See below for complete list of types. type: "TypeError", // Mandatory: Component where the error originated, such as "general", "ewasm", etc. component: "general", @@ -273,3 +274,21 @@ Output Description } } } + + +Error types +~~~~~~~~~~~ + +1. ``JSONError``: JSON input doesn't conform to the required format, e.g. input is not a JSON object, the language is not supported, etc. +2. ``IOError``: IO and import processing errors, such as unresolvable URL or hash mismatch in supplied sources. +3. ``ParserError``: Source code doesn't conform to the language rules. +4. ``DocstringParsingError``: The NatSpec tags in the comment block cannot be parsed. +5. ``SyntaxError``: Syntactical error, such as ``continue`` is used outside of a ``for`` loop. +6. ``DeclarationError``: Invalid, unresolvable or clashing identifier names. e.g. ``Identifier not found`` +7. ``TypeError``: Error within the type system, such as invalid type conversions, invalid assignments, etc. +8. ``UnimplementedFeatureError``: Feature is not supported by the compiler, but is expected to be supported in future versions. +9. ``InternalCompilerError``: Internal bug triggered in the compiler - this should be reported as an issue. +10. ``Exception``: Unknown failure during compilation - this should be reported as an issue. +11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. +12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. +13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index e76a0949..b85abe95 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -183,6 +183,12 @@ template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U con _a.push_back(i); return _a; } +/// Concatenate the contents of a container onto a vector, move variant. +template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U&& _b) +{ + std::move(_b.begin(), _b.end(), std::back_inserter(_a)); + return _a; +} /// Concatenate the contents of a container onto a set template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b) { @@ -197,6 +203,17 @@ inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& ret += _b; return ret; } +/// Concatenate two vectors of elements, moving them. +template <class T> +inline std::vector<T> operator+(std::vector<T>&& _a, std::vector<T>&& _b) +{ + std::vector<T> ret(std::move(_a)); + if (&_a == &_b) + ret += ret; + else + ret += std::move(_b); + return ret; +} template <class T, class V> bool contains(T const& _t, V const& _v) diff --git a/libjulia/optimiser/README.md b/libjulia/optimiser/README.md new file mode 100644 index 00000000..771cb707 --- /dev/null +++ b/libjulia/optimiser/README.md @@ -0,0 +1,61 @@ +## IULIA Optimiser + +The iulia optimiser consists of several stages and components that all transform +the AST in a semantically equivalent way. The goal is to end up either with code +that is shorter or at least only marginally longer but will allow further +optimisation steps. + +The optimiser currently follows a purely greedy strategy and does not do any +backtracking. + +## Disambiguator + +The disambiguator takes an AST and returns a fresh copy where all identifiers have +names unique to the input AST. This is a prerequisite for all other optimiser stages. +One of the benefits is that identifier lookup does not need to take scopes into account +and we can basically ignore the result of the analysis phase. + +All subsequent stages have the property that all names stay unique. This means if +a new identifier needs to be introduced, a new unique name is generated. + +## Function Hoister + +The function hoister moves all function definitions to the topmost block. This is +a semantically equivalent transformation as long as it is performed after the +disambiguation stage. The reason is that moving a definition upwards cannot decrease +its visibility and it is impossible to reference variables defined in a different function. + +The benefit of this stage is that function definitions can be lookup up more easily. + +## Function Grouper + +The function grouper has to be applied after the disambiguator and the function hoister. +Its effect is that all topmost elements that are not function definitions are moved +into a single block which is the first satement of the root block. + +After this step, a program has the following normal form: + + { I F... } + +Where I is a block that does not contain any function definitions (not even recursively) +and F is a list of function definitions such that no function contains a function definition. + +## Functional Inliner + +The functional inliner depends on the disambiguator, the function hoister and function grouper. +It performs function inlining such that the result of the inlining is an expression. This can +only be done if the body of the function to be inlined has the form ``{ r := E }`` where ``r`` +is the single return value of the function, ``E`` is an expression and all arguments in the +function call are so-called movable expressions. A movable expression is either a literal, a +variable or a function call (or EVM opcode) which does not have side-effects and also does not +depend on any side-effects. + +As an example, neither ``mload`` nor ``mstore`` would be allowed. + +## Full Function Inliner + +## Variable Eliminator + +## Unused Declaration Pruner + +## Function Unifier diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt new file mode 100644 index 00000000..e67583dd --- /dev/null +++ b/libsolc/CMakeLists.txt @@ -0,0 +1,9 @@ +if (EMSCRIPTEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") + add_executable(soljson libsolc.cpp) + target_link_libraries(soljson PRIVATE solidity) +else() + add_library(libsolc libsolc.cpp) + set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc) + target_link_libraries(libsolc PRIVATE solidity) +endif() diff --git a/solc/jsonCompiler.cpp b/libsolc/libsolc.cpp index 23feaa2a..3a6e1521 100644 --- a/solc/jsonCompiler.cpp +++ b/libsolc/libsolc.cpp @@ -20,7 +20,7 @@ * JSON interface for the solidity compiler to be used from Javascript. */ -#include <solc/jsonCompiler.h> +#include <libsolc/libsolc.h> #include <libdevcore/Common.h> #include <libdevcore/JSON.h> #include <libsolidity/interface/StandardCompiler.h> diff --git a/solc/jsonCompiler.h b/libsolc/libsolc.h index c392ce93..c392ce93 100644 --- a/solc/jsonCompiler.h +++ b/libsolc/libsolc.h diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 821e81d2..05b877b5 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -644,15 +644,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition() expectToken(Token::Event); ASTPointer<ASTString> name(expectIdentifierToken()); - ASTPointer<ParameterList> parameters; - if (m_scanner->currentToken() == Token::LParen) - { - VarDeclParserOptions options; - options.allowIndexed = true; - parameters = parseParameterList(options); - } - else - parameters = createEmptyParameterList(); + + VarDeclParserOptions options; + options.allowIndexed = true; + ASTPointer<ParameterList> parameters = parseParameterList(options); + bool anonymous = false; if (m_scanner->currentToken() == Token::Anonymous) { diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index 8d4100bf..557e3275 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -40,7 +40,7 @@ TMPDIR=$(mktemp -d) if [[ "$SOLC_EMSCRIPTEN" = "On" ]] then - cp "$REPO_ROOT/build/solc/soljson.js" . + cp "$REPO_ROOT/build/libsolc/soljson.js" . npm install solc cat > solc <<EOF #!/usr/bin/env node diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index 4996e957..b659e5e5 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -29,7 +29,7 @@ set -e REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) -SOLJSON="$REPO_ROOT/build/solc/soljson.js" +SOLJSON="$REPO_ROOT/build/libsolc/soljson.js" DIR=$(mktemp -d) ( diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index bf460e8e..56826997 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -87,8 +87,8 @@ make -j 4 cd .. mkdir -p upload -cp build/solc/soljson.js upload/ -cp build/solc/soljson.js ./ +cp build/libsolc/soljson.js upload/ +cp build/libsolc/soljson.js ./ OUTPUT_SIZE=`ls -la soljson.js` diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 656b27c3..d9b12a06 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -10,14 +10,6 @@ target_link_libraries(solc PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES}) include(GNUInstallDirs) install(TARGETS solc DESTINATION "${CMAKE_INSTALL_BINDIR}") -if (EMSCRIPTEN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") - add_executable(soljson jsonCompiler.cpp) -else() - add_library(soljson jsonCompiler.cpp) -endif() -target_link_libraries(soljson PRIVATE solidity) - if(SOLC_LINK_STATIC AND UNIX AND NOT APPLE) # Produce solc as statically linked binary (includes C/C++ standard libraries) # This is not supported on macOS, see diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6a8a4399..f36ad4c5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,7 @@ list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") file(GLOB_RECURSE headers "*.h") add_executable(soltest ${sources} ${headers}) -target_link_libraries(soltest PRIVATE soljson solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 53ba7201..578e63a4 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -20,7 +20,7 @@ #include <libevmasm/Assembly.h> #include <libevmasm/ConstantOptimiser.h> -#include <solc/jsonCompiler.h> +#include <libsolc/libsolc.h> #include <json/json.h> diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 7dc4808b..0c904c77 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -23,7 +23,7 @@ #include <boost/test/unit_test.hpp> #include <libdevcore/JSON.h> #include <libsolidity/interface/Version.h> -#include <solc/jsonCompiler.h> +#include <libsolc/libsolc.h> #include "../Metadata.h" #include "../TestHelper.h" diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 05dc9ba3..f5f7e64a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2971,7 +2971,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); function deposit() { Deposit(); } @@ -3013,7 +3013,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); event Deposit(address _addr); event Deposit(address _addr, uint _amount); function deposit() returns (uint) { @@ -3059,7 +3059,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) { char const* sourceCode = R"( contract A { - event Deposit; + event Deposit(); } contract B { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 72473c3e..861e6408 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -960,6 +960,16 @@ BOOST_AUTO_TEST_CASE(event_arguments_indexed) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(event_with_no_argument_list_fails) +{ + char const* text = R"( + contract c { + event e; + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_CASE(visibility_specifiers) { char const* text = R"( |